Using a multi-value parameters in SSRS reports is giving the users the flexibility to filter on one or more values. In the following example they can filter on one or more products and on one or more customers.
SSRS is passing the parameters to the stored procedure as joined strings. This means that it is very important to use a “safe” delimiter. Comma (,) or semicolumn (;) are not good enough, because they may exist in the filtered column (we may filter on column Part and a part ABC,DEF is an existing one… :-(). Of cource, the best practice it to filter on “Value-Label” pair where “Value” is the ID column of the structure table, but not always the data is clean enough.
I use CHAR(164) –> ¤ as a delimiter. It is not on the keyboard and most likely doesn’t exist as a data.
Datasets for the SSRS parameters.
USE AdventureWorks2008R2; GO -- Product SELECT DISTINCT ProductID AS [Value], Name AS [Label] FROM Production.Product ORDER BY [Label]; GO -- Customer SELECT DISTINCT BusinessEntityID AS [Value], FirstName + ' ' + LastName AS [Label] FROM Person.Person ORDER BY [Label]; GO
This example is just a preview of the logic that is using a dynamic SQL to convert the SSRS multi-value parameter to IN statement.
I will expand the code to check if the passed values are “ALL” and if “ALL”, the parameter will not be included in the WHERE clause.
Create database
USE [master];
GO
IF (DB_ID('PL') IS NOT NULL)
BEGIN
ALTER DATABASE PL SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE PL;
END
GO
CREATE DATABASE PL;
GO
USE PL;
GO
Create stored procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Peter Lalovsky
-- Create date: 2014-12-23
-- Description: Example to convert SSRS multi-value parameter in
-- =============================================
CREATE PROCEDURE usp_SSRSMultiValueParameters
(
@Product VARCHAR(MAX) -- multi-value, delimited with CHAR(164) --> ¤
, @Customer VARCHAR(MAX) -- multi-value, delimited with CHAR(164) --> ¤
, @DueDateStart DATETIME -- single-value
, @DueDateEnd DATETIME -- single-value
)
AS
BEGIN -- pl.usp_SSRSMultiValueParameters
SET NOCOUNT ON;
--** Parameters - Start **--
SELECT
@Product = REPLACE(@Product, CHAR(164), CHAR(44)) -- ¤ --> ,
, @Customer = REPLACE(@Customer, CHAR(164), CHAR(44));
--** Parameters - End **--
--** Get the report - Start **--
DECLARE @SQL VARCHAR(MAX);
SET @SQL = '
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT
SOH.SalesOrderNumber
, SOH.OrderDate
, SOH.DueDate
, Pe.FirstName + '' '' + LastName AS CustomerName
, Pr.Name AS ProductName
, SOD.OrderQty
, SOD.LineTotal
FROM
AdventureWorks2008R2.Sales.SalesOrderHeader AS SOH
JOIN AdventureWorks2008R2.Sales.SalesOrderDetail AS SOD
ON SOH.SalesOrderID = SOD.SalesOrderID
JOIN AdventureWorks2008R2.Production.Product AS Pr
ON SOD.ProductID = Pr.ProductID
JOIN AdventureWorks2008R2.Sales.Customer AS C
ON SOH.CustomerID = C.CustomerID
JOIN AdventureWorks2008R2.Person.Person AS Pe
ON C.PersonID = Pe.BusinessEntityID
WHERE
SOD.ProductID IN (' + @Product + ')
AND Pe.BusinessEntityID IN (' + @Customer + ')
AND SOH.DueDate BETWEEN
''' + CONVERT(CHAR(10), @DueDateStart, 120) + '''
AND ''' + CONVERT(CHAR(10), @DueDateEnd, 120) + '''
ORDER BY
SOH.DueDate
, SOH.SalesOrderNumber
, Pr.Name
';
--PRINT @SQL
EXEC (@SQL);
--** Get the report - End **--
END -- pl.usp_SSRSMultiValueParameters
GO
Test stored procedure
EXEC usp_SSRSMultiValueParameters @Product = '988¤712¤880' , @Customer = '8034¤13577¤641' , @DueDateStart = '2007-03-01' , @DueDateEnd = '2007-09-30'; GO
Delete database
USE [master];
GO
IF (DB_ID('PL') IS NOT NULL)
BEGIN
ALTER DATABASE PL SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE PL;
END
GO
udf_SplitString() is a great resource when CHAR(164) –> ¤ is not a good delimiter.
Keep it simple :-)

