{"id":338,"date":"2012-02-01T12:40:13","date_gmt":"2012-02-01T12:40:13","guid":{"rendered":"http:\/\/bicortex.com\/?p=338"},"modified":"2012-08-15T04:41:05","modified_gmt":"2012-08-15T04:41:05","slug":"ssrs-report-execution-with-multivalued-parameter-through-stored-procedure","status":"publish","type":"post","link":"http:\/\/bicortex.com\/bicortex\/ssrs-report-execution-with-multivalued-parameter-through-stored-procedure\/","title":{"rendered":"SSRS Report Execution With Multi-Valued Parameter Through Stored Procedure &#8211; Part 1"},"content":{"rendered":"<p style=\"text-align: justify;\">Recently, I have been racking my brains over a little SSRS inefficiency which hasn&#8217;t become apparent until I tried to use RANK() SQL function in one of the queries. As it turns out Query Designer in Report Designer does not play well with what is easily achievable in Management Studio (or other SQL editor for that matter) and common function expressions, for example RANK(), are still not supported. It is fairly well documented issue with SSRS (I&#8217;m running version 10.5 on SQL Server 2008 R2) and in most circumstances it can be rectified by building a custom view or running a query from a stored procedure (which should also positively impact execution performance). The problem with using stored procedure is that when we want to narrow the output scope by means of using selectable parameters with multivalued option (i.e. In(@Parameter) rather than =(@Parameter)), it is impossible to pass this this multivalued parameter through the very SP. This can be achieved by using a function within the SP to fetch parameter values from a table, rather than a string and passing the result into the SP for further crunching. This process seems to be more complex than it should be (why not just allow RANK() function in Report Designer!) so let me start from the beginning.<\/p>\n<p style=\"text-align: justify;\">Let&#8217;s say we would like to build a report which will display top five best-selling products in their categories for a given distributor and its corresponding warehouse by calendar month. In the report itself, you would like to make the distributor the first parameter the user will need to select and the warehouse a cascading parameter which stems from the distributor parameter and is marked as multivalued (see below).<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/bicortex.com\/ssrs-report-execution-with-multivalued-parameter-through-stored-procedure\/multi-value_parameters_through_sp\/\" rel=\"attachment wp-att-435\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-435\" title=\"Multi-value_parameters_through_SP\" src=\"http:\/\/bicortex.com\/bicortex\/wp-content\/post_content\/\/2012\/01\/Multi-value_parameters_through_SP.jpg\" alt=\"\" width=\"580\" height=\"186\" srcset=\"http:\/\/bicortex.com\/bicortex\/wp-content\/post_content\/2012\/01\/Multi-value_parameters_through_SP.jpg 580w, http:\/\/bicortex.com\/bicortex\/wp-content\/post_content\/2012\/01\/Multi-value_parameters_through_SP-300x96.jpg 300w\" sizes=\"auto, (max-width: 580px) 100vw, 580px\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">To display the results we should be able to run this simple SQL code with RANK() function to pull out top 5 products which sell best.<\/p>\n<pre class=\"brush: sql; highlight: [11]; title: ; notranslate\" title=\"\">\r\n-------------------------------------------------------------------------------------\r\n--                                     QUERY NO 1                                  --\r\n-------------------------------------------------------------------------------------\r\nSELECT * FROM\r\n(\r\n    SELECT COUNT(Sale_ID) as Number_of_Sales, Calendar_Month_Report_Date,\r\n    Product_Category, Distributor, Warehouse,\r\n    RANK() OVER (Partition By Distributor ORDER BY COUNT(Sale_ID) DESC) as Rank_Column\r\n    FROM Sales\r\n    WHERE (Distributor = @Distributor)\r\n    AND (Warehouse = IN(@Warehouse))\r\n    AND (Calendar_Month_Report_Date = @Month)\r\n    GROUP BY Calendar_Month_Report_Date, Product_Category, Distributor, Warehouse\r\n) as a\r\nWHERE Rank_Column ORDER BY Number_of_Sales\r\n\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">To turn it into a SP we prefix the above with the following:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\n--------------------------------------------------------------------------------------\r\n--                                       QUERY NO 2                                 --\r\n--------------------------------------------------------------------------------------\r\n\r\nCREATE PROCEDURE &#x5B;dbo].&#x5B;sp_GetTop5SalesCategories]\r\n    @Distributor varchar (30),\r\n    @Warehouse varchar (25),\r\n    @Month datetime\r\nAS\r\n<\/pre>\n<p>Mind that the parameters have already been defined in the report and assigned to their respective datasets. The problem with the above two solutions is that in the first query the RANK() function is not supported in Report Designer and the second code sample works only with single-valued parameters. As our report has a &#8220;allow multiple values&#8221; enabled on @Warehouse parameter the SP will throw an error unless we &#8220;feed&#8221; it with a nifty function which converts comma-delimited strings into a table.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\n--------------------------------------------------------------------------------------\r\n--                      CREATE FUNCTION dbo.fn_String_To_Table                      --\r\n--------------------------------------------------------------------------------------\r\n\r\nCREATE FUNCTION &#x5B;dbo].&#x5B;fn_String_To_Table] (\r\n     @String VARCHAR(max),         \/* input string *\/\r\n     @Delimeter char(1),           \/* delimiter *\/\r\n     @TrimSpace bit )              \/* kill whitespace? *\/\r\nRETURNS @Table TABLE ( &#x5B;Val] VARCHAR(4000) )\r\nAS\r\n     BEGIN\r\n              DECLARE @Val VARCHAR(4000)\r\n              WHILE LEN(@String) &gt; 0\r\n              BEGIN\r\n                   SET @Val = LEFT(@String,\r\n                       ISNULL(NULLIF(CHARINDEX(@Delimeter, @String) - 1, -1),\r\n                       LEN(@String)))\r\n                   SET @String = SUBSTRING(@String,\r\n                       ISNULL(NULLIF(CHARINDEX(@Delimeter, @String), 0),\r\n                       LEN(@String)) + 1, LEN(@String))\r\n              IF @TrimSpace = 1 Set @Val = LTRIM(RTRIM(@Val))\r\n              INSERT INTO @Table ( &#x5B;Val] )\r\n              VALUES ( @Val )\r\n    END\r\nRETURN\r\nEND\r\n<\/pre>\n<p style=\"text-align: justify;\">The beauty about this function is that it will happily take our multi-valued parameter and return a table which can be queried using &#8220;IN&#8221; statement. It also takes a &#8220;delimiter type&#8221; and &#8220;trim white spaces YES or NO&#8221; inputs, but its core functionality comes from the table output it provides for convenient query execution against its content. Once we replace the highlighted code row from Query No 1 (row 11) with a modified version which calls the function, as per the image below we should be able to execute the stored procedure and run the report as expected, regardless of whether a single or multiple values are selected from @Warehouse parameter.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\n---------------------------------------------------------------------------------------\r\n--                              REPLACEMENT CODE                                     --\r\n---------------------------------------------------------------------------------------\r\nAND (Warehouse = IN(SELECT val FROM dbo.fn_String_to_Table (@Warehouse,',',1)))\r\n--\r\n<\/pre>\n<p>Please also visit the second part to this post which can be found <a href=\"http:\/\/bicortex.com\/ssrs-report-execution-with-multi-valued-parameter-through-stored-procedure-part-2\/\">HERE<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently, I have been racking my brains over a little SSRS inefficiency which hasn&#8217;t become apparent until I tried to use RANK() SQL function in one of the queries. As it turns out Query Designer in Report Designer does not play well with what is easily achievable in Management Studio (or other SQL editor for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32,5,7],"tags":[12,49,47],"class_list":["post-338","post","type-post","status-publish","format-standard","hentry","category-how-tos","category-sql","category-ssrs","tag-code","tag-sql","tag-ssrs"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/comments?post=338"}],"version-history":[{"count":49,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/338\/revisions"}],"predecessor-version":[{"id":342,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/posts\/338\/revisions\/342"}],"wp:attachment":[{"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/media?parent=338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/categories?post=338"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bicortex.com\/bicortex\/wp-json\/wp\/v2\/tags?post=338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}