After working with SQL Server Reporting Services ( SSRS ) for a time, eventually a report user will want to select more than one value for a parameter. How does SSRS handle multi-value parameters?
Allowing users to select multiple values from a parameter list is a great feature in SSRS; it allows report consumers more selection flexibility when running reports. However, how the parameters are passed to the dataset (or report object filters if used) varies depending on if the datasource is based on T-SQL embedded in a dataset within a report or if the data is passed via a stored procedure. Furthermore, once the report is run, a good practice is to display the selected parameter list somewhere within the report. The below instructions will convey the various techniques needed to utilize multi-value parameters. Finally, we will cover using a filter based multi-value parameters.
Of the various options, passing multi value parameters to an embedded query is the less complex of the two methods. Utilizing this method, the report designer just needs to write a normal query, and utilize the "IN" key word in the criteria section of the query and then refer to the multi-value parameter using the @parameter name syntax within the parentheses portion of the IN statement. Using an AdventureWorks database and report example, the below code, inserted into a report dataset, notates the required syntax. This syntax should be somewhat standard to many of you who write T-SQL on a daily basis.
SELECT P.FirstName + ' ' + P.LastName AS Employee ,DATEPART(Year, SOH.OrderDate) AS Year ,DATEPART(Month, SOH.OrderDate) AS MonthNumber ,DATENAME(Month, SOH.OrderDate) AS Month ,SUM(DET.LineTotal) AS Sales FROM Sales.SalesPerson AS SP INNER JOIN Sales.SalesOrderHeader AS SOH ON SP.BusinessEntityID = SOH.SalesPersonID INNER JOIN Sales.SalesOrderDetail AS DET ON SOH.SalesOrderID = DET.SalesOrderID INNER JOIN Sales.SalesTerritory AS ST ON SP.TerritoryID = ST.TerritoryID INNER JOIN HumanResources.Employee AS E ON SOH.SalesPersonID = E.BusinessEntityID INNER JOIN Person.Person AS P ON P.BusinessEntityID = SP.BusinessEntityID WHERE (YEAR(SOH.OrderDate) IN (@ReportYear)) GROUP BY P.FirstName + ' ' + P.LastName ,SOH.SalesPersonID ,DATEPART(Year, SOH.OrderDate) ,DATEPART(Month, SOH.OrderDate) ,DATENAME(Month, SOH.OrderDate)
Next we will setup the parameter to accept multiple values. In the below example, a parameter called @ReportYear is already created, so right mouse clicking on the parameter (Report Year in the below example ) and selecting Parameter Properties will open the Report Parameter Properties window. Now check the Allow multiple values option. If you are setting up a new parameter, right mouse click on Parameters and then select New Parameter.
Subsequently, we will define the values to be used for our parameter list; this list will be the values presented to the report consumer. First, we define a dataset using the following simple query to generate a list of values for the ReportYear field.
SELECT Year(SOH.OrderDate) AS Year FROM Sales.SalesOrderHeader AS SOH GROUP BY Year(SOH.OrderDate) ORDER BY SOH.Year
Finally, we set the dataset, Year_Lookup, to be used for the available values for the ReportYear parameter, and note below.
Now, the parameter is setup to accept multiple values. The setup required several steps including setting up our main report query to accept a parameter using the IN criteria, changing the allow multiple values option on the parameter properties, and last, generating a list of available values, in this example using another query. The end result is two fold. First, the report consumer now sees check boxes next to each parameter item which allows the user to select multiple values, as displayed below. Second, the report displays only the years selected.
Finally, the report data displays the years selected.
You may notice in the above figure that the title shows #Error after Sales Report for:.This field references the @ReportYear parameter; when this parameter was just a single value, it displayed correctly. However, now that the parameter is multiple values, SSRS is unable to display a value. Fortunately, the SSRS Join function can be used to display all the values selected and separate them with a fixed symbol. For this report we will break up the years with an &. The exact formula used is as follows:
=JOIN(Parameters!ReportYear.Value, " & ")
The report with the JOIN function utilized is displayed below.
Multiple Value Parameters and Stored Procedure Based Datasets
Using stored procedures as the basis for SSRS datasets offers many advantages including potential reuse by other reports and potential performance advantages. However, multi-value parameters do not work well when getting passed to a stored procedure. Embedded SQL datasets noted above handle the parsing of the multiple values used in the IN criteria. To the contrary, when the multiple values are passed to a stored procedure, all the values are conveyed as one value.
The ReportYear parameter in our example report, for instance, would get passed as one value, "2006,2007,2008" which, of course would return no rows. Fortunately, we can use a string splitter function as part of our stored procedure to break up the years into multiple values. We will once again turn to using a Tally table by Jeff Moden; please see this article on using the Tally table to parse out the values, http://www.sqlservercentral.com/articles/Tally+Table/72993/.
I am not going to repeat Mr. Moden's code in the article, since it would be beneficial for you to understand what it can and cannot do. We will however use this function in dataset stored procedure which is noted below. Please make the following notes about the stored procedure. First we are passing in the concatenated multi-value parameter as a single entity and we use a varchar parameter (which must be large enough to accept the maximum length of all the parameters which might be selected). Next using the splitter function, the values are parsed out and placed into a temporary table, #YEAR_LIST. Last, the year criteria is moved from being part of the where clause to being part of the joins.
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: Scott Murray -- Create date: 01/01/2013 -- Description: Sales by year query with parameter breakout -- ============================================= ALTER PROCEDURE dbo.usp_Sales_by_Year @ReportYear varchar(50)
AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON;
--Parse values into table which will be an inner join on main data query. SELECT Item INTO #YEAR_LIST FROM dbo.DelimitedSplit8K(@ReportYear,',')
SELECT P.FirstName + ' ' + P.LastName AS Employee ,DATEPART(Year, SOH.OrderDate) AS Year ,DATEPART(Month, SOH.OrderDate) AS MonthNumber ,DATENAME(Month, SOH.OrderDate) AS Month ,SUM(DET.LineTotal) AS Sales FROM Sales.SalesPerson AS SP INNER JOIN Sales.SalesOrderHeader AS SOH ON SP.BusinessEntityID = SOH.SalesPersonID INNER JOIN Sales.SalesOrderDetail AS DET ON SOH.SalesOrderID = DET.SalesOrderID INNER JOIN Sales.SalesTerritory AS ST ON SP.TerritoryID = ST.TerritoryID INNER JOIN HumanResources.Employee AS E ON SOH.SalesPersonID = E.BusinessEntityID INNER JOIN Person.Person AS P ON P.BusinessEntityID = SP.BusinessEntityID INNER JOIN #YEAR_LIST AS YEARLIST ON YEAR(SOH.OrderDate) = YEARLIST.Item --Use join instead of where clause --WHERE (YEAR(SOH.OrderDate) IN (@ReportYear)) GROUP BY P.FirstName + ' ' + P.LastName ,SOH.SalesPersonID ,DATEPART(Year, SOH.OrderDate) ,DATEPART(Month, SOH.OrderDate) ,DATENAME(Month, SOH.OrderDate)
Certainly other methods exists to handle the parsing and include using the function in the where clause (I would avoid this method as I would not want to call this function for every row!). Alternatively, you could use a cross apply to match the years with the dataset. The final report utilizing the stored procedure methods is displayed subsequently.
Using Filters with Multiple Value Parameters
One last alternative involving the use of parameters pertains to dataset or object filters. Using filters at the object level, for example on a tablix, actually allows the same "larger" dataset to be used for multiple purposes while at the same time filtering the individual report parts based on a particular criteria. This setup can be advantageous in using a single dataset for all the report data; however, you also need to be careful about retrieving "very large" datasets while only using very small sets of the data. To setup a filter, first select the object in question, and then right mouse, click and select properties as illustrated below.
On the properties window, select the Filter window, and click add. Select the "field" or expression that is to be filtered, and then select the "In" Operator. Last, click on the fx expression builder button to the right of the value field to open the expression builder box.
As shown in the below illustration, within the filter expression box, click on Parameters under Category, and then double click on ReportYear Under years. What appears in the expression value box includes "(0)" at the end of name. This zero actually means retrieve the parameter in ordinal position 0 (ie the first parameter of the selected parameters). As you are probably realizing, that is not what we would like to filter on; we want to filter on all the parameter values selected. The trick to make the filter work, is as easy as removing the "(0)" from the expression.
Finally, the filter expression value actually should look as displayed below.
Now, the resulting report using a multiple value tablix filter is illustrated below.
Multiple value parameters are a wonderful tool within SSRS; their methods in practice, though, varies depending on their usage within embedded T-SQL, within stored procedures, or as part of an SSRS object filter. Embedded T-SQL is somewhat easier to use, however, the query can not be easily shared; to the contrary, using a stored procedure offers the ability to reuse a query (and other set based and logic structures), but you must parse parameter. Using a multiple value parameter with an object filter is also easy to implement as long as you know how to implement the parameter values in the filter. Last, it is often beneficial to display the parameters selected by the report consumer; the join function in SSRS allows you to display the parameter list easily in the report header or body.
This process should work the same with non integer parameters. I am unsure which of the 3 options you are saying does not work, but a couple of items I would check include: 1. verify the parameter data type (on the General window/tab), 2. for filter based solution, again check the data types on the filter screen, 3. if being passed to a stored procedure, run a profiler while executing the report to see exactly what is being passed to the procedure... depending on those results, run the stored procedure though some query check..... see what values the function is inserting into the temp table.
This is something that is very useful for me. Since using SSRS I've been using VIEWS to be able to use multi-value parameters. However, I want to take advantage of using stored procedures, but the whole multi-value parameter issue came up. I read this article. but is there a universal splitfunction can be provided or is it different for everyone.
I'm not a master at creating any functions, so I would know where to start if I wanted to create a split function.
Can another article be created on how to make a split function?
Tuesday, January 22, 2013 - 8:59:04 AM - Martyn Jones
My report does have multivalues parameter and it is not rendering the values when I view the report. IF i passing single Values for the parameter it is working . If i pass multple Values fro the parameter it is not working . I have tried the join that you mentioned in the parameter properties still it is not working for me. Any other ideas pleas!!
My report does have multivalues parameter and it is not rendering the values when I view the report. IF i passing single Values for the parameter it is working . If i pass multple Values fro the parameter it is not working . I have tried the join that you mentioned in the parameter properties still it is not working for me. Any other ideas pleas!!"
I have the exact same situation and have have tried all the suggestions posted. Has any one found a solution?
Great. It works well for me but I would like help with one related thing.
I have a multi value parameter to decide what graphs should be visiable. And for every one of the graphs I would like to do an expression to find out if this graph has been selected to be visiable. An expression something like "=LockupMemberFunction(1,Parameter!ParamViews.Value)" where 1 is the first graph and ParamViews is the multi value list of selected graphs (for example 1,4,5). Please help me with that expression.
Tuesday, June 18, 2013 - 11:00:59 AM - Scott Murray
I have an interesting situation, I use the multivalue filter on a dataset and it works just fine in a development environment. That is the filter will always work whether I choose the "select all" or any single or multiple item selections available for the parameter.
Again this is when I am editing and testing a report with the Report Builder.. However when I execute the report either from a url link or by directly clicking on the saved report (to run it), the display works fine the first time through (because my default is all available values). But as soon as I filter (select a single or more than 1 available items, and then click "View Report" the effect is as if the entire result set has been filtered out.
Wednesday, June 26, 2013 - 1:56:15 PM - Bruno Servulo
This is a nice article for beginners, but I've noticed a few doubts regarding filtering.
First, I would not use the filter like you, because that would work AFTER the SELECT's been made and so forth consuming unnecessary resources from your database.
You can use the report parameter in a dataset, and using the arrows in reportdata tab you can select the correct order. this way, you'll only select the data you want and apply the filter directly in the select.
another thing, to filter using multivalue parameters you can use IN (@parameter) operator. This will be much more performance friendly than CHARINDEX.