Using the DAX Calculate and Values Functions

In the first two articles in this series on creating DAX formulae, Andy Brown of Wise Owl Training showed how to create calculated columns and measures. In this third article, he turns his attention to two of the most important DAX functions (CALCULATE and VALUES), showing how and when to use them. If DAX knowledge can be compared to a heavily fortified castle, the CALCULATE function is the drawbridge giving access to it.

The series so far:

  1. Creating Calculated Columns Using DAX
  2. Creating Measures Using DAX
  3. Using the DAX Calculate and Values Functions
  4. Using the FILTER Function in DAX
  5. Cracking DAX – the EARLIER and RANKX Functions

If you should ever start reading a book on DAX, you will quickly reach a chapter on the CALCULATE function. The book will tell you that the CALCULATE function is at the heart of everything that you do in DAX and is the key to understanding the language. A delegate on one of my courses adopted the policy of starting every formula with =CALCULATE, and it’s not such a bad approach! This article explains how to use the CALCULATE function and also how to use the (almost) equally important VALUES function.

The Example Database for this Article

This article uses the same simple database as its two predecessors. This database shows sales of three toys for different cities around the world:

You can import this data into your own Power BI data model by first downloading this Excel workbook, or by running this SQL script in SQL Server Management Studio.

As for the previous articles in this series, everything I describe below will work just as well in Power BI, PowerPivot or Analysis Services (Tabular Model), each of which Wise Owl train.

The CALCULATE Function

To understand the CALCULATE function, you must understand filter context, so that’s where I’ll begin for this article.

Filter Context Explained Using an Excel Pivot Table

Suppose you have the following pivot table in Excel, showing the number of sales for each country, city, and product in the database. The figure selected shows that there were three sales of Timmy Tortoise products in London (UK):

The filter context for the shaded cell containing the number 3 is therefore as follows:

Country dimension: UK

City dimension: London

Product dimension: Timmy Tortoise

If you were to double-click on this cell in Excel, you would see the underlying rows:

These are the three sales which took place for this product in this country and city.

Now suppose that you change your pivot table to show the number of sales as a percentage of the total for each column. This would give:

The figure for Timmy Tortoise for London is 75%, which is:

This gives 75% because this is the result you get when you divide 3 (the number of sales in London for Timmy Tortoise) by 4 (the number of sales in London for all products).

Note that I’ll often refer in this article to the numerator and denominator. In any fraction A / B, the numerator is A and the denominator is B (but you knew that from school maths, didn’t you?).

Removing One Constraint Using CALCULATE and ALL

Now suppose that you want to recreate this pivot table using a matrix and slicer in Power BI:

The figures are exactly the same, and for Timmy Tortoise for London you’ll see 75% because this is the ratio between the number of sales for this product and city (3) against the number of sales for all products and this city (4).

To solve this problem, you’ll use the CALCULATE function which is the answer to most questions in DAX. The syntax of the function is as follows:

The measure you should create (and show) is this:

I’ve put my measure in a separate table – if you’re not sure how to create this table or how to create measures, see the previous article in this series. What the measure does is to calculate the numerator (the number of sales for the current product and city) and divide this by the denominator (the number of sales for the current city only, with any product constraint removed). Here’s what this calculates:

If you display row and column totals for this measure, you get this:

The figures in the bottom row make sense: total sales for London for all products divided by total sales for London for all products will always give 100%!

Removing Multiple Constraints Using ALL

Suppose that you now want to display the number of sales as a percentage of the total for all cities and for all products, to get this:

In this case, the numerator is the total number of sales in the UK in London for Timmy Tortoise, and the denominator is the total number of sales in the UK; the other two constraints have been removed from the denominator. Here is a DAX measure to calculate these figures:

You can use the ALL function as many times as you like – each time it will remove one dimension from the filter context.

Using ALLEXCEPT to Remove All but One Constraint

An alternative solution to the above problem would be to calculate this ratio:

Here’s a quick comparison of the two approaches:

Here’s a measure which would show each product/city’s contribution to the grand total for each country:

It’s up to you whether you think it’s more elegant to remove constraints from the filter context individually using ALL, or to remove all constraints apart from one using ALLEXCEPT.

Replacing Filter Context Using CALCULATE

The previous examples have all involved removing the filter context in whole or in part. What if you wanted to change it to show the ratio for each matrix cell between the number of sales for that cell and the number of sales for the same filter context, but for the product Timmy Tortoise? That is, you want to calculate:

For this example, it’s inevitable that the figures for Timmy Tortoise should be 100%, because for each cell in this row you’re dividing a figure by itself. The matrix above shows that sales of Olly Owl were only a third of those for Timmy Tortoise in London but were twice those for Timmy Tortoise in Manchester.

A formula that you could use might be:

What this does is to calculate the number of sales for a particular country, city and product, and divide this by the number of sales for the same country and city, but for Timmy Tortoise. The extra filter you add in the CALCULATE formula doesn’t build on the filter context for the product, but instead replaces it.

Using the ALLSELECTED Function as Opposed to ALL

Sometimes you’ll want to reference just the selected items in a dimension, in a slicer, for example, rather than include all of the items in your formula. Here’s an example of a matrix where you might want to do this:

The measure shown initially is as follows:

The figures don’t add up to 100% because for each country the statistic shown equals:

In this example the USA is included in the denominator but not in the numerator. To get the statistic to work, you need to reference only the selected countries in the denominator:

This gives the required 100% total, regardless of the combination of countries you select in the slicer:

Context Transition Using the CALCULATE Function

Before moving on from the CALCULATE function, it has one more string to its bow. Consider the following two formulae:

If you’ve been following up to now, you’ll realise that these two formulae must give the same result under all circumstances:

The first formula gives the total sales value for the current filter context;

The second formula gives the total sales value for the current filter context, with no extra modifications to it.

However … what happens if there isn’t a filter context to begin with? In this case the second formula will create a filter context, and hence return a different answer than the first. How can you not have a filter context? By creating a calculated column in a table:

The first formula gives the same result for each row. Because calculated columns don’t have a filter context by default, the formula sums sales over all of the rows in the sales table, giving the same answer (238.32) for each.

Remember that the second formula is as follows:

The CALCULATE function doesn’t just allow you to change the filter context, it can create it, too. For each country, this creates a filter context, limiting the rows in the sales table to those for the country in question, and hence giving a different answer for each row of the above table. The process of changing row context into filter context in this way is called context transition.

The VALUES Function

Learning the CALCULATE function is key to understanding how to create measures in DAX, but the VALUES function runs it a close second. The rest of this article shows what this function does, and how to use it to create a range of effects in your Power BI reports.

What the VALUES function returns

The VALUES function returns the table of data for the current filter context. To explain what this sentence means, here’s an example. Suppose you create this table in a Power BI report:

Note for this example that I’ve used a filter (not shown here) on the report to avoid showing any blank countries). The Number of cities column shows the number of cities for each country, using the following measure:

If you could look at the filter context, this is what you would see:

The VALUES function allows you to return a table containing one or more of the columns in the current filter context’s underlying table. For example, you could create this measure:

If you display this measure in your Power BI report, you’ll get this error message:

The problem is that you’re trying to display a column of values in a single cell. This would work for Brazil and China, each of which only has one city, but wouldn’t work for the other three countries.

What you could do, however, is to test whether there is only one city for a country, and in this event show its name; otherwise, you could show a message saying that there are multiple cities. Here’s a measure to do this:

Displaying this measure in our report would give:

For the total row there are lots of cities in the current filter context, so naturally you get the More than one city message.

The above example shows two important features of the VALUES function. The first is that it returns a table of data. In the measure above, the COUNTROWS function expects to receive a table:

Fortunately, that’s what’s supplied:

The VALUES function in this case returns a single-column table which looks like this for each of the 5 countries:

The second important point to understand about the VALUES function is that you can’t put a table into a cell without performing some sort of aggregation on it first, since a table can potentially contain multiple values. What this means is that the selected part of the measure below shouldn’t work:

This is because the VALUES function returns a column of data, and even though you know that there is only one row in this column, and hence only one value, you would normally still need to apply some aggregation function (e.g., MAX, MIN, SUM) to the data.

Happily, there is one exception to this rule. If a call to the VALUES function returns a table with one column and one row, you can automatically treat this as a single scalar value without any additional work. This is why this measure works!

The HASONEVALUE Function and Other Alternatives

Checking whether the filter context only contains one value for a particular column is a common thing to do. It’s so common, in fact, that DAX has a dedicated function called HASONEVALUE to do this.

You could rewrite the measure like this:

Another solution would be to count how many distinct city names there are in the current filter context:

These three methods, using VALUES, HASONEVALUE or DISTINCTCOUNT, are interchangeable, and I don’t think there’s any clear reason to favour one over another.

Using CONCATENATEX to List Out Multiple Values

For this example, you might want to list out the names of the cities for each country. You can do this using the CONCATENATEX function, which has this syntax:

The arguments to this function are thus:

  • The table containing the values you want to concatenate
  • The column in this table containing the values to concatenate. You have to specify this even if the table only has one column, even though in this case it is blindingly obvious that this is the one you should choose!
  • The text you want to use as glue to join the column values together
  • Which column you want to order by. Again you need to specify this, even if you know you’re working with a single-column table.
  • The order-by direction, ascending or descending

For this example, you could modify the measure to read like this:

This more or less works, since it gives this table:

The only remaining problem is that the total row now looks odd. Technically it is correct, because, for this row, the filter context contains all of the cities for all countries. A better solution would be to check whether there is more than one country in the filter context:

This is what you should now see when using this measure in the table:

All of this illustrates an important point about DAX measures. You can create a measure which gives sensible results for one particular visual, but can you be sure that it will give sensible results in another? Or in a totals row? Or a totals column, or grand total? You’ll often be faced with a trade-off in DAX between checking that a measure works under all possible circumstances and keeping things simple.

Modifying the Filter Context Using VALUES

Suppose that you now want to display the total sales for each country apart from the UK. The obvious way to do this is to sum total sales, but using the CALCULATE function to amend the filter context to omit the UK:

This suffers from one major problem – it doesn’t work! Displaying this measure in a table would show the same value for every country:

To understand why this measure is showing 166.57 for every country, remember what I said earlier in this article: when you apply a filter, it replaces the current filter context for a dimension. For this example you’re adding this filter to the CALCULATE function:

What this does is to lose any existing filter by the country dimension and replace it with one where the country is UK. Here’s what’s going on for each country:

The total sales value for all of the countries apart from the UK is 166.57, so that’s what gets displayed in every row. What you want to do is to keep the existing filter context constraints for the country dimension, but then add to them. One way to do this is to use the VALUES function, making the new measure read like this:

This would give the following results:

If you’re wondering why there is a discrepancy between the 166.57 shown in the first table and the 150.37 shown in the second, it’s explained by the fact that I had filtered the table to remove any sales taking place with no assigned country. If you remove this filter you get:

Add these 16.20 of sales in as above and you would get the required figure.

Using Disconnected Slicers to Make Reports Dynamic

This is a clever idea, which allows you to make reports dynamic. The idea is to create a slicer which allows you to choose which measure you want to show. In the example below, someone has chosen to show the average price of sales:

To make this work, first create a table to hold the statistics that you might want to report:

However, don’t link this table to any other. That’s why this technique is often called a “disconnected slicer”. Now create a slicer based upon this table:

The idea is that when you select a statistic in the slicer, the bottom table will show its value. All that you now need to do is to create and show a measure which will yield:

  • The average price of sales if someone selects the first measure;
  • The number of sales records if someone selects the second measure;
  • The total value of sales if someone selects the third measure; or
  • A blank if someone selects more than one statistic at a time or doesn’t select one at all.

Here’s what this measure might look like!

One final question: is it possible to display different statistics using different number formatting? I can’t think of any way to do this except to use the FORMAT function:

The problem with this is that the FORMAT function turns numbers into text, although because it does so only after the calculation is complete for each filter context, this shouldn’t cause too much of a problem. Here’s what you’d see for the number of sales for the above measure, for example:

And just in case you’re wondering, I can’t think of any way to change the column title dynamically!

Dynamic Titles

There’s one more thing to demonstrate with the creative use of the VALUES function: how to show the choices made in a slicer. For the report page below, you’d like a card visual (shown selected) to display a measure listing the countries chosen:

Here are some examples of what the card should display:

  • For the choices shown above, it should read “Brazil, China, India, UK”
  • If a user doesn’t select a country, it should read “All countries”
  • If a user picks a single country, it should give the country’s name

If you’ve been following the article so far, there’s nothing new with this – it just combines lots of the ideas you’ve already seen. Here’s a measure which would fit the bill:

Here’s what this would show if you have one country selected assuming that you attach the measure to your card:

If you have multiple countries selected, you’ll see this:

And finally, if you have no countries selected, you’ll see this:

If you want to be even fancier you could use a quick measure to display only the first 3 countries in any list which I covered in the previous article in this series.

Conclusion

This article has shown how you can use two of the most important DAX functions: CALCULATE and VALUES. The article began by showing how you can use the CALCULATE function to amend the default filter context, mainly in order to create ratios. I then showed how you can use functions like VALUES, HASONEVALUE and ISFILTERED to produce a variety of clever effects in DAX. The next article in this series will look at the FILTER and EARLIER functions. You should make sure that you understand clearly how you can use the CALCULATE function to change filter context before progressing, since the DAX formulae won’t get any easier!