Practice
Preparation: Access SQL Server Management Studio
To reinforce our understanding of the basics we have
covered, we will put the HAVING clause to work within a couple of
queries that illustrate its operation, focusing, within this article, upon
scenarios where we use HAVING to act as a filter within an axis
context. We will undertake our practice exercises within scenarios that place HAVING
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 from representatives of our client, the Adventure
Works organization. Having implemented the integrated Microsoft business
intelligence solution, including MSSQL Server, Analysis Services,
Integration Services, Reporting Services, and other components
for the client earlier in the operating year, we have been called upon to
assist in the continuing rollout of the various components throughout the
organization. In the present case, a group of report authors in the Operations
department has requested assistance in generating some simple values for a
specific analysis task that has been discussed at a recent meeting with the organizations
controllers.
The Operations authors are aware that the particular need
that they are currently expressing will manifest itself in recurring situations
as they work to meet the daily requirements of the Adventure Works
information consumers, as well as to support business requirements gathering
efforts as Analysis Services is implemented throughout other operating
entities of the organization. The authors have posed a couple of scenarios
wherein they wish to perform filtering within the scope of an axis, and,
while we have already imparted knowledge of the use of the FILTER()
function in meeting previous business needs, we feel that this is an
appropriate opportunity to introduce an alternative option.
In a brief discussion with our client colleagues, we provide
introductory details (as we have in the sections above) surrounding the HAVING
clause. We start by using a whiteboard to acquaint the group with the syntax
involved in its use, in an example similar to the one we provided in the Syntax
section above. Our next steps will be to provide practice with the use of HAVING
within a couple of query scenarios wherein we parallel our efforts in
obtaining the desired effect, using first the FILTER() function, and
then the HAVING clause, in a manner similar to our syntax example above.
Through the construction and execution of queries that illustrate the
characteristics of HAVING, we hope to reinforce the understanding of the
authors, so as to enable them to judiciously use the clause within MDX
queries for various analysis and reporting needs.
The authors group tells us that the values under immediate
consideration involve Reseller Order Quantity and Reseller Sales
Amount, 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 assist them in assembling, 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 current desired end is to simply
return the Reseller Order Quantity and Reseller Sales Amount
values, as captured within the Adventure Works cube, for the Product
Category Components, for the Calendar Year 2003.
Our client colleagues further tell us that they wish to see
only Value Added Resellers on the row axis of the returned dataset, with
empties suppressed. Moreover, they wish to see only Value Added Resellers
whose Reseller Order Quantity value exceeds ten units. (It is
this final part of the specification, the need to filter the members of the
row axis, that serves as the basis for our reasoning that this represents a
great opportunity for demonstrating the use of HAVING as a substitute
for the FILTER() function.)
We offer to illustrate the use of HAVING to meet the
immediate need, proposing to present a couple of examples, each subsequent to a
parallel case where we use the FILTER() function to achieve the same end,
to solidify the authors 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 HAVING.
Procedure: Use the HAVING Clause as a Substitute Means
of Filtering a Rows Axis Specification
We will first construct a simple core query to
support the business requirements in all except the specific focus of our
lesson, the use of the HAVING clause to enforce filtering within the row
axis. Once the core query is in place, we will apply filtering via the FILTER()
function, so as to have a baseline dataset that meets all the requirements. We
will then substitute the HAVING clause for the FILTER() function,
re-execute the query, and verify that the retrieved data reflects the same
desired, filtered result.
1. Type (or cut and paste) the following query into the Query pane:
-- MDX053-001-1 Core Query
SELECT
{[Measures].[Reseller Order Quantity], [Measures].[Reseller Sales Amount] }
ON AXIS(0),
NON EMPTY
{[Reseller].[Reseller Type].[Business Type].[Value Added Reseller].CHILDREN}
ON AXIS(1)
FROM
[Adventure Works]
WHERE
([Product].[Product Categories].[Category].[Components],
[Date].[Calendar].[Calendar Year].[CY 2003] )
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 Reseller Order Quantity
and Reseller Sales Amount for the Components Product Category sold
by each Value Added Reseller in Calendar Year 2003. As we noted
earlier, this core query gets us most of the distance in meeting the expressed
business requirement. (Once we have a core superset as a backdrop, we will enact
a filter, through the two approaches we have discussed, to fine
tune the query to produce the precise dataset our client colleagues have
requested, restricting the data returned to Value Added Resellers within
the requested time frame that had Reseller Order Quantities of greater
than ten.)
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) Core
Query Before Filtering
In the partial view of the returned dataset, we see that the
core query we have constructed accomplishes the intended purpose - generating
the Reseller Order Quantity and Reseller Sales Amount values, for
the Product Category Components, for the Calendar Year
2003. Moreover, only Value Added Resellers appear on the row axis
of the returned dataset, with empties suppressed.
3.
Select File
-> Save MDXQuery1.mdx As ..., name the file MDX053-001-1,
and place it in a meaningful location.
Our
client colleagues express satisfaction with the contextual backdrop we have established
for introducing contrasts in filtering to precisely tune the query to their
specifications. First, we explain, we will filter the core dataset with the FILTER()
function, to arrive at the exact dataset required. We will then replicate that
filter effect with the HAVING clause, to illustrate its use in a manner
that makes the results instantly verifiable as to accuracy and completeness.
4.
Replace the
comment line in query MDX053-001-1 with the following:
-- MDX053-001-2 Using FILTER() to Narrow Returned Data Set
5.
Select File
-> Save MDX053-001-1.mdx As ..., name the file MDX053-001-2,
and place it in the same location as its predecessor, to protect the former
query.
6.
Place the
cursor to the right of the NON EMPTY keyword on the fifth row of the
query.
7.
Press the
Enter key twice to create a new line between the line of the query on which
we have placed the cursor and the line that currently follows it, namely:
{[Reseller].[Reseller Type].[Business Type].[Value Added Reseller].CHILDREN}
8.
Add the following
syntax from the current cursor position (in the new space created by pressing Enter
twice above):
FILTER(
9.
On what is now
the seventh row of the query (including the comment line at top), place the
cursor to the immediate right of the following:
{[Reseller].[Reseller Type].[Business Type].[Value Added Reseller].CHILDREN}
10.
Add a comma (
, ) to the right of the existing right curly brace, which immediately
precedes ON AXIS (1) within the row.
11.
Press the
Enter key twice to create a new line between the line of the query on which
we have placed the comma and the line that currently follows it, namely:
ON AXIS (1)
12.
Type the
following syntax into the new row:
[Measures].[Reseller Order Quantity] > 10
13.
Ensuring
placement of the cursor to the right of the 10 in the line typed
above, press the Enter key twice more, to create a new line between our
newly added syntax and the line that currently follows it, namely:
ON AXIS (1)
14.
Type a right
parenthesis ( ) ) into the new row:
The effect, of course, is to enclose the specified set, {[Reseller].[Reseller
Type].[Business Type].[Value Added Reseller].CHILDREN} within the newly
added FILTER() function, predicated by the logical expression, [Measures].[Reseller
Order Quantity] > 10, which FILTER() will evaluate against each
member in the the specified set.
Once
we have incorporated the FILTER() function via the steps above, the Query
pane appears as shown in Illustration 5.
Illustration 5: Adjusted Query in the Query Pane
(Modifications Circled)
15.
Execute the
query by clicking the Execute button in the toolbar, as before.
The Results pane is populated by Analysis
Services, and the dataset partially depicted in Illustration 6 appears.
Illustration 6: Results Dataset (Partial View) FILTER()
at Work
The client representatives agree
that the FILTER() function has had the expected effect: only Value Added Resellers whose Reseller Order
Quantity value exceeds ten units appear. We have thus filtered the
members of the row axis successfully. Next we will demonstrate the use of HAVING
as a substitute for the FILTER() function in obtaining the same
results.
16.
Select File
-> Save MDX053-001-2.mdx.
17.
Replace the
comment line in query MDX053-001-2 with the following:
-- MDX053-001-3 HAVING Clause: Simple Filter Alternative
18.
Select File
-> Save MDX053-001-2.mdx As ..., name the file MDX053-001-3,
and place it in the same location as its predecessor, to protect the former
query.
19.
Remove the
line of the query containing FILTER( (currently the sixth line of the query),
which we added earlier.
20.
Remove the
line of the query containing ) (the eight line of the query, after
removing FILTER( above).
21.
Remove the
comma ( , ) to the right of the existing right curly brace of the
string {[Reseller].[Reseller Type].[Business Type].[Value Added
Reseller].CHILDREN}, which currently appears in the sixth row.
22.
Replace the
expression in the row immediately below ( {[Reseller].[Reseller
Type].[Business Type].[Value Added Reseller].CHILDREN} ) with the
following clause:
HAVING [Measures].[Reseller Order Quantity] > 10
The
modified query should look something like this:
-- MDX053-001-2 Using FILTER() to Narrow Returned Data Set
SELECT
{[Measures].[Reseller Order Quantity], [Measures].[Reseller Sales Amount] }
ON AXIS(0),
NON EMPTY
{[Reseller].[Reseller Type].[Business Type].[Value Added Reseller].CHILDREN}
HAVING [Measures].[Reseller Order Quantity] > 10
ON AXIS(1)
FROM
[Adventure Works]
WHERE
([Product].[Product Categories].[Category].[Components],
[Date].[Calendar].[Calendar Year].[CY 2003] )
The
effect of our modifications is to substitute the HAVING clause for the FILTER()
function, with the objective of filtering our row axis to deliver the same end
results dataset as that delivered by the query via the FILTER()
function. As before, the intent is to evaluate the logical expression, [Measures].[Reseller
Order Quantity] > 10, against each member in the specified set.
Once
we have substituted the HAVING clause via the preceding steps, the Query
pane appears as shown in Illustration 7.
Illustration 7: Newly Adjusted Query in the Query Pane
(Modifications Circled)
23.
Execute the
query by clicking the Execute button in the toolbar, as before.
The Results pane is populated by Analysis
Services, and the dataset partially depicted in Illustration 8 appears.
Illustration 8: Results Dataset (Partial View) HAVING in
Action
Our client colleagues concur that the HAVING clause
has had the expected effect, and has replicated the action of the FILTER()
function above. Several members of the group state that they prefer filtering
via the HAVING clause, as they deem the coding easier to understand.
24.
Select File
-> Save MDX053-001-3.mdx to ensure that the file is saved.