SQL Server 2005 Reporting Services does the yeoman's work
for reports that extract data from SQL
Server 2005 Analysis Services. Reporting
Services is good at providing the framework for report development, parameter
selection, report deployment, and controlling report access. However, sometimes
Reporting Services behaves differently
than expected or desired when dealing
with Analysis Services data and you need
to find imaginative solutions to work
around Reporting Services' limitations.
Unfortunately, sometimes the imaginative
solutions result in data-model or security
changes. Isolating reporting requirements
to the presentation layer as much as possible is preferable to making changes
to data models or security simply to
facilitate reporting. This article addresses
a common situation in which particular
report requirements aren't intuitively
available in Reporting Services. The
three-part solution to this problem uses
features available in Reporting Services
and Analysis Services to create a solution
that resides entirely in the report presentation layer.
The sample Reporting Services project,
which uses the AdventureWorks sample
database, is available for download at http://
www.sqlmag.com, InstantDoc ID 94827.
The project has two .rdl files: AW_Sample_
Problem.rdl: shows the problem report, and
AW_Sample.rdl: shows the solution. I'd
like to thank Al Ludlow, a data warehouse
developer at CIBER, for creating the bulk
of this solution.
Situation and Problem
A key business report for our sample
business is a sales report in which the
selection of an Analysis Services dimension member from the Geography hierarchy is a report parameter. A row group
exists on the sales report for each level in the Geography hierarchy. Business requirements dictate that members higher in the
dimension hierarchy (ancestors) of the userselected member should not be displayed in
the report. But Reporting Services doesn't
understand this requirement because there
isn't a facility within Reporting Services
to suppress higher-level members within
a hierarchy, so the report we get displays
unwanted ancestors. For example, when a
user selects Utah as the parameter for the
report, the report displays the Country level
along with Utah and its descendants. The
business requirement is violated when the
ancestor of Utah, United States, appears on
the report, as Figure 1 shows. The desired
display doesn't show ancestors of Utah, as you
can see in Figure 2.
Solution
The solution we create needs to hide the
undesired ancestors without affecting the
data model. We can accomplish our goal by using a mix of features in Reporting
Services and Analysis Services to hide the
undesired ancestors when the report is rendered. The technique for hiding the ancestors of the user-selected member uses three
distinct parts, all referenced from within the
report definition. The three parts are:
- Creating and referencing a data set
that uses MDX to provide the level-number
property of the selected report parameter.
- Creating a Visual Basic (VB) code
block that compares the member level for
display to the member level of the userselected parameter.
- Applying the Visibility property for
row groups to exercise the VB code block.
Create dataset for parameter-level number.
We can use "raw" MDX to define a new
dataset in Reporting Services. We can enter
the MDX by using generic query editor
mode, which not only gives us display values
(CAPTION and UNIQUENAME) but
also gives us the LEVEL.ORDINAL property, which our VB code block references
later. The generic query editor is available
in a new data set by clicking the Design
Mode button at the top of the Data tab
in the RDL definition. The Design Mode
button toggles the data set mode. We enter
the MDX (MDX is typed directly into the
data query pane of the Data tab in the RDL
definition), which provides the LEVEL .ORDINAL of the user-selected parameter.
The dataset, named DS Properties, is created
by the MDX code that Listing 1 shows
and referenced in the complete Reporting
Services project.
Insert VB code block comparing display
member level to the member level of the
user-selected parameter member level. The
VB code block that we're going to insert
into the report definition is a function
that compares the user-selected
parameter hierarchy level to the
current row level in the report.
The report row level corresponds
to the level in the hierarchy that
limits our solution to regular
hierarchies. The algorithm, which
you can see in Listing 2, compares
the user parameter-hierarchy
level (intLevel) to the row level (intRowLevel) and returns "false" when
the parameter level is less than the row level;
otherwise, it returns "true." To enter custom
code into a report definition, select Report,
Report Properties from the menu in the
Reporting Services development environment (the Business Intelligence Development Studio—BIDS). Click the Code tab
and enter the VB code as Figure 3 shows.
Use VB code block in the Visibility property
of row groups. Listing 2's VB code block is
applied from the Visibility.Hidden property
of the Row group. To access the expression
and work with the expression editor, you
highlight the row group in the Layout tab of
the RDL definition, right-click, and select
Properties. Click the drop-down box next
to the Hidden property and select <expression…> to display the expression dialog box
that Figure 4, shows.
The expression that we're editing:
=Code.ShowRow(Fields!Country.
Value,
First (Fields!ParameterLevel.
Value,
"DSProperties"), 1)
references the elements needed to return
true or false based on the level of the hierarchy referenced in the report and the
level of the user-selected parameter. In the
preceding line of code, the function call to
ShowRow() references the function defined
in our VB code block. The first parameter
contains the level number of the userselected parameter, First(Fields!ParameterL
evel.Value, "DSProperties"). DSProperties is the name of the dataset we defined, which
retrieves the level value of the selected
parameter.
The second parameter (1) references the
level of the hierarchy that shows up on the
row of the report. The following line of
code shows the call to ShowRow() for the
second row group of the report.
=Code.ShowRow(Fields!Country.
Value,
First (Fields!ParameterLevel.
Value,
"DSProperties"), 2)
The value 2 in the second parameter corresponds to the level of the hierarchy that's
on the report row group.
The solution in this article is a good
example of how to isolate reporting requirements to the presentation layer rather than
implementing model or security changes.
Your challenge is to try implementing this
solution within your own reporting environment and perhaps adapting the solution
to a more complex requirement than simply
suppressing ancestor display.
End of Article