articles

You are currently browsing articles tagged articles.

Unfiltered Coffee - http://www.flickr.com/photos/derektor/92480692/In this article I’ll be describing a method which you can use to filter data in your Reporting Services reports without making any changes to the dataset query.

To get started, I’ll first imagine I’m being interviewed.  (Yeah, I like that.)

Q: You mentioned “without making any changes to the dataset query”.  So you’ll be filtering on the SSRS side then?

A: (Darn, I think she’s onto me.)  Well, yes.

Q: Would you recommend this method?

A: (Yep, she is.) Well, erm, no.  For performance reasons it’s much better to filter on the database server side.  You don’t waste any network bandwidth and database servers are specialized in filtering data.

Q: Then why are you writing an article about filtering on the report side?

A: Well, because sometimes you don’t have another option.  Not all data sources are as flexible as SQL Server, and SSRS supports many different providers.  In some occasions, the DBA that supports exotic data source X gives you a command, similar to a stored procedure call, that returns the data for your report.  And you don’t get any say on how that gets implemented, you’re just told to use that command.  Unfortunately that command returns items A-Z while the report has an item filter.  In those scenarios, it’s interesting if you’d be able to filter on the report side.

Q: Let’s say I would like to filter on the database server side, how would I do that?

A: Just use the WHERE clause in your query.

Alright, enough intro, time to start the article!

To build the report I’ll be using SQL Server 2012, more precisely:

Microsoft SQL Server 2012 (SP1) – 11.0.3000.0 (X64)
Oct 19 2012 13:38:57
Copyright (c) Microsoft Corporation
Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)

I’ve made the RDL available for download from my SkyDrive.

The Scenario

You’ve been asked to develop a report that shows a list of products, grouped by category.  The report needs to take a filter on product category into account.  The values on which you need to filter are stored in a configuration table.  You’ve been given a query which for some mystical reason can’t be modified.

Weird enough?  Okay, let’s get that implemented then!

The Report

Let’s first set up a simple report with a dataset and a table.  The table groups the products on category:

A Grouped Table

In Preview it would look like this:

List of products grouped on category

The dataset used in the report above is called ProductList and uses the following query:

select DPC.EnglishProductCategoryName ProductCategoryName
    , DPS.EnglishProductSubcategoryName ProductSubcategoryName
    , DP.EnglishProductName ProductName
from dbo.DimProduct DP
inner join dbo.DimProductSubcategory DPS
    on DPS.ProductSubcategoryKey = DP.ProductSubcategoryKey
inner join dbo.DimProductCategory DPC
    on DPC.ProductCategoryKey = DPS.ProductCategoryKey
order by DPC.EnglishProductCategoryName, DPS.EnglishProductSubcategoryName
    , DP.EnglishProductName;

Retrieving The Filter Configuration

So now we need to filter this report based on configuration stored in a table.  That means we’ll need an additional dataset, let’s use the following query and call it CategoryFilter:

select 'Category' FilterName, 'Components' FilterValue
union all select 'Category', 'Clothing'

(Sure, I’ve hardcoded the configuration data but for scenario’s sake, imagine it’s coming from a table.)

Here’s what the result looks like:

The Category Filter

The dataset contains two fields: the name of the filter and a value on which to filter.  Each value on which we need to filter has its own record.  Having this FilterName field in the table means that it can be re-used for several different filters, if needed.  Your dataset would then use a WHERE FilterName = ‘YourFilter’ clause.

Now, one crucial question remains: how can we use this dataset to filter the data from the other dataset?

Depending on your requirements, there are several possibilities. If you’re sure none of your data regions require the data that will get filtered out, you can use the Filter options on the Dataset.  Alternatively you can use the Filter options on your data region.  In any case, those filters all use the same interface, so implementation-wise it’s all the same.

I’ll implement the filter on the tablix, this gives me the opportunity to show how to get to the properties (dataset is easier).

Okay, so how do we implement a filter on a tablix?

First select the Tablix and right-click the grey square in the top left corner:

Opening the Tablix Properties

Select Tablix Properties and switch to the Filters page.  Click the Add button to add a filter.  As you can see, filters support expressions.  That’s a good thing!

In the Expression combobox, select the field on which you want to filter the data.

The Operator that fits with our requirements should be one that supports multiple values.  In case you’re wondering why, take a good look at our CategoryFilter dataset mentioned earlier.  We want to filter on both Components and Clothing.  So the default equals sign is not really what we’re after, the IN operator will serve much better.

The only remaining empty textbox is Value.  How can we get the values from our filter dataset into this textbox?  In any case, we’ll need to use an expression so click the little fx button next to the Value textbox.

Adding a Filter to the Tablix

As you’re probably aware, a data region can only be linked to one dataset.  Which means we can’t say:

=Fields!FilterValue.Value

because FilterValue doesn’t exist in the dataset that’s linked to the tablix.

So how can we implement a filter based on data from another dataset?  Once again we’ve got several options.  Let’s look into them!

Option One: Report Parameter

If we’d set up a Report Parameter, we could reference that, right?  Let’s close the properties popup for now (click OK) and create a new report parameter:

Adding a report parameter

Our parameter is called CategoryFilter, its type is Text and it should allow multiple values.  We’ll also set visibility to Internal because it doesn’t need to get exposed in any way.

The parameter’s default values are the values retrieved by the CategoryFilter dataset, so switch to the Default Values page and specify the dataset as shown in the screenshot:

Retrieving the default values from a query

With the parameter created, switch back to the Filters page on the Tablix Properties and enter the following expression:

=Parameters!CategoryFilter.Value

If you’ve created the expression by double-clicking the parameter in the Values box, don’t forget to remove the (0) or your data will get filtered on the first item only.

Click OK and render the report:

Report Preview now shows Clothing as first category

It’s working!  Accessories is no longer the first category that gets shown because it’s been filtered out.  The data is now filtered on the configuration as specified through the second dataset.

But… do we really need to set up a report parameter for this?  Well, no!  Let’s investigate option number two.

Option Two: The LookupSet Function

A couple of years ago I wrote an article to explain how you can retrieve data from another dataset through the new lookup functions.  Well, “new” at the time meant SQL Server 2008 R2.  We’re now on 2012 so the functions still exist!

The function that can help us out today is LookupSet.  Open up the Filter properties once again and replace the Value expression with the following:

=LookupSet("Category", Fields!FilterName.Value, Fields!FilterValue.Value, "CategoryFilter")

Close the popup window and preview the report.  If it runs fine, delete the report parameter and run the preview again.

Filtering the data using the LookupSet function

Yes, it still works, nice!  So we don’t need to use a report parameter to filter our data, we can use a fairly simple expression using the LookupSet function!

Option Three: The Split Function

To finalize let’s investigate a variation on the requirement.  In this variation, the filter configuration doesn’t need to come from a table.  It can just be hardcoded in the report.  Can we do that, without using an additional dataset?  Yes we can!

In the Filter properties, change the Value expression to:

="Components,Clothing"

Now render the report:

Oh no, the data is gone!

Hmm, that didn’t work well.  Ow right, the IN operator expects an array of string values and not a comma-separated value string.  Let’s see, how can we get an array out of a character-separated value string?  Actually, I’ve covered this in one of my #tsql2sday posts.  We can use the Split function:

=Split("Components,Clothing", ",")

With that expression specified, let’s render the report a last time:

Filtering the tablix using the Split function

Woohoo, it works once again!

Conclusion

In this article I’ve demonstrated three different possibilities to implement filtering on multiple values on the report side.  It is not a recommended method but in some cases it can be very useful, especially when you don’t have another choice!

Have fun!

Valentino.

Share

Tags: , , , ,

Now and then I get inspired to write about a certain topic as a result of what I encounter on the forums.  Here’s one of those moments.

The examples in this article are created using SQL Server 2012 RTM – 11.0.2100.60 (X64).  I’m also using the free AdventureWorksDW2012 sample database available at CodePlex.  But that doesn’t imply that you actually need 2012 to get this to work.  The principle explained in this article should also work on SQL Server 2008 and even 2005.

You can download the resulting RDL from my SkyDrive.

Introduction

Have you ever needed to create a report that shows a list of something spread over multiple columns?  Then you may have come across a feature called newsletter-style reports.  Excited to have found out about that feature, you started creating your report.  But alas, upon rendering the preview you discovered that it didn’t work.  Further investigation made you conclude that the feature is only supported in two specific renderers (PDF and Image).  Ouch!

In this article I’ll be showing you a method to create multiple-column reports that you can actually use with all renderers!

Setting The Scene

We’ve been asked to create a report that shows a list of product codes with each category displayed on a separate page.  The codes should be displayed in four columns.

Implementing The Report

To get these requirements implemented, we’ll be using some T-SQL skills in combination with nested tables.  So, first step is the dataset query.

The Dataset

The query retrieves only the fields needed in the report plus one additional, calculated field called DisplayColumn.  This additional field indicates the column in which the code should get displayed.  Here’s what the results look like:

List of product codes with column number by category

As you can see, the column numbering starts at 1, goes up to 4 and then starts again at 1.  It also starts at 1 again at the start of a new category.  This is needed because we’ll be grouping the data on category in the report.

Now, let me show you that query and explain a bit how the DisplayColumn is getting calculated:

declare @numberOfColumns int = 4;

select dpc.EnglishProductCategoryName, dp.ProductAlternateKey
    , (ROW_NUMBER() OVER (
        PARTITION BY dpc.EnglishProductCategoryName
        ORDER BY dp.ProductAlternateKey) + @numberOfColumns - 1) % @numberOfColumns + 1
    as DisplayColumn
from dbo.DimProduct dp
inner join dbo.DimProductSubcategory dps on dps.ProductSubcategoryKey = dp.ProductSubcategoryKey
inner join dbo.DimProductCategory dpc on dpc.ProductCategoryKey = dps.ProductCategoryKey;

The main ingredient here is the ROW_NUMBER function.  According to its definition, it “returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition”.

That brings us to the OVER clause, and its PARTITION BY statement.  In our example we partition by category because we need the numbering to start over for each category.

Next up is the ORDER BY part.  We order by ProductAlternateKey, which is the product code that our report should display.  We want the codes to be ordered alphabetically so the column numbering should take that order into account.

As you can tell, I’m using some numeric trickery on the result of the ROW_NUMBER function.  The reason for that is because I’d like my numbers to start at 1 and end at 4.  If we would just do “row_number % 4” (% is the modulo operator btw), then our last column would be zero instead of 4.

Okay, that’s part one of the method explained.  Over to the visualization part.

Setting The Tables

We’re going to create a table that shows the product category in a header with the product codes spread over four columns below the header.

So, first drag a table into the design canvas and give it an additional column.

To quickly link the table with the dataset, drag the product code field into the detail cell of the first column.

Now we’re going to set up the grouping.  We’ll also be making use of nested tables and because tables cannot be nested on the detail level of a tablix, we need to set up grouping even on that detail level.  So first, open up the Group Properties of the Details level by right-clicking it in the Row Groups pane:

Opening up the Detail-level Group Properties

Click the Add button to add a group and group on the product category:

Group the Detail level on product category

We want to display a header on the category level, so we’ll need to add another group on top of this one.  Right-click the Details row group once more and add a Parent Group:

Adding a Parent Group to the Details level

Group by product category once more and activate the Add group header checkbox:

Adding parent group on product category

Here’s what our table now looks like:

Some cleaning-up to be done

We don’t need that first column and we don’t need that top row, so delete both.  Now select the four textboxes in the upper row, right-click and select Merge Cells:

Merging the header cells to prevent text getting cut off or wrapped

This way the header line gets to use all available space.  Now put the product category field into the remaining merged cell and render the report:

Grouping set up as expected

If all went well you should be seeing the four categories.  That means the grouping part is implemented.

Of course, the detail level is not yet as needed because only the first code of each category is being shown and the three other columns are still empty.

So let’s address that now!

Nesting The Tables

Currently our four detail-level cells are just plain textboxes.  But textboxes don’t allow filtering and we need filtering because the first column should show only the product codes with DisplayColumn equal to 1, second column 2, and so on.

So what we’re going to do is nest another tablix inside each of those four cells.  This nested tablix should be one-celled, so just one column and one detail-level row.  That can be done through several ways.  I’ll show you two.

To set up the first detail-level cell, drag another Table inside that first textbox (the one that currently contains the product code).  That gives us this:

Nesting a table in a table

Now select the nested table by clicking one of its cells:

Select the nested table

It may be a bit tricky so if needed, temporarily increase the height of the detail-level row to enlarge the nested table.

Now remove two columns and remove the header row of the nested table.  Then right-click the grey square in the upper-left corner and open up the Tablix Properties:

image

These are the properties of the nested table.

Switch to the Filters page, click the Add button and set the filter to DisplayColumn = 1.

Filter on DisplayColumn

Click OK to close the properties.

Now put the product code field again in that first detail-level cell and render the report:

One column done, three remaining!

One column done, three remaining!

Let’s set up the second column.  This time, drag a List into the second detail-level cell:

image

A List is in fact a one-celled tablix with a Rectangle inside.  But we don’t need that rectangle here, we need a Textbox.  So select the rectangle by clicking once inside the cell.  You can tell that the rectangle is selected by looking at the Properties window, as shown above.

Now hit the Delete button on the keyboard.  In the Properties window, you should see Rectangle changing into Text Box:

Removing the Rectangle from the List leaves us with a Text Box

Once more we’ve got a one-celled tablix.  Set up filtering on DisplayColumn = 2, using the method as explained when setting up the first column (hint: upper-left grey square).

Put the product code field inside the second detail-level cell and render the report:

Second column set up

If all went well, your second column should now display some codes as well.

Two more to go!

Setting up the remaining two columns is actually really easy.  Select the nested tablix inside the second cell by clicking the small grey square in the upper-left corner.  Then hit CTRL+C, select the third detail-level cell and hit CTRL+V.  Also paste into the fourth detail-level cell.

Now change the filtering to 3 and 4 for column three and four and render the report:

All four columns display product codes

All four columns display product codes!

What now remains is some visual cleanup and adding page breaks on the category group.  I’m only going into details on the page break.

In the Row Groups of the main table, right-click the upper group and select Group Properties.  Then switch to the Page Breaks page and check the Between each instance of a group checkbox:

Adding a page break on the category group

Render the report to see the final result:

Report implemented: product codes in four columns grouped per category!

The first page displays the product codes of the first category, spread over four columns!  The other pages contain the other categories.

Requirements implemented!

Conclusion

Even though it takes a bit of work and some tricks, it is possible to create multi-column (aka newsletter-style) reports using basic SSRS components whilst still supporting all renderers.

I hope you enjoyed this article, have fun!

Valentino.

References

ROW_NUMBER() function

Share

Tags: , , , ,

Dear Reader,

If you have read my previous post, you know that one of my articles was in the running for the first ever SQLServerPedia Awards.  And guess what?  I’ve won in the Business Intelligence category!

At first I wasn’t convinced that I’d be able to get enough votes.  Jen Stirrup had been scoring quite a bit as well.  But after a couple of days I noticed that my votes kept coming in too.  So I decided to contact some more people, such as ex-colleagues, the experts at Experts Exchange, and even my neighbours!

The result: 181 votes, which is 53 more than the next contestant in the category.  If you’re one of those 181: thank you!!  In the overall score list, my article ended up at the third spot!  Not too bad for a first-time nominee I suppose :-)

I can now add an extra logo to my blog:

SQLServerPediaAwards_badge-winner-bi

And my statue is on its way too.  Plus every nominee will receive a T-shirt.  Hopefully it’s one for regular use and not another one in the “suitable for household chores and bike cleaning only”-category…

Ow, congrats to all other winners and nominees!  To be honest, the nomination means more than the actual voting.  To get nominated, the editors (Iain Kick, Kevin Kline and Jeremiah Peschka) need to select your article out of all those present at SQLServerPedia.  Which means that it needs to be of a certain quality.  While the voting only shows how many people you know…  But the winner badge sure looks nice of course :-)

Have fun!

Valentino.

Share

Tags: , , ,

I Need Your Help

Dear reader,

Usually I’m helping you out by posting articles, quick tips or just some narration of my experience on my blog.  This time however I’m the one who needs your help.

I’m happy to announce that one of my articles – Your First OLAP Report, republished at SQLServerPedia – has been nominated in the 2010 SQLServerPedia Awards!

So, may I kindly ask you to vote for my article?  You can find it in the Business Intelligence category here: http://pulse.sqlserverpedia.com/awards/category/business-intelligence/

As you can see, I’ve got quite some competition so I can really use all the votes.

Many thanks!!

PS: I’ll reward you by continuing to post articles and other posts on my blog ;-)

As usual, have fun!

Valentino.

Share

Tags: , , ,

Since a while now, about two months and a bit to be exact, I’ve been publishing some of my articles on Experts Exchange.  It all started when their Content Coordinator found my blog and convinced me to start doing that, thanks for that Jenn (aka jennhp)!

In total I have now six articles published and four of them are EE Approved.  What does that mean?

EE-Approved is a designation awarded by the Page Editors to show that your Article is of superior quality: either it discusses something new, or provides a unique and original solution to a particularly interesting problem. If your article is selected as EE-Approved, you will receive 1,500 points.

When I published my first article back in September, getting an article EE Approved earned me 1,500 additional points on top of the 500 for getting it published.  This was recently changed to 4,000.  Which I really appreciate and I think was needed to make this a success.  When comparing the effort that goes into writing a good article with the effort to decently answer a 500 points question I think it is still not comparable.  A question can earn me 2,000 points (when the answer gets A-graded, most of my answers do) in just a couple of minutes – which doesn’t mean that all questions are solved this easily,  believe me.  But an article always takes several hours to write and publish.  Just to give you an idea: for my last article it took me one hour just to get it converted from my version in Live Writer to the tagged format expected by the “Write a new article” feature.  But then again, the minimum length requirements for an article is 300 words while my last article counted close to 2,500 words.

What should I conclude from the above paragraph?  That I should learn to make my articles shorter?  Or that Article Points and Expert Points received by answering questions are not comparable?  Well, probably both.  What I should learn is to split articles up when possible, but I will still keep mentioning the necessary details which in my opinion increases the quality indicator :-)

Anyway, it seems that I’ve been doing a good job because this is what the situation looked like on November 23, 2009.  Following screenshots were taken while filtering on Microsoft articles.

Five articles in Microsoft top 10

Not only did my articles control the Top 3 of most popular Microsoft articles,  they were also at position nine and ten, so in total I had five of my articles in the Top 10.

(The filter on Microsoft articles is highlighted in green btw.)

Highlighted on the left you can see one of my articles being featured.  This is a result of it being EE Approved.

As the next two screenshots show, it wasn’t the only one.

One of my EE Approved articles featured at Experts Exchange

And here’s number three:

My "Pie Chart Techniques" article feature at Experts Exchange

On that same page we can also see the Top 25 of Microsoft Contributors.  I was at position 7 with almost 15,000 points.  (Today I am at position 6, with about 20,000 points.)

Me at position 7 in the Top 25 of Microsoft article writers

So, why am I doing this?  Let’s see, I have several reasons:

  • I hope people will find them useful, which means I have been able to teach them something
  • It helps to improve my writing skills
  • It encourages me to explore details about certain topics that I wouldn’t need to explore otherwise
  • It brings me into contact with interesting people
  • I found out that writing can actually be a fun thing to do!
  • Points == T-shirts :-)

Right, so I started this post by thanking someone, let’s finish it the way it started.

I’d like to thank mark_wills for his enthusiasm and support.  He’s the Page Editor that has reviewed my articles and selected several for the EE Approved status (in cooperation with mwvisa1 I believe).  Just to make sure I haven’t missed anyone, I’d like to say thanks to all Page Editors that have approved my articles!

If you’d like to have a look at the articles, they are mentioned in my profile at Experts Exchange.  While you’re at it, if you like them I wouldn’t mind if you clicked that little YES button :-)

Now if you’ll excuse me, I’ve got some articles to write.  (In an attempt to control the Top 5 – erm, yeah right – just kidding of course.)

Happy reading!

Valentino.

Share

Tags: , ,

© 2008-2013 A Developer's Blog All Rights Reserved