MDX Essentials: Logical Functions: The IsEmpty() Function

About the Series ...

This is the twenty-fifth article of the series, MDX Essentials. The series is designed to provide hands-on application of the fundamentals of the Multidimensional Expressions (MDX) language, with each tutorial progressively adding features designed to meet specific real-world needs.

For more information about the series in general, as well as the software and systems requirements for getting the most out of the lessons included, please see the first article, MDX at First Glance: Introduction to MDX Essentials.

Note: Service Pack 3 updates are assumed for MSSQL Server 2000, MSSQL Server 2000 Analysis Services, and the related Books Online and Samples.

Overview

In this lesson, we will introduce a function that assists us in the handling of empty cells. In multidimensional data sets, we are often confronted with empty cells - data is often sparse in these sets by their very physical nature. Because, as a simple example, every product might not be sold at every store in every time period (to cite an instance from the FoodMart2000 sample environment), we will see empty cells in abundance in a data set that contains intersects of these dimensions. (Particularly in working with crossjoins of any magnitude, we will encounter many empty cells, as a general rule.) Empty cells mean nulls, and nulls can mean incorrect results in using MDX to support analysis in reporting.

A logical function, ISEMPTY() returns true the expression to which it is applied evaluates to an empty cell. As we will see in the practice example we undertake in this article, ISEMPTY() works ideally with IIF(), a conditional function, to check cells for empty or not-empty status. We will consider elementary uses of the ISEMPTY() function in this article, and then call it into service in subsequent articles where we require it as a tool to perform just this sort of check. In keeping with the objectives of the MDX Essentials series, we will seek to build a foundation in the rudiments of the function, from which we can expand to more sophisticated uses in other articles. As a part of building our basis in the ISEMPTY() function, we will also take a preliminary look at the IIF() function, which we will take up in subsequent articles that we devote to it especially.

ISEMPTY() will likely become a valued member in the toolset of any practitioner that relies heavily upon MDX to supply solutions to the organizations they support. We will introduce the function, commenting upon its operation and touching upon uses at a general level, and then we will:

  • Examine the syntax surrounding the function;
  • Undertake an illustrative example of the use of the function, in a multi-step practice exercise;
  • Briefly discuss the results datasets we obtain in the practice examples.

The IsEmpty() Function

Introduction

The ISEMPTY() function, according to the Analysis Services Books Online, "returns TRUE if the evaluated expression is the empty cell value, FALSE otherwise." We will examine the function's manner of accomplishing these evaluations, and discuss factors that we should consider based upon the function's behavior, in the sections that follow.

We will examine the syntax for the ISEMPTY() function in general, building to its use in meeting an issue that arises in the real world, where "empties get in the way" of our objectives of clear analysis and reporting. In this way, we will be able to clearly see that the ISEMPTY () function does, in fact, generate the results we might expect. Our objective is to gain a richer understanding of the capabilities found within the ISEMPTY () function, together with a feel for potential uses of the function. As a byproduct of our examination of ISEMPTY(), we will introduce a simple use of the IIF() function, which will serve to overview the basics of IIF(), as a preliminary for the two articles that follow this one.

Discussion

ISEMPTY () affords us a means of testing for empty cells. It becomes valuable in light of the fact that sparseness, as we have noted earlier, is a fact of life in multidimensional data sets, and is often an obstacle, as we shall discuss, in our analysis and reporting efforts. And with empty cells come various data presentation problems that we can remedy easily, in many cases, using ISEMPTY() to detect the empties and manage them in various ways.

ISEMPTY() is often accompanied by the conditional IIF() function, as we shall see in our practice section. It often appears, also, with the NOT keyword, in scenarios where we are attempting to manipulate cells, in some way, which are not empty.

Let's look at a syntax illustration to further clarify the operation of IS EMPTY().

Syntax

Syntactically, the expression upon which the evaluation of "empty or not empty" is to be applied by the ISEMPTY() function is placed within the parentheses to the right of ISEMPTY. The syntax is shown in the following string:

ISEMPTY(<<Value Expression>>)

A logical function, ISEMPTY() returns "True" if the Value Expression is null, and "False" if it is not null. That is, ISEMPTY() evaluates the expression we provide in the parentheses to its right, and returns either a negative one (-1) or a zero (0), depending upon whether the expression is revealed to be an empty cell or not, respectively.

If we seek to use a tuple, versus a basic member reference, as the Value Expression, we must surround the expression by parentheses, as shown in the following string:

ISEMPTY ( ([Measures].[Units Shipped], [Time].NextMember) )

The following simple example illustrates conceptually the operation of the ISEMPTY() function. (We will be doing a practice exercise in subsequent sections, but if you want to "test drive" a sample, you can certainly cut and paste, or type, the below into the MDX Sample Application).

We will query the Warehouse sample cube to determine, for operating year 1998, which State / Province locations recorded no shipping activity for canned vegetables. We will use a calculated member ("Activity") that indicates whether there was shipping activity by indicating "None," if there were no units shipped, and "Activity:" if, indeed, there were. We will display the number of Units Shipped for the year in a row below the string indicator, to demonstrate the fact that the calculated member generates the expected results.


WITH MEMBER

   [Measures].[Activity]

AS

   'IIF( ISEMPTY( [Measures].[Units Shipped]), "None", "Activity:")'

SELECT

   {[Warehouse].[State Province].Members} ON COLUMNS,

   CROSSJOIN( 

      {[Product].[All Products].[Food].[Canned Foods].[Vegetables]},

         {[Measures].[Activity], [Measures].[Units Shipped]}) ON ROWS

FROM

   [Warehouse]

WHERE 

   ([Time].[Year].[1998])

This query returns a set similar to that depicted in Illustration 1.


Illustration 1: Returned Dataset for Example Query

We note that the returned data indicates that two Mexican provinces, Jalisco and Yucatan, apparently experienced no shipping volume for canned vegetables within the operating year of 1998.

We will activate the concepts involved in the foregoing discussions by practicing the use of the ISEMPTY() function in the section that follows.

Practice

Preparation

To reinforce our understanding of the basics we have covered so far, we will use the ISEMPTY() function in a manner that illustrates its operation within a multi-step example. We will first create a query that attempts to meet a business need as stated by a hypothetical group of information consumers. The resulting dataset will illustrate a scenario in which we might want to manage an empty underlying cell. We will then expose a means, using the ISEMPTY() function, to provide the results we desire in the final presentation of the data.

We will call upon the MDX Sample Application again, as our tool to construct and execute the MDX we examine, and to view the results datasets we obtain.

1.  Start the MDX Sample Application.

2.  Clear the top area (the Query pane) of any queries or remnants that might appear.

3.  Ensure that FoodMart 2000 is selected as the database name in the DB box of the toolbar.

4.  Select the WAREHOUSE cube in the Cube drop-down list box.

Let's assume for our practice example that we have received a request from a group of information consumers in the Logistics unit of the FoodMart2000 organization. The request is for support in the presentation of some data, housed within the Warehouse sample cube, regarding Product shipping volumes for 1998. The consumers wish to see the total Units Shipped for FoodMart200 products by Product Family. They also wish to see a second column in the display that presents a percent of parent value - that is, they want to see the percentages of total product units shipped that can be attributed to each Product Family.

We will begin by composing a simple query to meet the business requirement, which will illustrate an effect of an empty cell upon the display of the data. We will then eliminate the problem in subsequent steps, after discussing why the problem exists in the first place. My objective is thus to illustrate use of the function to meet the hypothetical business requirement we have outlined, as a means of reinforcing the concepts in our minds. Once we have activated the concepts, they can be triggered upon meeting a similar situation in our respective business environments.

To initiate action, let's attempt to meet the requirement in a somewhat intuitive way.

5.  Type the following query into the Query pane:



-- MDX25-1:  Tutorial Query Step 1

WITH MEMBER

   [Measures].[% Total Products]

AS

   '[Measures].[Units Shipped]/([Measures].[Units Shipped],[Product].Parent)'

SELECT

   {[Measures].[Units Shipped], [Measures].[% Total Products]} ON COLUMNS, 

   {DESCENDANTS([Product].[All Products], [Product].[Product Family], 

      SELF_AND_BEFORE)} ON ROWS

FROM

   [Warehouse]

WHERE 

   ([Time].[Year].[1998])

The purpose of the WITH MEMBER section of the query is to create a calculated member to provide the "percent of parent" (that is, percent of total Product Units Shipped) contributed by each of the Product Family members, including the total of all Product Families. Reason tells us that the Total Products line in the data set returned should represent 100.00 %, and discussions with members of the intended audience confirms that this is what they wish to see. Herein lies a complication that rests at the heart of a need for management of empty cells, as we shall see shortly.

The SELECT statement simply requests the calculated member, alongside the Units Shipped measure for each respective Product Family's activity for 1998. At this stage, we have added no formatting for the calculated member.

NOTE: For a detailed introduction to calculated members, see the following Database Journal articles: Calculated Members: Introduction and Calculated Members: Further Considerations and Perspectives. In addition, for an introduction to the .Parent function, see MDX Member Functions: The "Family" Functions.

6.  Execute the query by clicking the Run Query button in the toolbar.

The Results pane is populated by Analysis Services, and the dataset shown in Illustration 2 appears.


Illustration 2: Initial Results Dataset

Upon initial blush, we can see that something funky seems to be happening with the calculated member for % Total Products, but to ascertain how the calculated member is performing overall, we first need to format it to indicate percentages correctly. What we see initially is a rounding effect that is making the effectiveness of the calculated member unclear.

7.  Select File -> Save As..., name the file MDX25-1, and place it in a meaningful location. Leave the file open for the next step.

8.  Modify the comment line to read:

-- MDX25-2:  Tutorial Query Step 2

9.  Save the file as MDX25-2, to protect MDX25-1.

Now we will take another preparatory step and add formatting into the WITH MEMBER clause, to make your percentages appear in a manner that is useful to the information consumers.

10.  Insert the following string:

     , FORMAT_STRING = '#.00%'

between the existing fifth line of the query (counting the comment line, and shown below):

'[Measures].[Units Shipped]/([Measures].[Units Shipped],[Product].Parent)'

and the existing sixth line of the query, the SELECT keyword.

The Query pane appears as shown in Illustration 3, with changes circled in red.


Illustration 3: Modified Query in Query Pane (Compressed View)

11.  Execute the query by clicking the Run Query button in the toolbar, once again.

The Results pane is populated once more, and the dataset depicted in Illustration 4 appears.


Illustration 4: Results Dataset, with Percent Formatting Enhancement

We see the percentages displayed, filling the blanks that existed before due to undesirable rounding.

12.  Select File -> Save to save the query as MDX25-2. Leave the file open for the next step.

13.  Modify the comment line to read:

-- MDX25-3:  Tutorial Query - Final

14.  Save the file as MDX25-3, to protect MDX25-2.

We have now eliminated the display issue of percentage format, and have only to take the necessary steps to present "100%" as the percent of total on the Total Products line. Here we will use the ISEMPTY() function to remedy the problem (an error indicated in the cell value calculation) that is causing the nonsensical result of "1.#INF" to appear in the field.

The problem that underlies the appearance of the 1.#INF result is division by zero. The expression that makes up our calculated member (inside the WITH MEMBER clause we constructed earlier) is as follows:

Measures].[Units Shipped]/([Measures].

  [Units Shipped],[Product].Parent)

The MDX above contains a .Parent function. This reference to the .Parent function works fine in the cases of each of the Product Families, because each of the families has a parent, the All Products level of the Product dimension. Total Products, however, which represents the All Product level itself, has no parent - All Products occupies the highest level in the dimensional hierarchy.

Because it has no parent, the value returned when we subject All Products to the .Parent function is null. Were this simply a reference to [All Products].Parent, an empty cell would be returned. However, when we use the reference within a calculation as a divisor, as we have done above, the result is effectively an attempt to divide by zero: MDX treats "null" as "zero," which causes the underlying error.

To eliminate the error, and to display the accurate value of "100.00%", we need to combine the IIF() function, which is employed to evaluate a conditional expression, with the MDX ISEMPTY() function, which, as we have noted, indicates whether a given value expression (in our case, a divisor of the expression forming our calculated member) is empty.

To meet the expressed needs of the information consumers with which we are working, we will take the following steps:

1.  Modify the WITH MEMBER, "AS" clause's contents, currently appearing as:

   '[Measures].[Units Shipped]/([Measures].[Units Shipped],[Product].Parent)'

to the following:



'IIF(ISEMPTY( 

([Measures].[Units Shipped],[Product].Parent) ), 1,

      [Measures].[Units Shipped]/([Measures].

   [Units Shipped],[Product].Parent))'

Here we are simply surrounding the original string with An ISEMPTY() function, placing it into the parentheses to the right of the function (recall that, since we are referencing a tuple versus a simple member reference, we need two sets of parentheses: one for the function and one to enclose the tuple).

We enclose the ISEMPTY() function in the IIF() function. Within the resulting combination of the two functions and our references, we are saying, "if the tuple ([Measures].[Units Shipped],[Product].Parent) is empty, provide a 1 (which equates to 100 percent, when formatted); if the tuple is not empty, substitute the results of the calculation [Measures].[Units Shipped] / ([Measures].[Units Shipped],[Product].Parent."

IIF() is restricted to two numeric return values or two string return values (we cannot mix the two in a single use of the function), as we shall see in our next articles. We are working with numeric values here, where IIF() works well also because it cannot return null as a value (null is neither a string nor a numeric).

The Query pane appears as shown in Illustration 5, with additions circled in red.


Illustration 5: Modified Query in Query Pane (Compressed View)

2.  Execute the query by clicking the Run Query button in the toobar.

The Results pane is populated, and the dataset shown in Illustration 6 appears.


Illustration 6: Results Dataset, With the IIF() / ISEMPTY() Combination in Place

We can now see that our problem with the spurious data in the % Total Product column is a thing of the past. "100.00 %" is correctly reflected, thanks to our incorporation of conditional logic into the definition of the calculated member.

3.  Select File -> Save to save the query as MDX25-3.

4.  Exit the Sample Application when ready.

Thus we meet the business requirement through the use of the ISEMPTY() function, demonstrating a scenario where it helps to overcome presentation issues when empty cells are involved. In combination with the IIF() function, ISEMPTY() is a valuable tool we can use to test cells for emptiness, and to handle them when ISEMPTY() returns a positive for the empty state.

Summary ...

In this article, we explored the ISEMPTY() function, whose purpose is to return a "true" if the expression to which it is applied evaluates to an empty cell. We noted that ISEMPTY() is an excellent means of identifying empty cells. We discussed the fact that empty cells are a common fixture of multidimensional sets, and then began a practice example that included the presence of such an empty, together with an undesirable consequence in the presentation of data to information consumers.

In addition to introducing the ISEMPTY() function in an exercise where we might see its value in managing empty cells, we introduced the IIF() function in the same example, combining the two functions to provide for the conditional application of our solution to manage an empty cell, as well as the divide-by-zero error that it brought into being. We examined the syntax surrounding the ISEMPTY() function, before beginning our multi-step practice exercise to illustrate the combined use of IIF() and ISEMPTY(). Moreover, we discussed the application of formatting our presentation to meet the needs of the intended audience. Finally, we discussed the results datasets we obtained in each of the steps of our efforts.

» See All Articles by Columnist William E. Pearson, III

Discuss this article in the MSSQL Server 2000 Analysis Services and MDX Topics Forum.

MDX Essentials Series
Article 1: MDX at First Glance: Introduction to MDX Essentials
Article 2: Structure of the MDX Data Model
Article 3: MDX Operators: The Basics
Article 4: MDX Members: Introducing Members and Member
Article 5: MDX Member Functions: The "Family" Functions
Article 6: MDX Member Functions: More "Family" Functions
Article 7: MDX Member Functions: The Cousin () Function
Article 8: MDX Member Functions: "Relative" Member Functions
Article 9: MDX Time Series Functions, Part I: PeriodsToDate() and Kindred Functions
Article 10: MDX Time Series Functions, Part II: The OpeningPeriod () and ClosingPeriod() Functions
Article 11: MDX Time Series Functions, Part III: The LastPeriods() and ParallelPeriod() Functions
Article 12: Basic Set Functions: The Order() Function
Article 13: Basic Set Functions: The Union() Function
Article 14: Basic Set Functions: The Intersect() Function
Article 15: Basic Set Functions: The EXCEPT() Function
Article 16: Basic Set Functions: The Filter() Function
Article 17: Basic Numeric Functions: The Count() Function
Article 18: Basic Set Functions: The CrossJoin() Function
Article 19: Basic Set Functions: Subset Functions: The Head() Function
Article 20: Basic Set Functions: Subset Functions: The Tail() Function
Article 21: Basic Set Functions: Subset Functions: The Subset() Function
Article 22: Basic Member Functions: The .Item() Function
Article 23: Numeric Functions: Introduction to the AVG() Function
Article 24: Basic Set Functions: The EXTRACT() Function
Article 25: Logical Functions: The IsEmpty() Function
Article 26: String / Numeric Functions: Introducing the IIF() Function
Article 27: String / Numeric Functions: More on the IIF() Function
Article 28: The CROSSJOIN() Function: Breaking Bottlenecks
Article 29: Set and String Functions: The GENERATE() Function
Article 30: Enhancing CROSSJOIN() with Calculated Members
Article 31: Basic Set Functions: The TopCount() Function, Part I
Article 32: Basic Set Functions: The TopCount() Function, Part II
Article 33: String / Numeric Functions: The CoalesceEmpty() Function
Article 34: String Functions: The .Name Function
Article 35: String Functions: The .UniqueName Function
Article 36: Drilling Through with MDX: The DRILLTHROUGH Statement
Article 37: Set Functions: The DRILLDOWNMEMBER() Function
Article 38: Set Functions: The DRILLDOWNMEMBERTOP() and DRILLDOWNMEMBERBOTTOM() Functions
Article 39: MDX Set Functions: The DRILLUPMEMBER() Function
Article 40: MDX Set Functions: DrillDownLevel()
Article 41: Set Functions: The DrillDownLevelTop() and DrillDownLevelBottom() Functions
Article 42: Set Functions: The DrillUpLevel() Function
Article 43: MDX Set Functions: The ToggleDrillState() Function
Article 44: MDX Set Functions: The Distinct() Function
Article 45: MDX Operators: The IS Operator
Article 46: Other MDX Entities: Perspectives
Article 47: MDX Numeric Functions: The .Ordinal Function
Article 48: MDX Operators: The IsLeaf() Operator: Conditional Logic within Calculations
Close    To Top
  • Prev Article-Database:
  • Next Article-Database:
  • Now: Tutorial for Web and Software Design > Database > MySQL > Database Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction