{"id":16067,"date":"2015-06-22T13:00:00","date_gmt":"2015-06-22T20:00:00","guid":{"rendered":"https:\/\/devwww.3cloudsolutions.com\/post\/6-ways-to-improve-the-speed-of-microsoft-power-pivot-reports-2\/"},"modified":"2023-09-20T09:06:06","modified_gmt":"2023-09-20T16:06:06","slug":"6-ways-to-improve-the-speed-of-microsoft-power-pivot-reports","status":"publish","type":"post","link":"https:\/\/3cloudsolutions.com\/resources\/6-ways-to-improve-the-speed-of-microsoft-power-pivot-reports\/","title":{"rendered":"6 Ways to Improve the Speed of Microsoft Power Pivot Reports"},"content":{"rendered":"<p><img decoding=\"async\" style=\"float: right;\" title=\"improve_pivot_reports.jpg\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/improve_pivot_reports.jpg\" alt=\"improve_pivot_reports.jpg\" width=\"398\" \/><\/p>\n<p>On a recent project, we had a request to build operational reports with a cube. The reports had one measure and a bunch of attributes and slicers. A typical pivot report wouldn\u2019t have thousands of rows, but these did. At first, we pointed Excel to the cube and the report would take 20-30 seconds to refresh. The client wasn\u2019t impressed with the performance so we started thinking of different ways to improve the experience.<\/p>\n<p>In Excel, when you create a pivot table from a SQL Server Analysis Services Tabular model, Excel tries to write its own MDX to return the data. Usually, that MDX is not the optimal MDX but it gets the job done. We found you can\u2019t control Excel but you can control the query that Excel runs. By optimizing the query, you can make Excel generate faster pivot reports.<\/p>\n<p>I used SQL Profiler to trace only the start and end of the queries (turning everything else off). Connected to the AdventureWorks Tabular model, I created a quick pivot table to demo the behavior.<\/p>\n<p><img decoding=\"async\" title=\"bluegranite1.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite1.png\" alt=\"bluegranite1.png\" width=\"1004\" \/><\/p>\n<p>Notice I have one measure with many attributes and a few slicers, nothing crazy. This query is a little slow, but not too bad since the model doesn\u2019t have a ton of data (our client\u2019s data had a billion products).<\/p>\n<p>I turned on SQL Profiler first and selected a few values from the slicer. Then I went back to SQL Profiler to see what the trace picked up. You will see some queries have a longer duration than others. I usually find the query that has the longest duration and start my detective work with that one.<\/p>\n<p><img decoding=\"async\" title=\"bluegranite_2.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_2.png\" alt=\"bluegranite_2.png\" width=\"1004\" \/><\/p>\n<p>Since the MDX here isn\u2019t easy to read, I use a MDX formatter online (<a href=\"http:\/\/mdxquery.com\/mdx\/\" target=\"_blank\" rel=\"nofollow noopener\">http:\/\/mdxquery.com\/mdx\/<\/a>).<\/p>\n<p><img decoding=\"async\" title=\"bluegranite_3.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_3.png\" alt=\"bluegranite_3.png\" width=\"814\" \/><\/p>\n<p>Notice how the MDX generated by Excel writes a query for each slicer you use. This is not optimal! We can do better than that! So, let me show you option two, where we use filters instead of slicers.<\/p>\n<h3>Option 2:<\/h3>\n<p>I removed the slicers and added in filters with the same values selected. I turn the profiler trace back on and refresh the report. This time the duration is only 531 (milliseconds?) It\u2019s faster than the slicer method, but don&#8217;t\u00a0always rely on the duration. Often, other queries running on the same server can cause the duration to be unreliable, so look at the query too! It\u2019s best to benchmark when less people are using the system.<\/p>\n<p><img decoding=\"async\" title=\"bluegranite_4.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_4.png\" alt=\"bluegranite_4.png\" width=\"1004\" \/><\/p>\n<p><img decoding=\"async\" title=\"bluegranite_5.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_5.png\" alt=\"bluegranite_5.png\" width=\"707\" \/><\/p>\n<p>When using filters, MDX only uses one query for all the different fields you are filtering with.<\/p>\n<p>Here, the query only needs to go back to the server once. As you saw above, the more slicers you use, the more times the query has to go back to the server.<\/p>\n<p>So now you see the difference between using slicers and filters, but that still didn\u2019t solve our problem. Our reports were still slow and our clients were still not satisfied. We can\u2019t change the queries that Excel generates, but we can change the query that Excel runs!<\/p>\n<h3>Option 3:<\/h3>\n<p>Creating an MDX set in Excel helps control the MDX query. You can go to Data, Connections, Manage Sets and select New, then Create Set using MDX. Build your MDX query here and click OK to save.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite_6.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_6.png\" alt=\"bluegranite_6.png\" width=\"633\" \/><\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite_7.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_7.png\" alt=\"bluegranite_7.png\" width=\"329\" \/><\/p>\n<p>Notice you lose the ability to filter by <span style=\"color: #000000; background-color: #ffffff;\">columns, <\/span>and you get an extra column added for each measure name. Although this option may generate data faster, it\u2019s not the approach we took because the results were too static. The user wanted something more interactive.<\/p>\n<h3>Option 4:<\/h3>\n<p>For Option 4 and Option 6, I\u2019ll use the same DAX query:<\/p>\n<p><span style=\"color: #339966;\">EVALUATE<\/span><br \/>\n<span style=\"color: #3366ff;\">FILTER<\/span>\u00a0(<br \/>\n<span style=\"color: #3366ff;\">SUMMARIZE<\/span>\u00a0(<br \/>\n&#8216;Internet Sales&#8217;,<br \/>\n&#8216;Internet Sales'[Due Date],<br \/>\n&#8216;Internet Sales'[Ship Date],<br \/>\n&#8216;Internet Sales'[Sales Order Number],<br \/>\n&#8216;Internet Sales'[Sales Order Line Number],<br \/>\n&#8216;Product'[Product Category Name],<br \/>\n&#8216;Product'[Product Id],<br \/>\nProduct[Product Line],<br \/>\n&#8216;Sales Territory'[Sales Territory Region],<br \/>\n<span style=\"color: #ff0000;\">&#8220;Order Line Count&#8221;<\/span>,\u00a0[Internet Order Lines Count]<br \/>\n),<br \/>\n<span style=\"color: #3366ff;\">AND<\/span>\u00a0(<br \/>\n<span style=\"color: #3366ff;\">OR\u00a0<\/span>(<br \/>\nProduct[Product Line]\u00a0=\u00a0&#8220;<span style=\"color: #ff0000;\">R<\/span>&#8220;,<br \/>\nProduct[Product Line]\u00a0=\u00a0&#8220;<span style=\"color: #ff0000;\">M<\/span>&#8221;<br \/>\n),<br \/>\n<span style=\"color: #3366ff;\">OR<\/span>\u00a0(<br \/>\n<span style=\"color: #3366ff;\">OR\u00a0<\/span>(<br \/>\n&#8216;Sales Territory'[Sales Territory Region]\u00a0=\u00a0<span style=\"color: #ff0000;\">&#8220;Northeast&#8221;<\/span>,<br \/>\n&#8216;Sales Territory'[Sales Territory Region]\u00a0=\u00a0<span style=\"color: #ff0000;\">&#8220;Canada&#8221;<\/span><br \/>\n),<br \/>\n&#8216;Sales Territory'[Sales Territory Region]\u00a0=\u00a0<span style=\"color: #ff0000;\">&#8220;Northwest&#8221;<\/span><br \/>\n)<br \/>\n)<br \/>\n)<\/p>\n<p>First, it\u2019s good to write the query to test out the performance in SQL Profiler. It\u2019s best to group the fields that belong to the same table together for better performance. This is true for DAX and MDX.<\/p>\n<p>Notice how all the Internet Sales fields are together followed by the Product fields, etc.<\/p>\n<p>Using profiler, you can see the duration of this query is much lower than that of the MDX queries generated by Excel.<\/p>\n<p><img decoding=\"async\" title=\"bluegranite8.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite8.png\" alt=\"bluegranite8.png\" width=\"1004\" \/><\/p>\n<p>So back to Option 4, using the ODC with DAX query\u2026<\/p>\n<p>First, create a data connection to your cube.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite9.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite9.png\" alt=\"bluegranite9.png\" width=\"551\" \/><\/p>\n<p>Select server and model that your data is in and click Finish.<\/p>\n<p>In the Import Data screen select the Only Create Connection option and click OK.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite_10.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite_10.png\" alt=\"bluegranite_10.png\" width=\"389\" \/><\/p>\n<p>Now go into your \u201cMy Data Sources\u201d folder and look for your connection. This folder is usually under your \u201cMy Documents\u201d folder.<\/p>\n<p>Open your connection with any text editor, here I\u2019m using Notepad. You will see the CommandType line and a CommandText line. Update CommandType from \u201cCube\u201d to \u201cQuery\u201d and paste in the DAX query in the CommandText line. Save the file.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite11.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite11.png\" alt=\"bluegranite11.png\" width=\"815\" \/><\/p>\n<p>Once the file is saved, open Excel and browse to your new connection file under \u201cExisting Connections.\u201d When the connection opens, you have a Usage tab and a Definition tab. In the Definition tab, you will see the Command Text box with your DAX query. This is where you will edit the DAX query from now on. It\u2019s best to use DAX Studio to build the query and paste it into this box when the query is parsed. Before clicking OK, turn on the profiler if you want to see the difference in performance.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite12.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite12.png\" alt=\"bluegranite12.png\" width=\"594\" \/><\/p>\n<p><img decoding=\"async\" title=\"bluegranite__13.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite__13.png\" alt=\"bluegranite__13.png\" width=\"1004\" \/><\/p>\n<p>Notice the profiler duration is much faster than the previous queries. So, what\u2019s the difference? Here we are using DAX to query a tabular model. DAX is a language that\u2019s native to tabular so it naturally runs better. Also, we formatted the DAX query so it runs optimally. You can\u2019t do that with the MDX code that Excel generates. Using DAX in these situations is a big advantage.<\/p>\n<h3>Options 5 and 6:<\/h3>\n<p>You can use SSRS to export to create a pivot in MDX or DAX. When you use MDX, go to SSRS and create the Data Connection to the tabular model and then create the Data Set. Edit the data set and click on the Design Mode button on the upper right of the Query Designer screen. You will now see an MDX query in the window and you can start writing your MDX query here.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite14.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite14.png\" alt=\"bluegranite14.png\" width=\"869\" \/><\/p>\n<p>For Option 6, when you use DAX, you paste the DAX query into the query window of the data set. This is a little different from SQL 2012, where you would use the Designer window.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" title=\"bluegranite15.png\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/bluegranite15.png\" alt=\"bluegranite15.png\" width=\"662\" \/><\/p>\n<p>The only thing to be aware of in options 5 and 6 is the parameters. Each parameter must have a separate data set.<\/p>\n<p>To find the best solution, use SQL Profiler to trace your queries, and see what the report is actually trying to do. Sometimes, two solutions may generate the report at the same speed, and you can use profiler to determine which one is actually more optimal. For our report, we found option four was best.<\/p>\n<p>Don\u2019t settle for slow reports. Try to think of different ways you can improve performance. Figure out what your report is really doing. By understanding what your report is doing, you can find an option that works for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here are 6 ways you can improve the speed of pivot reports.<\/p>\n","protected":false},"author":21,"featured_media":14957,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[260],"tags":[305],"class_list":["post-16067","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-ai","tag-modern-bi","topics-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/16067","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/comments?post=16067"}],"version-history":[{"count":0,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/16067\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media\/14957"}],"wp:attachment":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media?parent=16067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/categories?post=16067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/tags?post=16067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}