Practice
Preparation: Access SQL Server Management Studio
To reinforce our understanding of the basics we have
covered, we will use the IsAncestor() function within a couple of
queries that illustrate its operation, focusing, within this article, upon
scenarios where we use the function to support conditional logic within
a calculation. (We examine its use in combination with the MDX Filter()
function in another article of this series). We will undertake our practice
exercises within scenarios that place IsAncestor() within the context of
meeting basic requirements similar to those we might encounter in our
respective daily environments. The intent is to demonstrate the use of the function
in a straightforward, memorable manner.
We will turn to the SQL Server Management Studio as a
platform from which to construct and execute the MDX we examine, and to view
the results datasets we obtain. If you do not know how to access the SQL
Server Management Studio in preparation for using it to query an Analysis
Services cube (we will be using the sample Adventure Works cube in
the Adventure Works DW Analysis Services database), please
perform the steps of the following procedure, located in the References
section of my articles index:
This procedure will take us through opening a new Query
pane, upon which we will create our first query within the section that
follows.
Procedure: Satisfy Business Requirements with MDX
Lets assume, for purposes of our practice example, that we
have received a request for assistance from representatives of our client, the Adventure
Works organization. Analysts within the Controllers Group, with whom we
have worked in the past to deliver solutions to meet various reporting and
analysis needs, inform us that they have received a request to generate some
simple values for a specific analysis task that has been discussed at a recent
meeting with the Controllers.
The analysts tell us that the values under immediate
consideration involve Internet Order Counts, but, as is typically the
case in our collaborative sessions, they want to develop an approach that will
work equally well with other measures that have similar analysis potential. (They
often derive parameterized queries in Reporting Services from the basic
MDX syntax we assemble together, and can thus create self-serve reports that
allow information consumers to dictate what measure they wish to analyze, and
myriad other options, at run time). The desired end is to simply return the Internet
Order Count recorded for each day, month, quarter, and semester for a given
operating calendar year.
While this basic need might be easily met a number of ways
with an MDX query, the analysts throw a further twist into the requirement: In
addition to being likely to parameterize the calendar year at runtime,
they also want to be able to support parameterization of the level within
the Date dimension (Calendar hierarchy) when executing the report
(that is, to be able to change it from calendar year to a lower level,
such as a quarter of a month, for example and thus to narrow
the member selection that appears within a given iteration of the report
results, producing something akin to a selective drilldown effect.) Once again,
the richness of MDX affords us a number of avenues to this objective. While
parameterization is itself not a consideration in our current level of query
design, we want to make it easy to accomplish within Reporting Services
(the same concept would, of course, apply with other OLAP reporting tools that
afford developer access to the MDX syntax that underlies them).
After we initially explain the use of the IsAncestor() function
as one candidate for meeting the requirement, our client colleagues state that
they are interested in understanding how they might apply conditional logic
via this function, within the context of a practical scenario such as the immediate
requirement. A method of testing whether or not a specified member is an
ancestor to another specified member, or group of members, is something that
they hope to be able to extrapolate to uses with other dimensions, as well.
(Time / date dimensions are always good starters for introducing new
functions: the relationships between the various levels are familiar to
everyone, whereas the structures of other dimensions might not lend themselves
to population accuracy and completeness reasonability testing undertaken by
those not entirely knowledgeable of the corporate structure, geography, and so
forth.)
We offer to illustrate the use of IsAncestor() to meet
the immediate need, proposing to present a couple of examples, to solidify the analysts
new understanding, as well as to assist in rounding their overall MDX
vocabularies. We then set about the assembly of our examples to illustrate
uses of IsAncestor().
Procedure: Use the IsAncestor() Function to Perform
Conditional Logic within a Calculation
Per the request of our client colleagues, we will first construct
a simple query to provide an illustration of the use of the IsAncestor() function
within a common context, the definition of a calculation based upon conditional
logic. Our initial example will serve as an introduction to a means of
distinguishing the presence of a ancestor / descendant relationship
between members of the Date dimension. (We will work with Calendar
Year 2003, as the primary member, within our initial example), as requested
by the analysts, as a basis for meeting the business requirement to present the
simple Internet Order Counts at multiple levels.
1.
Type (or cut
and paste) the following query into the Query pane:
-- MDX052-001 ISANCESTOR()Function: Conditional Logic in
-- the Definition of a Calculation
WITH
MEMBER
[Measures].[InternetSelectCount]
AS
'IIF(
ISANCESTOR([Date].[Calendar].[Calendar Year].[CY 2003],
[Date].[Calendar].CURRENTMEMBER),
[Measures].[Internet Order Count],
NULL)'
SELECT
{[Measures].[InternetSelectCount]}ON AXIS(0),
NON EMPTY{[Date].[Calendar].MEMBERS} ON AXIS(1)
FROM
[Adventure Works]
The Query pane appears,
with our input, as depicted in Illustration 2.
Illustration 2: Our Initial Query in the Query Pane ...
The above query returns the Internet Order Count
for each member of the Date dimension (Calendar hierarchy) in the
cube whose ancestor is Calendar Year 2003 regardless of the
level the member inhabits; we use Non Empty to physically screen the
results to show our focus Calendar Year, 2003, and the descendants
of this specified, year-level primary member expression. Had we not
inserted the Non Empty keyword, we would get all members of the Date
dimension, Calendar hierarchy, with those non-descendant members simply
indicating (null) as a measure value.
Recall that we have said that we might accomplish our ends
through alternative methods. The approach we are taking here allows us to
parameterize the primary member expression to accomplish the extended
ends of our client colleagues. In doing so, we could set up a hierarchical
picklist within Reporting Services, whereby information consumers
might select a given date, month, quarter, and so forth,
to drive the level below which values are returned. The obvious
advantage is that consumers can dictate both the level of the date
hierarchy and the specific focus member of the hierarchy itself, within
the level (in our example, the year for which they wish to
display the value for the corresponding descendants). In some
circumstances such double leverage provided by a single parameter might be
seen as a highly desirable efficiency certainly within the realm of simulated
dynamic drilldown effects and so forth.
In the Tentative Reseller Share calculation, we put
the IsAncestor() function to work in applying conditional logic
to generate the Internet Order Count value: if Calendar Year 2003 (the
primary member expression of our function) is the ancestor of the
secondary member expression (the Current Member of the Date
dimension / Calendar hierarchy), then the corresponding Internet Order
Count value is presented. Alternatively, we have directed (via the conditional
logic of the IIF() function), that if the primary member expression
(Calendar Year 2003) is not the ancestor of the secondary
member expression (that is, the member is not a dimensional
descendant of Calendar Year 2003), than the Internet Order Count
value is returned as null. (Moreover, as we have noted, while we might have
displayed all values, including nulls, we eliminated nulls in our present
exercise by preceding the rows specification with the NON EMPTY keyword.)
We have specified that the Calendar Date members are
to populate the rows axis. This provides, to some extent, a quick means of reasonability
testing out the logic within the calculation that we have defined, as we shall
see.
2.
Execute the
query by clicking the Execute button in the toolbar, as shown in Illustration
3.
Illustration 3: Click Execute to Run the Query...
The Results pane is populated by Analysis
Services, and the dataset, partially depicted in Illustration 4, appears.
Illustration 4: Results Dataset (Partial View) IsAncestor()
Function within a Calculation
In the partial view of the returned dataset, we see that the
calculation accomplishes the intended purpose - generating the Internet Order
Count for the individual Date (Calendar hierarchy)
descendants of Calendar Year 2003, which share the same ancestor
(Calendar Year 2003). Again, the conditional test
of ancestry is applied via a calculated member within which we have
leveraged the IsAncestor() function.
3.
Select File
-> Save MDXQuery1.mdx As ..., name the file MDX052-001,
and place it in a meaningful location.
Our
client colleagues express satisfaction with the contextual backdrop we have
established for introducing the IsAncestor() function. We will use a
similar query within another such example next, to confirm understanding of the
concepts. This query will
provide an
illustration of the use of the IsAncestor() function within the context
we have already seen, the definition of a calculated member based upon a
comparison. As before, we will base our example upon a local, albeit
slightly more sophisticated, scenario described by the client representatives.
The developers / authors within the group cite the following
example as useful. They would like to create a query that returns a tentative
calculation for Net Resale Revenues, based upon a recent proposal for
determining Reseller share of a Product sale for operating Calendar
Year 2003. The team provides the following, example-only details, as
final details are currently being negotiated: for purposes of the example, we
would like to calculate Reseller share for Bike (our primary
product) sales as 7.5 percent of Reseller Sales Amount; Reseller
share for all other Product sales would be calculated at 5.75 percent.
To refresh our memory of the dimensional structure under
consideration, we examine the expanded Product Categories hierarchy of
the Product dimension, within the Adventure Works cube. A
partial view of this structure appears as shown in Illustration 5.
Illustration 5: Partial View of the Product Categories
Hierarchy Product Dimension