Level: Intermediate
This is the final article in my series about DAX as a Query Language. In my first article I introduced DAX as a Query Language and then in my second article I went in deeper to the specifics on how to use some of the more useful DAX query functions. In this final article today I am going to cover a few of the many uses for DAX Queries and also introduce one last DAX function – the ROW function.
Create Summary Tables
As you have may have seen in my previous articles, you can use DAX Queries to create summary tables like the one below.
This may be useful if you want to pre-calculate this summary table and have it as part of your data model (more on that covered in the “Materialisation” section below). If you want to create a summary table for any reason you can do it using DAX Queries.
Create Tables to Use As Filters Inside CALCULATE
Another major use of queries is as a table input inside a CALCULATE function. Because the virtual tables retain lineage to the data model they behave as if they were physically part of the data model. The following measure returns the total sales for all unique combinations of product category and customer gender where the margin to those unique combinations is greater than 45%.
= CALCULATE( [Total Sales], FILTER( SUMMARIZE(Sales, Products[Category], Customers[Gender]), [Total Margin%] > .45 ) )
This measure doesn’t have any real value however it does demonstrate the concept well.
Create Tables to Use in Iterators
You can also create table to use as the table parameter in an iterator like SUMX or AVERAGEX. The following measure returns the straight line average Margin % across all the actual combinations of product category and customer gender.
= AVERAGEX( SUMMARIZE(Sales, Products[Category], Customers[Gender]), [Total Margin%] )
Here is an article I wrote some time ago that explains how to use DAX Query tables instead of a more complex nested SUMX approach. This article will be much more useful with your new understanding of DAX Queries.
To Debug DAX Formulas (the Table Bit)
One thing I really like about DAX Queries is they allow you to “Inspect” tables that are used inside DAX Measures and Calculated Columns. Excel users come from a world where everything thing they do – every formula and every number – is materialised in front of their eyes in the spreadsheet. When you begin to write more complex DAX Measures or Calculated Columns, the formulas will often be in the following format:
Your Measure = CALCULATE([Some Base Measure], <A complex table function used to filter the data model>)
The issue of course is you can’t actually “see” the result of “the complex table function used to filter the data model”. If you can’t see it, it can be hard to conceptualise and also hard to debug the measure particularly if it gives an answer different to your expectations. This is where the concept of materialising tables comes in. A great way to move forward when you are stuck is to write a DAX Query to return just the table portion of your measure first. Once you have that working correctly you can insert the table portion into the measure.
Materialise Tables Into Excel
It is possible to materialise a DAX Query into Excel by using a trick that I learnt from The Italians. For this next example I have an Excel Workbook that contains an Adventure Works Power Pivot data model. The first thing to do is insert a Table* (not a Pivot Table) connected to the data model. To do this, go to a blank area of the spreadsheet (shown as 1 below) and then select Data\Existing Connections\Tables, then select any table (I have selected Products shown as 5 below) and then click Open. *At this point you can select any table as it is going to change shortly anyway.
You will be given an option to insert a Pivot Table however you also have an option to insert a Table as shown below. Go ahead and click OK to insert this table.
Now that you have a table inserted into a spreadsheet from the data model, it is possible to change what is returned in this table. To change what is returned in the table, right click anywhere inside the table (shown as 1 below) and then select Table\Edit DAX.
Now you have the opportunity to change this table from being a “table” to being a DAX Query as shown below.
I have now taken a DAX Query I wrote earlier and pasted this into the expression box (shown below) to return a summary table into the spreadsheet.
You can manually refresh this table by right clicking in the table and clicking “Refresh”. Unfortunately there is no easy way to pass filters to this type of table in Excel although I believe it can be done with VBA. That may be a topic for another day.
Materialise Tables Into Power BI
You can also materialise DAX Query Tables into Power BI using the “New Table” function. This can be very useful to help you debug the formulas you are writing (as I covered in this article). The only thing different to note here is that you need to omit the EVALUATE statement inside Power BI New Tables. The example below materialises the table (shown as 5) from the (formula shown as 4).
Check the Performance of your Measures using ROW
DAX Studio has a really cool feature that allows you to understand how Power Pivot is completing its task of calculating a formula under the hood. To demonstrate the point I need to introduce an new DAX Query function called ROW.
You can see the syntax for ROW below from the DAX Studio Intellisense.
ROW returns a single row table. Because a DAX Query can ONLY return a table, it follows that if you want to see the result of a measure inside DAX Studio then you must first convert the scalar result of the measure into a table – this is exactly what ROW does.
Below is a simple example where I return the value of the [Total Sales] measure as a new table.
My ROW function (shown as 1 above) returns a single row table with 1 column (called “my result”) and the row has the value 29.3m (shown as 2 above) which is the total for the entire database. Note the Server Timings button shown in 3 above. If you click this button it will enable the capability for DAX Studio to keep track of how long your query takes to execute and also how the query is being executed.
With the server timings enabled, a new tab will appear in DAX Studio (shown as 1 below).
After executing the query, the server timings tab will show the total time taken to execute the query in milliseconds (shown as 2), how much time each engine in Power Pivot completed the tasks (Formula Engine and Storage Engine shown in 3), and if the Storage Engine Cache was used (shown in 4).
SE or FE?
This is a huge topic in its own right and way beyond the scope of this article. What I will say here is that in general you want to leverage the Storage Engine in preference to the Formula Engine. Take a look at some of the key points below.
Storage Engine | Formula Engine |
---|---|
Really Fast | Really Smart |
Can retrieve records without decompressing in the right conditions | Iterates in a row context and hence it can be slow |
Multi threaded | Single threaded |
Cached | Not cached |
I will cover the Storage Engine and Formula Engine in another post sometime.
CROSSJOIN Revisited using ROW
Earlier in this series I warned that CROSSJOIN can return very large tables, but this is only really an issue if you try to materialise the tables. If I write the following query in DAX Studio, it takes forever to execute and I ended up canceling the query (remember there are more than 7 million rows in this table).
The issue is not the creation of the above table in Power Pivot – the issue is the materialisation of this table in DAX Studio. Consider the following query that uses ROW to return a single row table containing the count of the rows in the CROSSJOIN query.
The above query executed in 400 milliseconds on my PC confirming that Power Pivot can create these large tables really quickly. If you want to materialise the results, well that is a completely different thing.
You probably heard it a million times before, but I find your website to be the most complete getting started resource ever. Been using DAX on and off for years, but your site is the one that covers everything that is somehow missing in all the rest of the resources, including the Italians, Microsoft online reference, etc. Thank you for your effort!!!
Thanks Dmytro. I appreciate you taking the time to say so. ?
Now I´m getting an error message when trying to export to Excel (linked) as well.
“Error sending results to Excel (El parámetro no es correcto. (Excepción de HRRESULT: 0x80070057 (E_INVALIDARG)))
I would appreciate any help to solve this issue. Thanks
It works however if the export option is Excel (linked)
Hi,
I can not export to an Excel static table, I get this error message:
Query(2,26) The syntax for ‘[Category] is incorrect. (EVALUATE FILTER(Products, Products[Category]=”Bikes”))
The code used is:
EVALUATE
FILTER(Products, Products[Category]=”Bikes”)
Your query looks fine. Must be a bug. I suggest you seek help at https://daxstudio.codeplex.com
Ok, thanks.
Matt, thanks! But the link for the Excel AdventureWorks points to a PBIX file and not an Excel spreadsheet
Thanks for the heads up. I have fixed this now
Very Good series (3) of articles about Dax Queries.
Thanks a lot Mr. Matt
Very Good series (3) of articles about Dax Queries.
Thanks a lot Matt
Tried to download the Excel version of AdventureWorks but it prompts for a password.
Sorry, it was a bad link. Try again now
Thanks. Great post. I am looking forward to putting this into practice!
nice! deepened my understanding of ROW:)
With DAX Studio, you can also send a filtered table directly to Excel. (1)Have an Excel worksheet open (2) Open DAX Studio (3) On the Home Tab, Query Group, change Output to Excel Static. (4) Write a Query, such as: EVALUATE
FILTER(Products,PRODUCTS[ProductKey] <400 && Products[Color]="Black")
Excellent blog, Matt! Very useful information and as usual you made it simple to understand.
Thanks