Professional Documents
Culture Documents
24 Riverhaven Drive,
Wade Heads,
Whangaparaoa 0932
New Zealand
Copyright © 2021 by RADACAD, Reza Rad
All rights reserved. No part of the contents of this book may be reproduced
or transmitted in any form or by any means without the written permission
of the publisher.
Agenda
AGENDA
PART 1: FUNDAMENTALS
CHAPTER 8: SUM VS. SUMX; WHAT IS THE DIFFERENCE BETWEEN THE TWO
DAX FUNCTIONS IN POWER BI?
CHAPTER 9: CALCULATE TOTALS IN POWER BI: USING ITERATORS IN DAX
CHAPTER 10: SHOWING RANKING IN A VISUAL IN POWER BI USING RANKX
DAX FUNCTION
CHAPTER 11: GENERATING ROW NUMBER IN POWER BI VISUALIZATION USING
DAX
PART 3: FILTER
CHAPTER 12: FILTER FUNCTION IN DAX AND POWER BI: APPLY CUSTOM
FILTER TO CALCULATIONS
CHAPTER 13: NOW YOU SEE ME! USE CASES OF THE ALL DAX FUNCTION IN
POWER BI
CHAPTER 14: HOW TO USE THE ALL IN A DAX EXPRESSION IN POWER BI
CHAPTER 15: REMOVING THE TOTAL VALUE FOR A COLUMN IN THE TABLE
VISUAL OF POWER BI USING ISFILTERED
CHAPTER 16: FIND THE DATA VALUE USING LOOKUPVALUE DAX FUNCTION
IN POWER BI; SIMPLE AND USEFUL
CHAPTER 17: THE IF AND FILTER ARE DIFFERENT! BE CAREFUL (DAX)
CHAPTER 18: OVERWRITE INTERACTION OF POWER BI WITH DAX
CHAPTER 19: GET A FIELD VALUE FROM A RELATED TABLE IN POWER BI: DAX
RELATED FUNCTION EXPLAINED
CHAPTER 20: POWER BI DAX RELATEDTABLE FUNCTION: GET THE SUBTABLE
RELATED TO THE CURRENT ROW
CHAPTER 21: USERELATIONSHIP OR ROLE-PLAYING DIMENSION; DEALING
WITH INACTIVE RELATIONSHIPS IN POWER BI
CHAPTER 22: DAX CROSSFILTER FUNCTION IN POWER BI: WRITE THE
FORMULA BOTH-DIRECTIONAL, BUT KEEP THE RELATIONSHIP SINGLE-
DIRECTIONAL
Download the files and codes for this book from here
[https://radacad.com/books/files/PowerBIDaxRockStar.zip]
Part 1: Fundamentals
Chapter 1: Basics of DAX
Expression in Power BI
There are a lot of resources about how to use each function. However, you
always need to start with learning how the expression language works itself.
You need to know how to reference columns and tables. What operators can
be used, and what are the elementary basics of writing a DAX expression. In
this chapter, you will learn about that.
What is DAX?
DAX is an acronym for Data Analysis Expression language. This language
is used in Microsoft’s data analysis products: Power BI, Excel Power Pivot,
SQL Server Analysis Services Tabular Edition, and Azure Analysis
Services. The language is a combined version of a bit of T-SQL, Excel
formula, and C#.
DAX is an expression language, which means most of it is written after an
equal sign (=) as a formula. There are hundreds of functions that can be used
for doing different things. However, the expression language itself has some
fundamentals.
DAX as a calculation
DAX is most commonly written as a calculation. The calculation comes in
three forms below;
Calculated Column
Calculated Table
Measure
DAX as a calculation
This chapter will be very long if we want to discuss the difference between
these three types of calculations. You will learn about the different types of
calculations in the later chapters of this part.
all the examples above show an expression after the equal sign. The
expression can be a literal value such as a number or a text. If it is text, then
it is wrapped inside double quotes (“). You can use operators such as * or / in
the expression too. And You can use Functions (Such as Sum and Calculate
in the above samples), and you can refer to other columns, tables, and
measures.
Referencing objects
An essential part of a DAX expression is when you reference other objects.
For example, you can create a column, which can be precisely equal to
another column;
referencing a column in a DAX expression
To reference a column, you need the column name inside [ and ]. The table
name can appear before that. If you are using that column inside the same
table, you can skip the table name and just have it as below too;
Column = [FullName]
The above works as long as the Column I have created exists in the same
table that the FullName column exists. So as a best practice, it is advised to
have the table name before the column name. The table name comes just as-
is before the column name like below;
Column = DimCustomer[FullName]
If the table name has some special characters (and space is also considered
as a special character), then the table names come inside single quotes (‘)
like below;
referencing table names with special characters in DAX
Operators
There are many operators you can use within DAX expressions. Operators
are in the below categories;
Operator samples operation
category Here
Arithmetic + adds two numbers
are
* multiplies two numbers
some
/ divides
sampl
Comparison = equal
es of
<> not equal
DAX
=> greater than or equal
expre
Text concatenation & concatenate texts
ssions
Logical && AND with
|| OR opera
IN if the value is IN the list
tors;
=1+10
=[ColumnX]+[ColumnY]
=”Reza”&” “&”Rad
=If( TableX[ColumnY]>=12, “XYZ”, “WYQ”)
=”Reza” IN {“Reza”,”Leila”}
= TableX[ColumnY]>=10 && TableX[ColumnZ]<20
Variables
You can define a variable inside a DAX expression. The variable can store a
single value or a table and can be re-used throughout that DAX expression.
When using variables in places that output is needed, a return keyword is
also mandatory. Here is an example of using variables;
Column 2 =
var HireYears=DATEDIFF('Dim Employee'[HireDate],TODAY(),YEAR)
return
if(HireYears>20,"hired for 20+ years","hired for less than 20 years")
As you can see, the variable HireYears is defined once and re-used in the IF
statement.
Comments
In every language, it is helpful to be able to write some none-executables
lines within the code. This will enable the developer to put some comments
for future reference. In DAX, you can write commentary using double
forward slash characters in one line (//)
writing a single-line comment in DAX
Functions
And finally, the heart of DAX expression is filled with the usage of
functions. In DAX, there are functions for many different operations. A
function can be as simple as concatenating two text values. There are
functions to deal with date-based calculations, such as calculating the same
period last year. Each function gets input parameters and has an output.
Functions can be used inside each other. Here are some expressions with
functions;
Sum(TableX[ColumnY])
SumX ( All (TableX), [ColumnY]+[ColumnX] )
Functions have a wide variety, and usually, the intellisense (The pop-up
expression help screen when writing DAX) has good information about what
parameters the function needs and the generated output.
Functions can be different, but one primary way to separated them is tabular
Vs. Scalar functions (although there are functions that are neither tabular nor
scalar). You will learn more about it in another chapter in this part.
Functions are also categorized based on the work they do. here are some of
the categories;
Date and time functions
Filter functions
Information functions
Parent and child functions
Time intelligence functions
table manipulation functions
logical functions
text functions
relationship functions
…
Summary
DAX as an expression language is used to create calculations in Power BI,
Excel Power Pivot, and Analysis Services. There are basics on how to
reference columns and tables. Some operators can be used in this expression
language. You can define variables for re-using part of the expression, and
you can write comments in the code. However, the heart of the DAX
expression is when you use functions, and that is where most of your time
will be spent when learning DAX.
Chapter 2: M or DAX? That is the
Question!
What is M?
M is the scripting language behind the scene for Power Query. M is the
informal name of this language. The formal name is Power Query Formula
Language! This is a long name, and even Microsoft refers to it as M. M
stands for many things, but one of its most common words is Mashup. This
means this language is capable of data mashup and transformation. M is a
functional language. The structure of the M script can be similar to this:
M is a step-by-step language structure. Usually (Not always), every line in
the M script is a data transformation step. And the step after that will use
the result of the previous step. It is usually easy to follow the structure of
the M language for a programmer. Because it is understandable with
programming blocks of Let and In and some other programming language
features alike.
What is DAX?
DAX is Data Analysis eXpression Language. This is the common language
between SQL Server Analysis Services Tabular, Power BI, and Power Pivot
in Excel. DAX is an expression language, and unlike M, it is very similar to
Excel functions. DAX has many functions in common with Excel.
However, DAX is much more potent than the Excel formula in many ways.
Here is an example DAX expression:
DAX calculations are built in a way that makes sense mainly for Excel
users. Usually, Excel users are very comfortable with this language.
Everything goes through functions. DAX doesn’t have programming blocks
in it and combines function uses, filters, and expressions.
Example Usage of M
M can be used in many data transformation scenarios. For example, it can
be used to Pivot or Unpivot Data, To Group[https://radacad.com/grouping-
in-power-query-getting-the-last-item-in-each-group] it based on some
columns. Here is how a Pivot/Unpivot[https://radacad.com/pivot-and-
unpivot-with-power-bi] can work in Power Query;
Role-Playing Dimension
The first functionality that appears in mind when we talk about Calculated
Tables is creating role-playing dimensions. Role-playing dimensions are
dimensions with the same structure and data rows that play different roles in
our data model. For example, the Date Dimension is a generic dimension.
However, you might have more than one date column in a sales transaction
table to relate with the date dimension. In the example below, we have three
date fields in the FactInternetSales table: Order Date, Ship Date, and Due
Date.
The calculated table will be created in memory and allows you to write the
definition of the table
The language for the table definition is DAX. For now, let’s keep it simple
to see how it works in action. We want an exact copy of the DimDate table
here. So you can use the ALL function in DAX as below:
Ship Date = ALL(DimDate)
As soon as you type the expression above and press Enter, You’ll see the
result underneath it as data rows and a list of columns in the Fields pane.
You’ve created a role dimension as simple as that. Now you can set up the
relationship;
I’ve also created a Due Date dimension for the relationship above and
renamed the original DimDate to Order Date.
As you can see, this is much more efficient in terms of reducing the refresh
time. However, the memory consumption would be the same in both
methods.
The date dimension was a small table. You might need role-playing for big
data tables. Calculated tables will save you a lot of time in refreshing data
in such cases.
Here are details about parameters I passed in the expression above to the
Summarize function:
First parameter: Source Table. FactInternetSales is the source
table that I want the group by (summarize) operation to be
applied on it.
Second Parameter: Group by Column. CustomerKey in the
FactInternetSales table is the column that I want to use as the
key for grouping.
Third parameter: Output Column Name. I named the output
calculated column name as Total Sales.
Forth parameter: Output Column Calculation. Here I write the
calculation for the output column, which is simply the sum of
the Total Sales Column.
So, as a result, I will have a table with CustomerKey and Total Sales.
TOPN
Now that we have a list of customers with their total sales, it is easy to get
top 100 customers. I can use a TOPN function like this to create another
calculated table (I could do this example with only one calculated table
instead of two, but I only did it with two tables to help you understand the
logic better);
Top 10 Customers = TOPN(100,'Customer Sales','Customer Sales'[Total Sales],DESC)
Limitations
The calculated table's very first limitation is memory. This limitation is also
an advantage, on the other hand, because the in-memory structure makes
these calculations fast.
The other limitation which I like to mention at this stage is: Not Inheriting
Formatting.
By not inheriting formatting, I mean the calculated table doesn’t inherit
format from the source table. In some complex scenarios where the
calculation comes from many tables, that might not be necessary. But for
our simple role-playing example above; If my original date dimension has
some formatting configuration. Such as setting DateKey to a “Do Not
Summarize” or some other configuration, then I would like to see the same
in the calculated table fetched out of this.
The formatting applied on the calculated table columns also will be
overwritten after each change in the DAX expression.
Chapter 4: Measure vs. Calculated
Column: The Mysterious
Question? Not!
Despite all articles, blog posts, and videos on DAX Measures and
Calculated columns, I still hear that people ask what the difference between
Measure and Calculated Column is? What situation should we use each of
these? On the other hand, what is the difference between creating a column
here or in Power Query? The chapter “M or DAX that is the question” in
this book explained situations that you need to use Power Query or DAX.
In this chapter, I will explain the difference between DAX
Calculated Column and Measure.
Read this chapter If You have any of
Questions Below
This chapter is written for you; if you have any of the below questions;
What is a Calculated Column?
What is Measure?
When should I write a calculated column or measure?
What is their difference in Performance?
What are operations that I cannot do with these?
And many other questions about the difference between these
two types of calculations in DAX.
What is a Calculated Column?
A calculated column is a column like any other column created in the table.
However, the result of a calculated column is coming from calculating an
expression (DAX). Usually, the calculated column leverages a DAX
expression that applies to every row in the dataset, and the result will be
stored in the new column.
Example: Profit as a calculated column
Consider a table that has sales and costs information. Calculating Profit in
such a table would be simply deducting costs from sales for every row. So
this basically would be a calculated column.
Expression:
Profit = FactInternetSales[SalesAmount] - FactInternetSales[TotalProductCost]
The measure calculation only shows me the sum of sales for 2007, which is
$9.79M.
Filter Context
Measure evaluates on the fly. If there is a slicer value for 2007, the
calculation will be done on the subset of data for 2007. If there is a table in
visualization somewhere that slices and dices the data by Education
category, the measure will take that into account. We can then say this;
The calculated column is what you need if the calculation is row by row
(example: Profit = Sales – Cost, or Full name = First Name & ” ” & Last
Name).
If the calculation is an aggregation or it is going to be affected by filter
criteria in the report (example: Sum of Sales = Sum(Sales), or Sales Year to
Date = TotalYTD(….)), then Measure is your friend.
Let’s go through some examples;
Example 1: Calculating the age of customers
The age of customers does not change based on filters! It is only dependent
on one thing; the birthdate of the customer. In the customer table, you
usually have the birthdate as a field. So this calculation can be simply a
calculated column, which evaluates row by row for every customer.
Example 2: Calculating Sales Year to Date
The year-to-date calculation depends on the filter criteria in the report, and
also it is an aggregation. It becomes very complicated to calculate year to
date for all variations of fields (per day, per month, per customer, per
product, etc.). So this needs to be a Measure.
every time you put this measure into a report, it calculates based on the
filter criteria of the report;
Calculated Column or Power Query?
When it comes to calculating row by row, then Power Query is a better
option in the majority of the cases. You’ve learned in the previous chapters
about M or DAX and what scenarios you need to use each. Power Query
can implement calculated Columns (in the majority of the cases).
Scalar Functions
Scalar function in a function that returns one single value. This value can be
of any data type; Date, Numeric, Text, etc. But it is always one single value.
One of the most basic and straightforward functions in this category is
SUM.
Consider the measure below:
Sales = Sum(FactInternetSales[SalesAmount])
This calculation will return one single value. It depends on where you use it
in the report actually, but here you can see that it returns the total sales:
SUM function returns one single value as a result, based on the filter applied.
We have many Scalar functions in DAX. Here are a few examples:
SUM/Average/Min/Max
SUMX/MinX/MaxX/AverageX/CountX
LastDate/FirstDate
Calculate
Related
…
You can use these functions directly in a Measure or Calculated Column.
The result of a Measure or Calculated Column should be one single value.
If these functions are used in a Measure, you will see the measure's value in
the visual. If they are used in a calculated column, you will see the values in
every row of the table;
The calculation above returns one single value per row in the Customer table.
Tabular Functions
Some functions return a table as the output, not a single value, a whole
table. The table can have multiple columns, or just a single column depends
on the function used. But it would be a table structure with multiple values
in it.
One of the most simple tabular functions is ALL. The All is a function that
returns the entire table without any filters applied (if just the table passed as
the input with no changes).
Copy of Customer Table = ALL(DimCustomer)
If you do this action, you will get the error saying: “the expression specified
in the query is not a valid table expression.”
That is because the Calculated table expects a table expression, which only
comes from tabular functions.
Tabular Functions to Return the output of a
Measure or Calculated Column
Another miss-use of the functions is when you use a tabular function
directly in a measure or calculated column to return the output.
Tabular function cannot be used to generate the main output of a measure or calculated column.
If you do this action, you will get the error “The expression refers
to multiple columns. Multiple columns cannot be converted to a scalar
value.”
This is because the function returns a table, and the measure expects a
single value.
The expression below is written as a measure, yet, you can see that I have
used the SamePeriodLastYear in it, a tabular function.
Tabular function can be used inside a scalar function as a table expression parameter.
As you can see in the above screenshot, the result of the
SamePeriodLastYear (which is a tabular function) is used as the 2nd
parameter of the Calculate (which is a scalar function). And because the
measure's main output comes from the Calculate function, it works
perfectly fine.
Using Scalar Functions in Calculated Table
You can use the same approach and cascade scalar functions inside a tabular
function.
GroupBy - with aggregation = GROUPBY(
DimCustomer,DimCustomer[EnglishEducation],
"Row Count",
COUNTX(
CURRENTGROUP(),
DimCustomer[CustomerKey]
)
)
The expression above uses the CountX, a scalar function, inside the
GroupBy as a parameter. And the GroupBy is a tabular function. If you look
more in detail, you will also see that the CurrentGroup function is another
tabular function nested inside the CountX.
Sometimes input parameters and output type are understandable from the function definition in the
Editor.
The Docs would also guide you to the same thing;
https://docs.microsoft.com/en-us/dax/sumx-function-dax[https://docs.microsoft.com/en-
us/dax/sumx-function-dax]
Not all functions have a perfect definition guide, but most of them have it.
Restrictions
Some functions can be used only in a specific context. For example, The
Calculate returns a scalar value, but it cannot be used inside a GroupBy
function. That is the limitation of the GroupBy function.
calculate function is not allowed as the expression for the GroupBy function.
You will find restrictions like these in some functions. However, mostly you
will find the docs that explain the limitation.
Check out the Docs to find out if there are any restrictions.
Exceptions
Some functions are neither tabular nor scalar. They don’t return an output.
For example, the CrossFilter function changes the behavior of a relationship
and can be used only inside a Calculate Function.
Summary
Understanding DAX requires you to change your mindset from
programming languages or even expression languages. The difficulty is
mainly understanding the filter context. However, another part is
understanding the output type of each function. You should know how and
where to use tabular or scalar functions in DAX. In this chapter, you learned
that you could nest these functions inside each other in the right way, but
always keep an eye on restrictions and exceptions.
Chapter 6: DAX Variables: Better
Readability, Consistency, and
Performance in Power BI
Calculations
Have you ever had a scenario that you need to use part of your calculation
multiple times? You might go and create a table or column for that and then
re-use it. However, there are times that you just need that calculation to be
re-used multiple times within one place. DAX variables can help you with
that. DAX variables are also helpful to make the performance of the
calculation better. This chapter will explain the DAX variable, scenarios of
using it, and how it can improve your Power BI calculations.
Re-Using Part of the Code
It sometimes happens that you need to re-use part of the code. Consider the
example below:
Adjusted Budget =
IF(
SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
>
SUMX(
FactInternetSales,
FactInternetSales[UnitPrice]*FactInternetSales[ExtendedAmount]
),
SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
),
SUMX(
FactInternetSales,
FactInternetSales[UnitPrice]*FactInternetSales[ExtendedAmount]
)
)
The expression above is hard to read and also has some repetitive sections.
Let me mark them for you for better understanding:
We have two main parts in the expression above: A and B. Each of those is
doing a calculation. Now, with the markings above, reading the expression
is much simpler. The whole expression means this:
=IF(A>B, A, B)
DAX Variables
You can define a DAX variable with VAR (not case-sensitive), and then re-
use it as many times as you want through the same expression. Here is for
example, how I define a variable for A:
Adjusted Budget =
var A=SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
The expression above is not yet a complete one, and if you try something
like that, you will get an error. Defining the variable is part of the operation.
The other part is to return something. That is what we do using
the RETURN keyword.
Adjusted Budget =
var A=SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
return A
This expression just defines a variable and returns it. We don’t use the
expression defined within variable more than once, but still ok.
You can define more variables by adding more VAR to the statement. Here
is what our expression looks like using the variables:
Adjusted Budget =
var A=SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
var B=SUMX(
FactInternetSales,
FactInternetSales[UnitPrice]*FactInternetSales[ExtendedAmount]
)
return
IF(A>B,A,B)
However, if you define the part as a variable, then calculation happens once,
the result stored in the variable and re-used multiple times. This would
perform much faster than re-calculating it.
Anything can be stored in a variable:
Table or Value
Another good thing about the variable is that you can even store a table in a
variable. Like below:
var _allSalesTable=ALL(FactInternetSales)
var _totalmargin=SUMX(
_allSalesTable,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
As you can see, the outcome of the ALL of the FactInternetSales is stored
in a variable. This is a whole table stored in a variable and can be used in
other places.
This also means that variables have a scope in which they operate. If you
define the variable within the SUMX expression, then the variable cannot
be used outside of that part. If you define the variable at the beginning of
the main script's expression, it can be used anywhere in the same
expression.
Variables in DAX are helpful in both readability and also the performance
of your code. However, there are scenarios that you have to be careful when
you use variables. Because variables are stored, they might return a
different result if you had that definition in a measure. Let’s see an example
in this chapter.
Variables in DAX
You can define a DAX variable using the VAR statement and then use it in
a RETURN statement or even in another variable through that expression.
Here, for example, you can see a use case of the variable:
The above is an example of the “right” usage of the variable. Variable can
be used mistakenly in the wrong situation, though. Let’s see an example.
As you can see in the below screenshot, the result is wrong! The result is
similar to the sales amount of each month! And that is wrong.
Why?
In DAX, variables are calculated within the scope in which
they are written. And then, their value is stored and re-used
in the rest of the expression.
SAMEPERIODLASTYEAR(DimDate[FullDateAlternateKey].[Date])
)
This expression is using the variable inside the context that it should be
calculated, and as a result, it returns the correct output;
Another Example
The example you have seen is more like a “learning” example because you
won’t create a variable usually for such a simple statement. I used that
example to explain to you what is the problem, and how to solve it. Now,
here is another example, which makes sense more;
Then this would be the wrong usage of variables for it:
Sum and Sumx are functions that are often considered to be misleading for
many Power BI users. As both functions are doing the aggregation, it seems
confusing the actual difference between these two. There are many blog
posts and articles about each function. This chapter is explaining the
difference between these two functions.
All other aggregation functions also work the same; Average, Min, Max,
Count, etc. Now let’s see when SUM functions fall short.
When you start writing that measure, you don’t even get the DAX
intelligence for the second part of your expression:
And it would work. However, for long expressions, this way of writing will
become hardly readable. If you add one Sum in front of every column
name, you may end up with expressions such as below;
A measure with few SUMs =
if((SUM(FactInternetSales[SalesAmount])
-SUM(FactInternetSales[TotalProductCost]))
/SUM(FactInternetSales[OrderQuantity])
>SUM(FactInternetSales[ExtendedAmount])
,SUM(FactInternetSales[ExtendedAmount])
-SUM(FactInternetSales[SalesAmount])
,SUM(FactInternetSales[OrderQuantity])
*(SUM(FactInternetSales[UnitPrice])
-SUM(FactInternetSales[UnitPriceDiscountPct])))
It looks scary. Well, there is another way; use SUMX. SUMX is the sum of
an expression, the X at the end of this function is for eXpression. This
function gives you the sum of any expression. Here is the way to use it:
SumX(<table name>,<expression>)
For SUMX to work, you need to specify a table name. When you use SUM,
you do not need a table name because one column only belongs to one
table. But when you use SUMX, you may write an expression that uses
columns from other tables. In the example for Margin, both columns are
coming from the same table; FactInternetSales. So, our expression would
be:
Sum of Margin = SUMX(
FactInternetSales,
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
But the expression above is not always giving you the total margin. If you
slice and dice it by a column, here is the result;
Filter context (or, let’s say, whatever filters the visual) will impact the
calculation result. So, when looking at the Bachelors' education category,
the sum of Margin for that is not the total margin; it is just the sum of
margin for that category.
ALL is an exciting function, which we will have a separate chapter about it
in this book. I can use the ALL function to give me the entire table
regardless of the filter context; this is what my expression and the result
would look like:
Total Margin = SUMX(
ALL(FactInternetSales),
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
How does this work? ALL is a function that returns a table as output.
SUMX is a function that gets a table as input. So they can work with each
other nicely! ALL can be the input table of the SUMX function. Nesting or
cascading functions and tables into each other is something that happens
very often in DAX. Because ALL is a function that passes the entire table
regardless of the filter context, we get the full FactInternetSales table with
no filters, and the result would always be the total margin.
You may think, what is the usage of such a thing? Well, you can use it to
calculate the percentage of the margin for each education category. Here is
how it works:
Any TABLE can be the Input for SUMX
It is not just the ALL function that can be the input for SUMX. You can also
use any other functions that return table or any other tables as the input for
the SUMX. For example, the expression below is giving us the Filtered
result of the FactInternetSales table, when the Education category is “High
School”;
In this example, the FILTER function is used as the input for SUMX to give
us the calculation result only on a filtered dataset.
Sum of Margin for High School = SUMX(
FILTER(
FactInternetSales,
RELATED(DimCustomer[EnglishEducation])="High School"
),
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost]
)
This can be done with other functions as well. Here, for example, I used the
CalculateTable function to do the filtering:
Sum of Sales by Customer = SUMX(
CALCULATETABLE(
FactInternetSales,
DimCustomer[EnglishEducation]="High School"
),
FactInternetSales[SalesAmount]-FactInternetSales[TotalProductCost])
Summary
Sum and SumX are both functions calculating aggregation. However, the
SUMX calculates the aggregation on an expression resolved from a table
that can be dynamically calculated. SUMX is a generic and powerful
function, so we see the usage of that a lot in DAX. One thing to remember
is that SUMX, like any other iterator function, is consuming temporary
memory storage and doing the calculation one row at a time, then
aggregates it.
Chapter 9: Calculate Totals in
Power BI: Using Iterators in DAX
The total value that you see in a table is not SUM of all values in that
column; it is, in fact, the calculation when there is no filter. This, however,
might not be the calculation that we want sometimes. We might want this
value to be the sum of values of that column in the table visual. This
chapter will show you an easy method to calculate the total using Iterator
functions in DAX.
All you see in the above expression is that I am saying calculate the [New
Customers] measure value once for every row in the customer table. This
result will be stored in the temporary memory and then at the end
summarized (because we are using SUMX).
The same method can be used for Lost customers too:
Lost Customers Total Count =
SUMX(
DimCustomer,
[Lost Customers]
)
Summary
SUMX and Iterators are only one way to help you create the totals, but it is
not the only way. Sometimes, you might find the performance of iterator
functions slower than other methods.
Chapter 10: Showing Ranking in a
Visual in Power BI using RANKX
DAX function
If you want to show the ranking in a Power BI visual, one way is to use a
visual supporting that, such as a Ribbon chart[https://radacad.com/ribbon-
chart-is-the-next-generation-of-stacked-column-chart]. Another way, which
is a more common way, is to write a calculation for rank, and RANKX is a
DAX function that can help you with that. In this chapter, I explain how
that works.
The table and expression are the required parameters. The rest are optional.
Table: The table (or virtual table) is used as the source of items
for the ranking.
Expression: The expression that the ranking is calculated
based on it. This DAX expression should return a single scalar
value.
value (optional); scalar expression
order(optional); Based on what order the ranking is calculated.
Default is descending.
ties(optional); What should happen if there is a tie
Using RANKX as a Measure
If you want to use it in a measure (usually when you want the ranking to be
calculated dynamically), there is a little trick.
Let’s assume we want to have the ranking in the visual below based on the
Sales measure (which is Sum(FactInternetSales[SalesAmount]).
Color is a field in DimProduct, and you may think the table parameter of
the RANKX should be DimProduct, but that leads to all ranks to be
calculated as 1!
CrossJoin is, of course, not the only way to produce this result; it is just one
way of doing it.
calculating rank in Power BI when table columns are from two different tables
Think of all tabular functions when you want to write the table parameter
here. They can be most helpful.
Summary
RANKX is a scalar DAX function in Power BI, which can be very helpful
when calculating rank as a value in a Power BI visual. The critical
consideration for ranking is to pass the table parameter value correctly. You
can also choose what happens when there is a tie.
Chapter 11: Generating Row
Number in Power BI Visualization
Using DAX
Sample model
I have a simple model with three tables below;
I also have a couple of measures for the SalesAmount for each of the fact
tables;
Internet Sales = CALCULATE(SUM(FactInternetSales[SalesAmount]))
Reseller Sales = CALCULATE(SUM(FactResellerSales[SalesAmount]))
Now imagine that we want to have a visualization like below that shows all
the products, and their Internet Sales;
In the above visualization, I want to calculate the row number based on
Internet Sales.
,[Internet Sales])
If you use the ALL, even if you are in the current row, the indexing will
happen on the entire list, leading to the correct result.
Can’t I use a column only?
No. This parameter has to be a table. A column is not representative of a
table.
,[Internet Sales])
You can have multiple columns inside the ALL function. The result is as
below.
Is ALL the only function that works?
No. you can use many other functions. The main thing to remember is that
you need to use a function that gives you a list of unique combinations of
values you want to create the index.
2nd Parameter: Expression
The second important function is the expression. The row number is based
on what value? Internet Sales or Reseller Sales? The below example returns
a row number based on Reseller Sales.
Row Number = RANKX(
ALL(DimProduct[EnglishProductName])
,[Reseller Sales])
Summary
RANKX is a function that you can use to calculate the row number
dynamically in a measure in Power BI. If you want to calculate the row
number, not dynamically, I strongly recommend doing it in Power
Query[https://radacad.com/create-row-number-for-each-group-in-power-bi-
using-power-query] or the data source.
Part 3: Filter
Chapter 12: FILTER Function in
DAX and Power BI: Apply Custom
Filter to Calculations
The Color field in the DimProduct will be filtered to only include Red as
below;
Filter function in DAX used to filter a table with one condition in Power BI
Note that DAX is not case-sensitive, “Red” and “red” would be the same. If
you want to make it case-sensitive, you can use exact match functions, as I
explained in later chapters of this book.
DimProduct[Color]='Red' &&
DimProduct[SizeUnitMeasureCode]='CM')
return
SELECTCOLUMNS(
filtered,
'Product Name',
DimProduct[EnglishProductName]
)
)
return
CALCULATE(
[Sales],
filtered)
Summary
The FILTER function in DAX is a simple function to use for filtering rows
of a table. This function does not change the columns (unless used as an
input of column manipulation functions such as SELECTCOLUMNS or
ADDCOLUMNS). The filter function requires a table input and an
expression. The expression should return true or false and can include
AND/OR functions or operators. Like many other tabular functions, the
main benefit of this function is when used to create a virtual table in a
measure expression.
Chapter 13: Now You See Me! Use
cases of the ALL DAX Function in
Power BI
Among all the functions in DAX, the behavior of the ALL function still
seems mysterious for many. Many users don’t use it and write a highly
complex calculation for a scenario that only a straightforward expression
can do the same job. Some users use it but don’t exactly know how the
function works, get unexpected results, and call it an error. This chapter will
explain what the ALL function is, how it can be used, and what are use
cases of using such a function in DAX and Power BI.
Prerequisite
The dataset for this model is the AdventureWorksDW2012 Excel file,
which you can download from the book’s code file. The tables used in this
example are DimCustomer, DimProduct, FactInternetSales.
What is the ALL() Function in DAX?
Nothing is better than an example to understand the behavior of the ALL
function. Let’s see how it works in action; I’ve created a Measure using the
ALL function. ALL function accepts a table as input, and
ALL( <table name or column name>, [Column name 1],[column name 2], …)
The output of the ALL function is a TABLE, and you cannot use it in a
measure. As you can see in the below screenshot; if I create a measure with
ALL, I get an error saying; The expression refers to multiple columns.
Multiple columns cannot be converted to a scalar value.
As the output of ALL function is a table, then you have only two ways to
use it in DAX:
Using ALL directly in Calculated Table
As the output of the ALL function is a table, it can be used directly in
creating a calculated table. For example, All used with a table name will be
an exact copy of that table.
Or, if you use all with only one or more columns, then you get a table with
a distinct combination of those column values;
ALL(FactInternetSales[SalesAmount]) will return only 42 rows, which is
the distinct values in the SalesAmount Column,
However, If ALL is used with a combination of columns. Such as three
columns below, then the combination would be distinct;
Using the ALL function in the Calculated table, give us one of the most
common use cases for the ALL function:
As you can see, the only difference is the usage of ALL in the Total Margin
expression. The output value is also different. The total Margin in each row
is the same value; $12,080,883.65, like the margin, is never filtered by
Education, or let’s say like ALL function ignores the filter applied.
Common Use Case for ALL: Calculating
Percentage
One of the most common use cases for using ALL is calculating the
percentage of a measure. The trick is to calculate that value once without
the ALL. And once with the ALL. And then divide one by the other! Just
for an instant, think how you would do this calculation if there were no
ALL function? How would you find out the total margin when the
Education category already filters the margin? This is the power of the ALL
as a function to give you such a combination.
ALL and Calculate
ALL can also be used as an input for the Calculate function. The second
input is a filter, and a table function acts as a filter. In our case, ALL is a
filter function that DOES NOT filter! No matter where you write the
calculate function, whatever filter applied will be ignored when you put an
ALL function.
FactInternetSales
)
)
The previous chapter was about the ALL function in Power BI and how it
helps work with filters in your report. However, more clarity on this
function would always help. This chapter is explaining that in detail.
Why ALL?
The ALL is a handy function in DAX that ignores the filters. Because in
Power BI, measures are always affected by the filters coming through
visuals (filter context), sometimes IGNORE these filters, can be very useful
on many occasions.
For example, you can use ALL in an expression like below to get the total
SalesAmount regardless of filters applied in other visuals or slicers;
Sample Model
To understand the rest of the chapter, I start showing you the data model I
am working with, which is as below:
Sample Report
I also have the below report as a sample:
In the above screenshot, you can see that the Sales measure’s value is
affected by three filters: Color from DimProduct, EnglishPromotionName
from DimPromotion, and EnglishEducation from DimCustomer. Although
they are not defined as a filter, two of them are slicers, and one is a column
in the table visual. Still, they are filtering the values calculated by the
measure.
The calculation above gets filtered by the Promotion and Education, but not
by the Color (from DimProduct).
The expression below won’t accept any filters coming from the
DimCustomer or DimProduct tables.
Other Variations
Other variations of using ALL, such as ALLExcept or using ALL with
other functions, can help ignore some filters and accept custom filters.
Chapter 15: Removing the Total
Value for a Column in the Table
Visual of Power BI Using
ISFILTERED
Table visual is one of the most commonly used visuals. You can turn off the
total row (when it won’t make sense to have the total) entirely. However,
you cannot turn off the total for some columns and keep it working for
others. Using a DAX function, you can, however, do this easily. Let’s see
how it is possible.
To show you how this function works, I write a measure like below:
Is Filtered = ISFILTERED(DimCustomer[EnglishEducation])
If I add this into my table visual, I can see when this function returns TRUE
or FALSE
Because the EnglishEducation field filters the table visual in the above
screenshot, the ISFILTERED returns true for every row in the table.
However, the total row is NOT Filtered by the EnglishEducation, and that
means returning False.
The ISFILTERED function is beneficial in many scenarios of writing DAX
expressions. In our case, for removing the total value from a specific
column, it can be used simply by using the field that filters all other fields.
In my case, All I need is to check if the EnglishEducation is filtered or not
and then use it in the measure calculation of “List of FullName values” (this
measure is, by the way, a quick measure created by a concatenated list of
values.[https://radacad.com/quick-measures-in-power-bi-you-dont-have-to-
write-dax])
Here is my changed version of the DAX expression with ISFILTERED in
it;
As you can see, I wrapped the measure’s value inside an IF statement like
this:
IF(
ISFILTERED(
<column or table name>
),
<the result you want to show for the table rows - not the total>
)
The above IF statement only uses the expression, and the what-if true, part
of the IF., the what-if false part of it, is not used because it is optional, and
when not provided, the default is blank.
If you ever want to provide something else as a result of the total row, you
can then use the what-if false part of the if statement like below;
IF(
ISFILTERED(
<column or table name>
),
<the result you want to show for the table rows - not the total>,
<the result you want to show for the total row only>
)
If you don’t have a database background, this is what the code is doing:
The expression will find the data row with the “31” value in the
EmployeeKey column and then return the value of the FirstName column.
What if the value not found?
If the value is not found, then the alternate result will be returned by default
blank.
Employee 31 =
LOOKUPVALUE(
DimEmployee[FirstName],
DimEmployee[EmployeeKey],
2222222,
"Not found!"
)
What if multiple values as the output?
The LookupValue function works best when you have only one value
returned. If you have multiple values, it will either return the result of
<alternate result> if supplied; otherwise, it will return an error.
Employee 31 =
LOOKUPVALUE(
DimEmployee[FirstName],
DimEmployee[MiddleName],
"R",
"Not found or Multiple results"
)
You can add more criteria
If you have more search conditions, you can add them all by adding more
search columns and values.
Employee 31 =
LOOKUPVALUE(
DimEmployee[FirstName],
DimEmployee[MiddleName],
"R",
DimEmployee[LastName],
"Gilbert",
"Not found or Multiple results"
)
LookupValue Function is Often Used
Within Other Functions
Although, you can use the result of the LookupValue function as a measure
or column on its own. However, the majority of use cases of LookupValue
is where it has been used inside another function. Let’s say you are looking
for a value of a different column in a table when another column’s value is
equal to something, and then using the result, you want to apply some
filtering or other work.
Here is an example of the LookupValue function I have used in
my Dynamic Row-Level Securit[https://radacad.com/dynamic-row-level-
security-in-power-bi-with-organizational-hierarchy-and-multiple-positions-
in-many-to-many-relationship-part-2]y example:
In that example[https://radacad.com/dynamic-row-level-security-in-power-
bi-with-organizational-hierarchy-and-multiple-positions-in-many-to-many-
relationship-part-2], I fetched the user ID of the logged-in user using the
LookupValue function.
Summary
The LookupValue function in DAX is a very simple yet helpful way of
fetching the value of a column in a data table when other column’s values
are equal to something. You can read it as a select/where statement in T-
SQL, or similar to how VLookup somehow works in Excel. The primary
usage of this function is when it is used inside other functions as an input
parameter. However, this function can be used on its own to return a value
for a visualization.
Chapter 17: The IF and Filter are
Different! Be Careful (DAX)
Brief of Functions
IF
“IF” is a conditional filtering expression function for DAX. You can write a
conditional expression including the Then and Else part of it. It simply
works with this syntax;
IF(<conditional expression>, <what happens if true>, <what happens if false>)
Filter
“Filter” is a function that filters data set based on a custom filter. For
example, you can filter only products with a “Red” color. Here is an
example Filter expression;
FILTER( <table>, <filter condition>)
Conditional Sum
There are multiple ways of calculating conditional sum in DAX. You can
use SUMX or CALCULATE. Both of these functions calculate an
expression (In this case, it would be the sum of sales amount from
FactInternetSales) based on a filter (which would be our conditional
expression to find “Red” products). I will use SUMX in this example, but
the same concept applies to Calculate function as well. Here is how you can
use SUMX for calculating the sum of “Red” products;
Method 1 – SumX with FILTER
I can use the SUMX expression and filter the data set to be only “Red”
products in the first method. Create a new Measure in FactInternetSales
with this expression;
Sum of Red Products - With Filter = SUMX(
FILTER(FactInternetSales,
RELATED(DimProduct[Color])='Red')
,FactInternetSales[SalesAmount]
)
As you can see in the above expression, I have used a simple FILTER
function to filter everything in FactInternetSales when the Color or product
is “Red”. I have used the RELATED function because Color is a column in
DimProduct, and Related Function goes through the relationship from
Many (FactInternetSales) to One (DimProduct) and allows us to do the
filtering based on a column in a related table.
Method 2 – SumX with IF
We can achieve the same result with SUMX and IF together. In this case,
the condition comes as an IF statement in the expression part of SUMX.
Here is the new measure’s code;
Sum of Red Products - With IF = SUMX(
FactInternetSales,
IF(RELATED(DimProduct[Color])='Red',
FactInternetSales[SalesAmount],
0)
)
In this expression, instead of filtering data with the FILTER function, I have
used a conditional expression to identify if the product's color is “Red” or
not. If it is “Red”, then I use SalesAmount for sum calculation. Otherwise, I
use zero (means don’t summarize for other product colors).
Method 3 – Calculate with Simple Conditional
Expression
There are many other methods of calculating the conditional sum, but just
adding this one because it looks different; If I use Calculate Function with
simple expression for checking the color of the product as a new measure;
Sum of Red Products - Calculate Simple Expression = CALCULATE(
SUM(FactInternetSales[SalesAmount]),
DimProduct[Color]='Red'
)
Different Results
The result for the measures is perfectly similar; however, if you use one of
these measures for a data set, you will see the result of the data set is
different, which significantly changes the outcome. For example, if you use
“Sum of Red Products – With Filter” only in a table with “Color” from
DimProduct, here is what you will see:
If you use “Sum of Red Products – With IF” only in a table with “Color”
from DimProduct, you will see the different results;
In both cases, the total is similar. However, the table with a FILTER
measure will automatically filter the data set and only show the result set
for RED products. The second table with IF measure will display all
products with zero in front of all colors, except Red. These two are VERY
different from the user's point of view, while the final total value is similar.
The reason is that IF apply a conditional expression on the result set, where
FILTER works differently and filters the data set to the custom filter
expression. Notice that we don’t have any Visual, Report, or Page Level
filters applied in this example. Filtering happened automatically because of
the FILTER function.
If you bring the last method’s result into a table (Sum of Red Products –
Calculate Simple Expression), you will see the calculation happens on
every row in the result set. It won’t filter the data set, but the filter applies to
calculating the final result for every row.
This chapter will explain one method of writing DAX expressions that
overwrite how Power BI visuals interact. You will learn how to write a
DAX expression that some filters affect on that, some not. Let’s see how the
method works.
In the above screenshot, you can see that result of Sum of the Sales Amount
is not always the same value. IT DEPENDS! It depends on what filter you
have selected or what values you have sliced and diced. For example,
Highlight numbered 1 shows the sum of Sales Amount for product Color
Blue, the Calendar Year 2008, and Customer’s Full Name “Aaron Collins”.
While the highlight numbered 2, shows the sum of Sales Amount for the
Year 2008, and color Blue, but for all Customers. What you see here is
Filter Context.
Filter Context is the combination of all filters, slicers, highlight, slicing, and
dicing applied to a report or visual. Filter Context for number 1 in the above
image is product Color Blue, the Calendar Year 2008, and Customer’s Full
Name “Aaron Collins”.
Everything in DAX resolves based on Filter Context and Row Context.
However, there are some ways to control the context. Controlling the
context means controlling the interaction of visuals. In the above example,
with any change in the slicer, filter context changes, and the result of
Sum(SalesAmount) also changes. However, if we write a DAX expression
that doesn’t change with selecting a slicer, that means we have controlled
the context. Let’s look at some examples.
In the above DAX expression, the ALL function will act regardless of
filter context. No matter what Filter context is, the ALL will return
everything, and as a result, SUMX will calculate the sum of SalesAmount
for all rows. Here is a screenshot of the report;
It doesn’t matter what filter you select or what slicer you click. The result
for the measure is always total value. Now let’s control the context a bit
differently.
Total Sales Filterable Only by Date
Selection
Let’s take one step forward with bringing one selection criteria in the
measure. For this measure, we want to create a Total Sales that can only be
changed when a date selection happens (Year in our example), but nothing
else.
Because we need multiple filters now, I’ll do it this time with a
CALCULATE function to specify various filters. Here is the code:
Date Filter Sales = CALCULATE( SUM(FactInternetSales[SalesAmount]),
DATESBETWEEN(DimDate[FullDateAlternateKey],
FIRSTDATE(DimDate[FullDateAlternateKey]),
LASTDATE(DimDate[FullDateAlternateKey])
),
ALL(FactInternetSales)
)
The above measure is similar to the previous measure with only one more
filter: RelatedTable(DimProduct). This filter will return a subset of select
products. As a result of this measure, Product and Date selection will be
effective;
Summary
As you can see, simply with DAX expressions, you can control the filter
context. In other words, you can control the interaction in Power BI. Note
that you can write DAX expressions in many different ways. The
expression above is not the only way of controlling filter context. Iterators
and Calculate function can be very helpful in changing this interaction.
Part 4: Relationship Functions
Chapter 19: Get a field value from
a related table in Power BI: DAX
RELATED Function Explained
Sometimes, in Power BI, you need to access a field’s value from another
table that somehow is related to the existing table. You can use Power
Query transformations such as
combining Merge[https://radacad.com/append-vs-merge-in-power-bi-and-
power-query] with something else. However, this can be needed when you
write a DAX expression too. In this chapter, I explained a simple but
effective DAX function for this purpose; RELATED.
The only input parameter for this function is the column's name which we
want to fetch its value. Let’s see that as an example.
The expression above won’t work, and I will get an error, saying that:
A single value for column ‘EnglishProductSubcategoryName’ in table
‘DimProductSubcategory’ cannot be determined. This can happen when a
measure formula refers to a column that contains many values without
specifying an aggregation such as min, max, count, or sum to get a single
result.
You cannot access a field’s value from another table in a calculated column
Why can’t you write an expression that way? Because the
EnglishProductSubcategoryName in the other table has multiple values, not
one single. Your column expression should return one single value. The
EnglishProductSubcategoryName that is for this product (the current row’s
product). You can use a LookupValue function in DAX to retrieve the value
you want, but the solution is much simpler than using the RELATED
function.
Fortunately, in the model, there is a relationship between the two tables
based on ProductSubcategoryKey;
The existing relationship between the two tables
This means that the RELATED function can give you the value of any
column from the DimProductSubcategory table while writing a calculated
column in the DimProduct table. All you need as an input is the column's
name you want to pull the data from it.
Sub category =
RELATED(DimProductSubcategory[EnglishProductSubcategoryName])
The Related function fetches the value from another table based on the existing relationships in the
model.
The Related function goes through a one-to-many relationship and will give
you a value from the ONE side of the relationship and bring it to the
MANY side.
How the RELATED function works in Power BI and DAX
The related function accesses the field’s value from tables even if the relationship is not direct
In the example above, the values of category names traveled through two
relationships, with just one mention of the RELATED function.
The related function can traverse multiple relationships
As you see, the Related function makes things far simpler than
LookupValue if the relationship already exists. There is, however, a
direction that the RELATED function won’t work on that.
),
FactInternetSales[SalesAmount]
)
There are, of course, much easier ways to write the expression above using
Calculate. However, I just wrote it using SUMX without the help of extra
measures to show you how the RELATED function can work in this
context. I have filtered the FactInternetSales table using the Color field in
the DimProduct table using the RELATED function used inside a FILTER.
Multiple functions can help when you work with tables that are connected
through relationships. One of these functions is Relatedtable. This function
gives you the subtable from the other table for all the rows related to the
current row. For example, calculate all the sales transactions (from the Sales
table) for the current customer (from the Customer table). In this chapter, I
explain how this function works.
Summary
The RELATEDTABLE function is working with existing active
relationships in the model. This function returns a table, which is the subset
of rows from the given table for the row context of the other table. This
function can traverse multiple relationships. This function can be used in
measures too, but as this is a tabular function, you need to wrap it in other
functions to return a scalar value.
Chapter 21: UseRelationship or
Role-Playing Dimension; Dealing
with Inactive Relationships in
Power BI
Let’s create a simple column chart with the SalesAmount from the
FactInternetSales table and the FullDateAlternateKey from the DimDate
table. Because the FullDateAlternateKey is a date field, Power BI brings the
default hierarchy. I’ll see the visual slicing and dicing data by the highest
level of the hierarchy, Year.
But wait, it isn’t slicing and dicing! It is showing the same SalesAmount for
every single year from 2005 to 2010! The value is very close to $30 million,
which is the total sales in my dataset. The fact is that the
FullDateAlternateKey field is NOT filtering the FactSalesAmount table.
As you can see, the same visual, this time filters the sales by the date field.
Or better to say, DimDate can now FILTER the FactInternetSales table. All
of that because of the relationship. Without a relationship, we cannot filter
data across tables just by itself. You may need to do some DAX expressions
instead.
Now that you know relationships are for Filtering let’s check out what the
inactive relationship is.
Inactive Relationship
The type of relationship you have seen above is called an active
relationship. There is another type of relationship called Inactive. Let’s see
how an inactive relationship will be created. In the previous example, we
sliced and diced data by the OrderDateKey field because the field
connected through the relationship to the DimDate table. Now, let’s say we
want to slice and dice data by the ShipDateKey. The simple approach is to
create another relationship between the DimDate table and
FactInternetSales but this time to the ShipDateKey. Here is the result:
As you can see, this new type of relationship is different. It is a dashed line,
compared to the active, which was a solid line. This is an inactive
relationship. You can only have one active relationship between two tables.
Any other relationships will become inactive.
Role-playing Dimension
A dimension that acts as multiple dimensions is called the role-playing
dimension in the data warehousing terminologies. In the above example,
DimDate will play the role of Order Date in some scenarios, the role of
Ship Date in other scenarios, and sometimes the role of Due Date in other
times. I already explained a sample usage of Calculated tables in DAX to
implement a role-playing dimension in another chapter earlier in this book,
so let’s go through it very quickly here too.
One method to deal with the inactive relationship is to remove the cause to
create it! If having multiple relationships between two tables is causing the
creation of an inactive relationship, one way to avoid it seems to be creating
multiple instances of the same table. Then you would need only one
relationship, not more than that.
Let’s create a copy of the DimDate. One way to make the copy is to use a
Calculated Table with the ALL DAX function in it;
The ALL is a function that gives you the entire table. In this case, we are
creating a copy of the DimDate table and calling it ShipDate. Now you can
create a normal active relationship between ShipDate and the
FactInternetSales table (I have removed the inactive relationship from the
previous section);
And now, as a result, you have slice and dice by the ShipDate table as well
as the Order Date (or let’s say DimDate table);
The role-playing dimension is one of the ways that you can
handle an inactive relationship, but be careful of memory
consumption!
This measure calculates the sum of sales by ship date. The whole secret is
the usage of the UseRelationship function. This is a really simple function
to use. You need to provide two input columns to it, the two columns that
are two sides of the relationship. Their order is not important.
UseRelationship (<column 1>, <column 2>)
The critical tip to consider is that you HAVE to have an existing inactive
relationship for this function to work; otherwise, you get the error below:
An inactive relationship must exist; otherwise, the
UseRelationship doesn’t work.
Summary
In this chapter, you learned about inactive relationships and how to handle
them through two methods; the Role-playing dimension and the
UseRelationship function in DAX. The role-playing dimension method is
good for smaller tables where the extra memory consumption is not the
issue. UseRelationship method, on the other hand, can be a good substitute
when the tables are bigger. There are other benefits, such as getting one
table filtering based on multiple fields at the same time as you’ve seen.
Chapter 22: DAX CrossFilter
Function in Power BI: Write the
Formula both-directional, but keep
the relationship single-directional
If you are familiar with relationships in Power BI, you know that there are
scenarios that you may need to change the direction of the relationship to a
both-directional. A both-directional relationship comes at a cost, which is
mainly the performance and ambiguity of the model. There is a way to
write a calculation in a both-directional way but keep the relationship still
single direction. This would help with the performance because the
performance impact will only happen when using this measure. In this
chapter, I explain how you can do that.
Multiple Relationships
To understand how it works if you have multiple relationships, let’s discuss
another requirement. Let’s say we want to see the Sum of SalesQuote (from
FactSalesQuota) table for all the employees (from DimEmployee) that have
sold products (from FactResellerSales) that each customer has purchased
(from FactInternetSales).
For a requirement as above, we need all tables on the deck. If we use the
sum of the SalesQuota from the FactSalesQuota table, it is not going to
work;
Now, if you want to add more IF statements, this becomes getting hard to
read;
This is only for three of those values. You can imagine how the expression
would be if we have five values, or what if we have even more!
SWITCH
The Switch is an efficient function in DAX (and many other languages) to
help writing multiple IF statements much easier. The switch is written in
this way:
SWITCH(
<expression>,
<value 1>,<result 1>,
<value 2>,<result 2>,
...
,<else>
)
If we want to write the expression above using Switch, it will look like this:
Back Color =
SWITCH(
SELECTEDVALUE(DimCustomer[EnglishEducation]),
"Bachelors","Green",
"High School","Red",
"Partial High School","Tan",
"Graduate Degree","Yellow",
"White"
)
You can see that even I’ve added one more condition in the expression
above, and it is still much more straightforward than writing many IF
statements.
[Sales]>3000000,"Yellow",
[Sales]<2000000,"Red",
"White"
)
Replacing the expression with TRUE and the value with a conditional
expression means that you get the same output, but this time, you can write
a condition that can be greater than, less than, or even between values.
I hope you use SWITCH in your statements instead of multiple IF
statements much easier with this chapter help. The techniques above,
especially the last one, is what I use a lot in my expressions.
Chapter 24: Stop DAX Cumulative
Total Calculation in Power BI
Suppose you have calculated a cumulative total (such as running total, year
to date, etc.) using quick measures or writing the DAX expression. Then
you realize that the calculation happens even for periods without any data.
You want to stop that calculation at a certain point in time. The trick is
simple. In this chapter, I’ll explain how it works.
Sample Report
I have a sample model to show you how this works, including two tables,
the DimDate and FactInternetSales table.
The DimDate table is marked as a date table, which means I am not using
the default Power BI date table[https://radacad.com/power-bi-date-
dimension-default-or-custom-is-it-confusing].
I have a line chart with FullDateAlternateKey (a date field in the DimDate
table), SalesAmount from the FactInternetSales table in one value, and
Sales YTD, which is a measure in another value.
Sometimes, this might be the desired outcome. Which in those cases, you
don’t need to change anything else. Sometimes your calculation of
cumulative can be a running total, etc.
Also, other scenarios can be showing the calculation results until today (or
tomorrow, or yesterday), but nothing more. You want the calculation to stop
at a certain point in time. Below is a simple trick of how you can do it.
The SelectedValue function is used to get the value from the chart's axis,
and if that particular date is less than or equal to _lastActualValue, it
calculates the expressions. When you don’t define the ELSE part of the IF
statement, it means BLANK in the case of ELSE. And when blank is
returned, the visuals in Power BI (also depends on the visual) won’t show
any value.
So this leads the visual to appears correctly now:
In my sample data this code, won’t be much different than the normal Sales
YTD, because the date of writing this chapter is the 3rd of Sep 2020, and
my sample data is for the years 2005 to 2008. However, it would work
simply in your up-to-date data.
Tomorrow;
Today()+1
If you are dealing with TODAY() or any functions related to that, remember
the timezone configuration of Power BI servers. I have explained in an
article how that can impact your reports and what you can do about it, read
that article here[https://radacad.com/solving-dax-time-zone-issue-in-power-
bi].
Summary
Changing a calculation to stop at a certain point in time is not complicated.
It is straightforward. You must first find out that point in time (for example,
the latest date for actual value, today, etc.). And then use an IF statement to
check the date in the visual and only shows values until that point in time.
Chapter 25: DAX and Conditional
Formatting Better Together: Find
The Biggest and Smallest Numbers
in the Column
This chapter will show you how to use DAX combined with conditional
formatting to highlight the biggest and smallest number in a column in a
table. In the Power BI world, DAX is your friend, so let’s see how DAX, in
combination with conditional formatting, can do that for you.
Prerequisite
The dataset for this model is the random set of numbers I have created,
which you can download from this book’s code file.
Our sample database table is as below;
“By Customer” is our table name, and Revenue is the column that we want
to find out its ranking.
Now you can add that measure in the table visual, and you will see this
output
Using Switch to Choose Color
Now that we know the biggest number and the smallest number (using the
result of the RANKX expression), we can set the color based on it. The
SWITCH is a function that works like multiple IF THEN ELSE statements.
We can write another measure as below;
Background Color = SWITCH(
[Rank of Revenue],
1,"Green",
25,"Red",
"White"
)
Let’s translate the code above to if-then-else; if the revenue rank is 1, the
color is green. If it is 25, the color is red, and otherwise, for any other
values, it is white. This is the measure that we will be used for coloring the
values in the table.
Now, let’s do the conditional formatting this time for Font Color;
In the advanced controls window; Choose the Fixed Value, and then Font
Color;
25,"Red",
"White"
)
It is not a good way of writing what we are after. You need to use Switch,
but use it for multiple values. Here is a better way of doing it:
Background Color Three = SWITCH(
TRUE(),
[Rank of Revenue]<=3,"Green",
[Rank of Revenue]>=22,"Red",
"White"
)
and then if we use these two new measures (Background Color Three and
Font Color Three) for conditional formatting, this is what we get;
What If Parameters for Conditional
Formatting: The sky is the limit!
When it comes to combining DAX and visualization, the sky limits what
you can do. Let’s say you don’t know is it the top three that you want to
color code, or four, or five. And also, you don’t know it is different from the
bottom bound of the values. So, as a solution, you can use What If
parameters in Power BI to create two parameters; one for the upper bound
and one for the lower bound.
Start by creating a new what-if parameter under the modeling tab. Name it
as Upper Bound, and then set the minimum as 1, the maximum as 10, the
default as 5, and the increment 1. Make sure the Add slicer to this page is
selected.
Do it one more time for the lower bound with the configuration below;
Now you should have two slicers on your page for each of the what-if
parameters.
Let’s use these two in our background color and font color measures. This
is what the updated background color measure looks like:
Background Color Parameter = SWITCH(
TRUE(),
[Rank of Revenue]<='Upper Bound'[Upper Bound Value],"Green",
[Rank of Revenue]>=
CALCULATE(
COUNT('By Customer'[Customer]),
ALL('By Customer')
)+1-'Lower Bound'[Lower Bound Value],"Red",
"White"
Well, the DAX expression is a bit more complicated than what you
expected! Let’s explain a bit of detail here: ‘Upper Bound'[Upper Bound
Value] and ‘Lower Bound'[Lower Bound Value] are the values selected in
the slicers of what-if parameter tables. Because the Lower Bound value is a
value between 1 to 10, we want this value to be deducted from the
maximum rank in the revenue column, so I used a calculate function to
count all records in the table and then use that as the source of deduction. I
added one to it to avoid results such as 25-1=24. We also want 25 to be
color-coded, which is 25+1-1.
And for the Font color;
Font Color Parameter = SWITCH(
TRUE(),
[Rank of Revenue]<='Upper Bound'[Upper Bound Value],"White",
[Rank of Revenue]>=
CALCULATE(
COUNT('By Customer'[Customer]),
ALL('By Customer')
)
The above measure replaced the White color with Orange.
And here is the output now:
Summary
In this chapter, multiple techniques were used to achieve something I
believe every business would need in their visualization; conditional
formatting based on top/bottom values. We used the RANKX function in
DAX to calculate the rank of values, and then using SWITCH, we produced
the output of coloring. Then using conditional formatting with the fixed
value, we put it in the visualization. You also learned how the process could
If you have worked with Power BI for some time, you know two types of
Date dimensions; Custom or built-in/Default. It is always confusing for
people, which date dimension is suitable to use, and what is the difference
between these two approaches. Also, based on selecting the type of Date
dimension, your DAX calculations may differ slightly. This chapter will
explain all you need to know about the default and custom date dimensions
in Power BI and help you choose the right one for your Power BI solution.
Note that you cannot see the view above in the Power BI Desktop. You can
use tools like Power BI Helper[https://radacad.com/power-bi-helper] to get
to that information, though. In the screenshot above, you can see a
DateTableTemplate, and then three Date tables copied from that template.
What does the Default Date table look like?
There are many variations of the Date dimension, and you may think, what
does the default Date table look like? What columns are available there, and
what columns are not? Here is the list of columns:
The view above can be generated with tools such as Power BI Helper. But if
you are interested to see the list of these column names in the Power BI
Desktop, One way is to see it when you write a DAX expression. After
typing a dot (.) after the Date or Date/Time field name, you get the list of
fields;
This might have been a mystery for many people when they write the DAX
statement. “What is the list of fields that comes after the dot (.) in front of a
date field name?” Now you know the answer;
The Date field is NOT a column from the Power BI model
point of view. It is a TABLE, a hidden table, and because of
that, you can choose which column in that table you want
to use within your expression.
If you don’t use the “.[Date]” then you won’t get the correct result because
Time Intelligence calculations need a DATE column to work with, and with
the “.[Date]”, you choose the date column in the default hidden Date table.
As you can see, the calculation doesn’t work if you do not include the “.
[Date]” in the expression. But if you include it, then all is good to do.
Writing time intelligence expressions using the default Date Dimension is
very easy, as you’ve seen here.
Default Date Dimension has a Built-in Date
Hierarchy
One of the main benefits of using the default Date dimension is the built-in
Date hierarchy of Year/Quarter/Month/Day it provides. Whenever you drag
a Date field, Power BI automatically shows the hierarchy under visual
because there is a hidden Date field with the built-in hierarchy behind the
scene.
Usually, after this change, if you look at the icon of Date fields under your
custom Date table, you will see them differently (without the default Date
hierarchy), which shows the table now successfully marked as a Date table.
Writing Time Intelligence Calculations with
Custom Date Dimension
The prerequisite for this step is to mark the table as Date Table, which we
have done in the previous step, now you can write a DAX expression as
easy as below:
Sales YTD = TOTALYTD(
SUM(FactInternetSales[SalesAmount]),
DimDate[FullDateAlternateKey])
As you can see, in this expression, we do not need “.[Date]” to get to the
date field. Because there is no hidden Date table for this column, you just
refer to the column to get the Date field. If you use the “.[Date]” here, you
get an error because there is no hidden Date table here.
This is Wrong! You will get the correct result in the visualization, but doing
it this way is Wrong! Because if you are going to use the default Date table,
then what is the point of adding an extra custom Date dimension? If you are
going to use the custom Date table, you HAVE TO mark it as a Date Table.
Summary
Date dimension and its behavior in Power BI can be confusing if you don’t
know about the default Date dimension and how to use your custom Date
dimension. In this chapter, I explained the differences between these two
and elaborated on the difference.
Chapter 27: Creating Calendar
Table in Power BI using DAX
Functions
The output of the Calendar function is a table with one column which
includes all dates between the start and end date, with one day at each row.
Here is an example of creating a calendar table using this function:
Create a new Table. (The output of the Calendar function is a table)
The two inputs here are two date fields, so I used Date functions to generate
them from the year, month, and date. You can always use Date() functions
in this way:
Date(year, month, day)
The output of this calculation is a table with one column and values starting
from 1st of Jan 2018 and end on 31st of Dec 2019. This is how easy it is to
use the Date table;
It is not mandatory to put static dates when you define the calendar table.
You can even use it with dates relative to the current date. Here is another
example of creating a calendar table; with the range of dates from a year
before today to a year after;
Calendar Relative = CALENDAR(
TODAY()-365,
TODAY()+365
)
Or you can even create it based on a column, and start with the minimum
date in that column and end with the maximum date, like the below
expression;
But wait! instead of doing it this way, there is a better function for it;
CalendarAuto()
The fiscal year’s start month is an optional parameter. If you don’t set it, it
will use the calendar year instead and starts in January and ends in
December. Here is an example of using it:
CalendarAuto = CALENDARAUTO()
As you can see, the date values are starting from the 1st of January 1910.
The reason is that somewhere in our data model, there is a date field, which
has a value in 1910. It might not be the first of January of that year,
however, but because the CalendarAuto function always starts on the first
day of the year (or fiscal year) and ends on the last day of the year (or fiscal
year), it started then from 1st of January.
Now If I want to do it as a fiscal calendar considering that the 1st of July is
the first day of a fiscal year, this is how I can use it: (Note that you should
enter the last month of the fiscal year, in this example: 6, saying June is the
last month)
Summary
Calendar and CalendarAuto functions are helpful and very simple-to-use
functions in DAX. You can create a date/calendar table with one of these
functions in just a few seconds.
Chapter 28: All in One: Script to
Create Calendar Table or Date
Dimension using DAX in Power BI
I have written an article with the full script of generating a date dimension
in Power BI using the Power Query script, and I would always recommend
that as the first choice. However, sometimes, you want this to be in DAX
way, so I explained how to create a fully-fledged date table using a DAX
script in this chapter.
Script
Download the script for the date dimension from this book’s code file.
Configuration
You need to configure the Date table based on your need. The first few lines
are the configurations that you can set based on your requirement;
set the start and end year, and the starting month of the fiscal year
Considerations
There are a few things you need to consider if you are using this script;
This Date dimension does NOT include public holidays
information. If you wish to get that, use this
approach[https://radacad.com/create-a-date-dimension-in-
power-bi-in-4-steps-step-3-public-holidays].
This Date dimension is not supporting scenarios with fiscal
weeks. For those scenarios, some changes need to be applied
to the script.
If you want to use this date dimension in multiple Power BI
files, consider using the Power Query version
[https://radacad.com/all-in-one-script-to-create-date-
dimension-in-power-bi-using-power-query]and a dataflow
entity for the date dimension.
Chapter 29: Day of Year and Day
of Quarter – DAX calculations for
Power BI
Power BI has some built-in, easy-to-use DAX functions to get the Day of
Month and Day of the week, but nothing for Day of Year and Day of the
quarter. These calculations, however, are straightforward to implement
using other functions. In this short chapter, I explain a method to calculate
those for Power BI.
Day of Year
A straightforward way of calculating Day of Year is to get the date
difference of that date with the starting day of that year. Here is how it
works:
Day of Year = DATEDIFF(STARTOFYEAR('Date'[Date]),'Date'[Date],DAY)+1
This code will give us the day number of the year for a given date.
‘Date'[Date] means the column named “Date” under the table named
“Date”.
Day of Quarter
You can use the same method and get the date difference of the given date
with the starting date of that quarter.
Day of Quarter = DATEDIFF(STARTOFQUARTER('Date'[Date]),'Date'[Date],DAY)+1
calculate the number of days until the end of the quarter using DAX
If you don’t have a date table with a column that represents the day of the
week name (Saturday, Sunday, Monday, etc.) or number (from zero to six,
or from one to seven), and you have a date field, which you want to get the
day name of the week quickly, here is a quick trick for you.
Sample Table
The below DAX calculated table is a table with a list of dates in it;
calendar = CALENDAR(
DATE(2020,1,1)
,TODAY()
)
Three Characters
If you just want the three-character day of week name, you can use “ddd” in
the format function as below;
3 characters =
FORMAT(
'calendar'[Date],
"ddd"
)
Many other format strings are helpful to get other date or time attributes.
You can find them all here[https://docs.microsoft.com/en-us/dax/custom-
date-and-time-formats-for-the-format-function].
You can then use a function such as INT if you want to achieve the number:
INT(FORMAT('Date'[Date],"q"))
The \ in the format string above is an escape character, which means this Q
after the \ will be an actual Q letter rather than the q that the quarter number
will replace.
the output is:
RoundUp
In this scenario, you can also use the roundup function to achieve the same
thing with the same approach:
ROUNDUP( MONTH('Date'[Date])/3 ,0)
You need one extra step because the round might end up with zero for some
values. You first have to add one to the month value and then do the divide
and rounding. The output is the same.
and EndOfQuarter:
ENDOFQUARTER('Date'[Date])
In this chapter, first, I explain what time intelligence is and the requirements
for setting up time intelligence calculations. I will talk about DAX
functions and expressions that help get insights such as year to date, year
over year comparison, etc.
Prerequisite
To run examples of this chapter, you would need to have the
AdventureWorksDW excel file dataset. The table we are using is only one
table: FactInternetSales to be loaded into Power BI.
then you can get the year to date calculation at the monthly level as below;
The year-to-date calculation (for every month) is the accumulated sales of
all months before that (from January of that year).
DatesYTD returns a table as the output, a table with all dates in a year to
date. That is why we need to use a function such as Calculate.
Here is how the calculation works with DatesYTD function;
Sales YTD Method 2 = CALCULATE(
SUM(FactInternetSales[SalesAmount]),
DATESYTD(
FactInternetSales[OrderDate].[Date]
)
)
You may ask which one is a preferred option? TotalYTD or DatesYTD. The
answer depends on the type of filter you are using. If you are using multiple
filter criteria, I would suggest DatesYTD because you can apply whatever
filter you want inside a Calculate. You may be able to do that still with
TotalYTD, but you probably make the expression a bit complicated.
As you can see from the structure, the year-end date is set as month/day.
You can use a few other options, such as 06-30, 6/30, June 30, or 30 June.
“06/30” value as the parameter here means that the end of the fiscal year is
30th of June of each year, and start as the result would be 1st of July of the
year. Here is the output; as you can see, the calculation restarts in July each
year instead of the calendar year, starting from January.
The approach is very similar if you want to use the DatesYTD approach.
Here is the code:
Sales YTD Fiscal Method 2 = CALCULATE(
SUM(FactInternetSales[SalesAmount]),
DATESYTD(
FactInternetSales[OrderDate].[Date],
"06/30"
)
)
)
)
Month to Date Calculation: TotalMTD
You can use the TotalMTD function very similar to the other functions you
have seen in previous examples to calculate the month to date. Here is the
code:
Sales MTD = TOTALMTD(
SUM(FactInternetSales[SalesAmount]),
FactInternetSales[OrderDate].[Date]
)
And the output (note that you can test it better when you have DAY on your
visual to see the accumulation happening);
Month to Date Calculation: DatesMTD
The same approach can be applied for the DatesMTD as below;
Sales MTD Method 2 = CALCULATE(
SUM(FactInternetSales[SalesAmount]),
DATESMTD(
FactInternetSales[OrderDate].[Date]
)
)
More Functions?
As you have seen in this chapter, using Time intelligence functions is not
hard to start. In the following chapters, I’ll explain a few other time
intelligence functions.
Chapter 33: Month over Month
Calculation in Power BI using DAX
When working with dates, one of the common types of analysis is period
vs. period, such as Year over year and month over month. This chapter will
explain how you can use DAX to write calculations for month-over-month
simply in any Power BI report.
The expression above can return the same result for the previous month’s
calculation:
Two functions work very similarly but have a bit different usage;
DatesInPeriod, and DatesBetween. This chapter will show you the
difference between these two functions and scenarios that you can use each.
DatesBetween and DatesInPeriod both give you a period of dates, but let’s
see their main difference.
Sample Dataset
For examples of this chapter, you need the FactInternetSales table from the
AdventureWorksDW Excel dataset.
DatesInPeriod
The DatesInPeriod function in DAX will give you all dates within a period.
The period can be one of these: Day, Month, Quarter, Year. Here is the
syntax of using this function;
DATESINPERIOD(<dates>,<start_date>,<number_of_intervals>,<interval>)
LASTDATE(FactInternetSales[OrderDate].[Date]),-1,YEAR)
)
DatesBetween
DatesBetween function in DAX is a more generic version of DatesInPeriod.
You have more flexibility with this function. With this function, you do not
need to worry about the interval or number of intervals. This function will
give you all the dates between a start date and an end date. Here is the
syntax of this function;
DATESBETWEEN(<dates>,<start_date>,<end_date>)
Parameters are:
<dates>: The date field (like many other time intelligence
functions, this function also requires a date field)
<start_date>: The start date that period starts from it (unlike
DatesInPeriod, this cannot go backward from the start date. It
always go forward from there)
<end_date>: The end date that period ends there.
The output of this function is a table. The table includes dates from the
start_date to the end_date, including both start and end dates.
An important understanding of this function is that the function itself
doesn’t go back or forth from the start date to give you the period. You have
to calculate the start or the end date first and then get the period based on
that. For example, we want to calculate dates in the last rolling year from
the current date in the filter context (similar to the example we have done
with DatesInPeriod). You need first to find out what your start date is.
DATEADD(LASTDATE(FactInternetSales[OrderDate].[Date]),-1,YEAR)
DATESBETWEEN(
FactInternetSales[OrderDate].[Date],
DATE(2007,8,1),
DATE(2007,11,16)
)
)
DatesInPeriod vs DatesBetween
Now let’s see if we use the DatesBetween for calculating the period and get
the start and end of that period what we get as a result;
First DatesBetween =
FIRSTDATE(
DATESBETWEEN(
FactInternetSales[OrderDate].[Date],
DATEADD(LASTDATE(FactInternetSales[OrderDate].[Date]),-1,YEAR)
,LASTDATE(FactInternetSales[OrderDate].[Date])
)
)
LASTDATE(
DATESBETWEEN(
FactInternetSales[OrderDate].[Date],
DATEADD(LASTDATE(FactInternetSales[OrderDate].[Date]),-1,YEAR)
,LASTDATE(FactInternetSales[OrderDate].[Date])
)
)
Another difference between these two is the input parameters that you have.
Sometimes, you have the start and end date, and you want to get all dates in
that period. DatesBetween is an excellent function to use in this situation.
Sometimes, you do not have both ends of the period; you have one and the
interval. In that case, DatesInPeriod is your best friend. There are many
scenarios that you can use DatesBetween and DatesInPeriod instead of the
other one.
If you have the start and end date, and you want to get all
dates in that period, DatesBetween is an excellent function
to use. However, Sometimes, you do not have both ends of
the period; you have one and the interval; in that
case, DatesInPeriod is your best friend.
Summary
DatesBetween and DatesInPeriod are DAX functions to give you a period
of dates. DatesBetween gives you dates from a start date to an end date.
DatesInPeriod will provide you with an interval of dates from a particular
period. Each function has its usages. You can tweak and change your
expressions with each function to get the same result as the other function
(like anything else in DAX!). However, these two functions will give you
good power in different situations of calculating a period.
Chapter 35: DateAdd vs
ParallelPeriod vs
SamePeriodLastYear; DAX Time
Intelligence Question
Using DAX time intelligence functions for a while; you may ask this
question from yourself that what is the difference between functions below;
SamePeriodLastYear function vs. using ParallelPeriod with
Year parameter
ParallelPeriod for a month vs. DateAdd for a month ago
and many other questions that lead to this final question:
Which function should be used in which situation?
Let’s take a look at these questions and their responses in more detail
through this chapter.
SamePeriodLastYear
Let’s start with the SamePeriodLastYear function; this function will give
you precisely what it explains; same PERIOD but last year! Same period; if
you are looking at data on the day level, it would be the same day the
previous year. If you are slicing and dicing in a month or quarter level, this
will give you the same month or quarter in the last year. You can use the
function simply just by providing a date field:
SamePeriodLastYear(<date field>)
the image below shows how the SamePeriodLastYear works for Date
For every month, the ParallelPeriod expression will return a month before
that, because in the parameters, we mentioned the month before:
PARALLELPERIOD(DimDate[FullDateAlternateKey].[Date], -1, MONTH )
ParallelPeriod can be used to fetch the Sales of last month like this:
Parallel Period -1 Month = CALCULATE(
SUM(FactInternetSales[SalesAmount]),
PARALLELPERIOD(
DimDate[FullDateAlternateKey].[Date],
-1,
MONTH)
)
As you can see in the above screenshot, ParallelPeriod will return sales of
the entire last month, even if you are looking at the day level. This brings us
to an important conclusion:
DateAdd
DateAdd is a function that adds or subtracts some
days/months/quarters/years from or to a date field. DateAdd can be used
like this:
DateAdd(<date field>, <number of intervals>, <interval>)
DateAdd used in a example below to return the period for a month ago.
DateAdd can be used in a Day level too. This brings us to the first
difference of ParallelPeriod and DateAdd;
DATEADD(
DimDate[FullDateAlternateKey].[Date],
-1,
DAY))
DateAdd vs ParallelPeriod
Comparing these two functions with each other, you can see that DateAdd
works on the period dynamically (like SamePeriodLastYear), but the
ParallelPeriod works statically on the interval mentioned as the parameter.
That leads us to conclude that DateAdd(<date field>,-1, Year) is similar to
SamePeriodLastYear. However, one difference is still there:
DateAdd vs SamePeriodLastYear
SamePeriodLastYear only goes one year back. DateAdd can go two years
back or even more. DateAdd is a customized version of
SamePeriodLastYear.
Conclusion
In summary, there are differences between these three functions:
DateAdd and SamePeriodLastYear both work based on the
DYNAMIC period in the filter context
ParallelPeriod is working static, based on the interval selected
in the parameter
ParallelPeriod and DateAdd can go more than one interval
back and forward, while SamePeriodLastYear only goes one
year back.
DateAdd works on the interval of DAY and month, quarter,
and year, but ParallelPeriod only works on month, quarter, and
year.
Depends on the filter context, you may get a different result
from these functions. If you get the same result in a year-level
context, it doesn’t mean that all these functions are the same!
Look more into the detailed context.
Chapter 36: Same Period Last Year
to Date DAX Calculation in Power
BI
The problem, however, appears when we do not have a full year like below;
In the above screenshot, we have only sales up until July 2008. The same
period last year's calculation at the month level is correct for that period
itself (month level). However, for the whole quarter is not, because if I am
comparing Qtr 3 of 2008, I have one month of sales there (July 2008).
However, in Qtr 3 of 2007, because we have sales of all months (July,
August, and September 2007), the two values are not comparable. This
leads to a wrong year-over-year calculation too.
The Solution
The correct calculation would be finding the last date that we have sales on,
then find the same date but last year, and then calculate the sales of the
same period last year up until that day. Like anything else in DAX, there are
multiple ways of doing this. Here is one method explained below.
Last date of sales
I am using the below expression to find what is the last date that we have
any sales:
var lastdateAvailable=CALCULATE(MAX(FactInternetSales[OrderDate]),ALL(FactInternetSales))
Having the ALL helps me find the last date from the sales table regardless
of the filter context in the visual.
A year before that
Now that we have the last date of the sales, we can go one year back. I use
the below approach;
var lastyearsameday=lastdateAvailable-365
Full expression
Here is the full expression:
Sales SPLY to Date - Considering Leap Year =
var lastdateAvailable=CALCULATE(MAX(FactInternetSales[OrderDate]),ALL(FactInternetSales))
var lastyearsameday=lastdateAvailable-365
var ifLY=IF(DAY(lastyearsameday)<>DAY(lastdateAvailable),TRUE(),FALSE())
var lastyearsamedayLY=IF(ifLY,lastdateAvailable-366,lastyearsameday)
var SPLYUntillastdate=FILTER(
SAMEPERIODLASTYEAR(DimDate[FullDateAlternateKey].[Date]),
DimDate[FullDateAlternateKey].[Date]<=lastyearsamedayLY)
return
CALCULATE(
[Sales],
SPLYUntillastdate)
Introduction
There is a set of functions for calculating Year to Date (TotalYTD), Quarter
to Date (TotalQTD), and Month to Date (TotalMTD). However, for
calculating Week to Date, there is no built-in function. There are many
ways to calculate week to date. One method uses functions such as
DatesBetween and WeekDay to calculate the period between the first day of
the week and the filter context. Let’s see how it works.
Sample Dataset
If you want to use this example, create a Power BI file connected to
AdventureWorks data source and load FactInternetSales, and DimDate into
the model. Create a connection between these two tables based on DateKey
(from DimDate) and OrderDateKey (from FactInternetSales).
This measure would be the day number of the week. Starting from Sunday
as 1, ending Saturday as 7.
Starting from Monday
Not always in all businesses, the week starts from Sunday. In fact, in many
companies, the week begins on Monday. WeekDay function has a second
parameter that can determine the starting day of the week. The parameter
name is Return Type.
CurrentDate,
-1*DayNumberOfWeek,
DAY),
CurrentDate)
DAY),
CurrentDate))
In Power Query, there is an easy way to use Duration and get the number of
days, hours, minutes, and seconds from it. However, sometimes you need
this calculation to be dynamic as a measure in DAX. I had that requirement
too. And I wrote a simple DAX calculation which will give you the result.
The ParallelPeriod is a function that helps you fetching the previous period
of a Month, Quarter, or Year. However, if you have a dynamic range of
date, and you want to find the previous period of that dynamic selection,
then Parallel Period can’t give you the answer. As an example; if the user
selected a date range from 1st of May 2008 to 25th of November 2008, the
previous period should be calculated based on the number of days between
these two dates, which is 208 days, and based on that previous period will
be from 5th of October 2007 to 30th of April 2008. The ability to do such
calculation is helpful for reports that users want to compare the current
period's value with whatever period it was before this. In this chapter, I’ll
show you an easy method for doing this calculation. I will be using one
measure for each step to help you understand the process easier.
Current Period
I will go through this with an example; Create a new Power BI Desktop file
and choose DimDate and FactInternetSales from AdventureWorksDW.
Ensure that there is only one Active relationship between these two tables
based on OrderDateKey in the FactInternetSales table and DateKey in the
DimDate table. Now add a slicer for FullDateAlternateKey on the page
Also, add a Card visual that shows SalesAmount from the FactInternetSales
table.
Date() DAX function returns the first available date in the current
evaluation context, whatever filtered in the date range.
End of Current Period
Same as the start of the period, I will use a simple calculation for the end of
the period, but this time with LastDate() to find the latest date in the current
selection.
End of This Period = LASTDATE(DimDate[FullDateAlternateKey])
I add them all in the report as Card Visuals (one for each measure), and here
is the result so far;
Previous Period
After finding the number of days in this period, start, and end of the current
period, it is a simple calculation to find the previous period. The previous
period calculation should be the number of days in this period minus the
start of the current period. To exclude the start of the period to calculate
twice, I’ll move one more day back. Here is the calculation step by step. I’ll
start with the Start of the Previous Period;
Start of Previous Period
Using DateAdd to reduce the number of days from
DimDate
DATEADD(DimDate[FullDateAlternateKey],-1*[Days in This Period],DAY)
To exclude the current date from the selection, we always move one day
back. That’s what PreviousDay() DAX function does. It always returns a
day before the input date.
These are not three separate DAX expressions or measures. This is only one
measure which I explained step by step. Here is the full expression:
Start of Previous Period =
PREVIOUSDAY(FIRSTDATE(DATEADD(DimDate[FullDateAlternateKey],-1*[Days in This
Period],DAY)))
Now, if I add all of these measures to the report with card visuals again, I
can see previous period calculation works correctly;
With every change you apply in the date range slicer, you can see the
previous period calculates the range again. It will always be the same
number of days as the current period, but the same number of days
BEFORE. In the screenshot above, you can see that the start of the previous
period is 321 days before starting this period. One more day because the
end of the previous period is not exactly the start of this period. It is one day
before. We don’t want to duplicate values of date in current and previous
calculations.
Summary
This chapter showed you a useful DAX calculation to find Dynamic
Previous Period based on the date range selection in the Power BI report
page. I have used number of DAX functions such as FirstDate(),
LastDate(), DateAdd(), DateDiff(), and PreviousDate() to do calculations.
Calculation logic calculates the number of days in the current period and
reduces it from the start and end of the current period to the previous
period.
Part 7: Table manipulation
functions
Chapter 40: Creating a Table in
Power BI Using DAX Table
Constructor
There are some functions in DAX that are useful in particular scenarios. For
example, sometimes, you might want to create a table entirely in DAX. If
you're going to do that, what are your ways, and how is it possible? This
might be helpful, especially in the first days of learning about DAX. Let’s
see how the table constructor can help you to do that.
Table Constructor
Table constructor is not a function in DAX. It is a set of characters which
you can create a table in DAX by them. Table instructor is always
surrounded by {} characters. The syntax of the table constructor is simple.
It is like below:
{<value1>,<value2>...}
This means value1 will be the value of the first column in the table, value2
would be the value of the second column, etc.
if you want to have more rows, you can separate them with
parenthesis () and a comma, like this:
{
(value1,value2,...),
(value1,value2,...)
Let’s Experiment
Create a new Power BI Desktop file. And then, in the Modeling tab, click
on New Table.
This will create a table called Sample Table, with one single column called
“Value”, and the value in the only row for that would be 1. The Value
column automatically takes the data type of the Whole Number.
If you use an expression like this with parenthesis;
Sample Table = {(1)}
You would still get the same output:
If I want a table with two or three rows, I can try this expression:
Sample Table = {1,2,3,4}
As you see, even the comma separates the rows. However, what if we want
to have more columns? In that case, you need to use parenthesis to bundle
them together in one row. Like this:
Sample Table = {(1,2,3,4)}
If you want to have multiple rows and columns in the table, you can write
an expression like this:
Sample Table = {(1,2),(3,4)}
This will create a table with two columns: value1, and value 2, and two
rows as below;
And Power BI automatically sets the columns data types to Whole Number,
Text, and DateTime;
With the table constructor, you can have different data types in each
column, but then, Power BI converts all values to a common data type. Like
below example;
Sample Table = {
(1,"the first row",DATE(2019,1,1)),
(3,"the second row",12),
Limitations
When you are using constructors, you can put any values you like as the
values of cells and rows. However, all rows should have the same number
of columns. For example, the below expression won’t work:
Sample Table = {
(1,"the first row",DATE(2019,1,1)),
(3,"the second row",),
(3,"the second row","something")
}
And that is why you get the error: Each tuple in the table constructor must
have the same number of columns.
If you want to pass a cell with no value, you can either leave that part blank
or use the BLANK() function. Like the below example:
Sample Table = {
(1,"the first row",DATE(2019,1,1)),
(3,"the second row",),
(3,"the second row","something")
}
Column names are always Value1, Value2, Value3, etc., and you cannot
change them in the table constructor. You can, however, change it afterward
by just renaming it or even using the SelectColumns function. Data types of
columns are defined automatically, which you can then go and adjust
manually. Because of these two limitations, I’d instead use
the Datatable function in DAX, giving us more options and flexibility.
Summary
Table constructor is a fast and straightforward way of creating tables in
DAX if you need it. However, this method has some limitations on the
column names and the data types, making the Datatable function a better
replacement if you want more customization.
Chapter 41: Using DataTable DAX
Function for Creating Structured
Table in Power BI
In the previous chapter, you learned how easy it is to use a table constructor
in DAX to create a data table fast in Power BI. However, that method has
some limitations, such as not naming columns or setting their data types.
This chapter will explain the DataTable function in DAX, giving us more
flexibility to set column-specific structures. Let’s see how this can be used.
DataTable Function
Datatable is a function in DAX to create a table. The syntax allows you to
define each column name and data type and then add data values. Here is
the structure of a Datatable function usage:
Sample Table = DATATABLE(
"column 1 name",<data type>,
"column 2 name",<data type>,
{
{<value row 1 col 1>,<value row 1 col 2>},
{<value row 2 col 1>,<value row 2 col 2>}
}
)
The minimum things you need for this function to work is at least one
column, plus one row, which can be used like this:
Sample Table = DATATABLE(
"First Name",STRING,
{
{"Reza"}
}
)
The part inside {} is the data rows. And each row itself is in another {}. So,
if I want to add two rows, it would be like this:
Sample Table = DATATABLE(
"First Name",STRING,
{
{"Reza"},
{"Leila"}
}
)
To add more columns to this table, you can just add another line of name
and data type before the first {}. Similar to below;
Sample Table = DATATABLE(
"First Name",STRING,
"Last Name",STRING,
{
{"Reza"},
{"Leila"}
}
)
However, the above expression won’t work like that because we still have
one value in every row.
You need to have the same number of values in each row. However, the
blank itself is also considered as value. Blank can be represented with
BLANK() or with no value in a place holder.
Sample Table = DATATABLE(
"First Name",STRING,
"Last Name",STRING,
{
{"Reza","Rad"},
{"Leila","Etaati"},
{"someone",},
{"Unknown",blank()}
}
)
You can see that both 3rd and 4th rows are blank in their “Last Name”
column but defined differently in the expression.
The data type of each column can be one of the below types: Integer,
Double, String, Boolean, Currency, and DateTime
If a value is specified in a data value that is not of the column's data type,
the type conversion will happen. Like what you see below that occurs on
the “0” in the 3rd column of the first row;
Sample Table = DATATABLE(
"First Name",STRING,
"Last Name",STRING,
"Time",DATETIME,
{
{"Reza","Rad",0},
{"Leila","Etaati",},
{"someone",,"2019-2-10"},
{"Unknown",blank(),"2019-2-10"}
}
)
You can specify column names with special characters in them, such as
below. However, if you want to use ” (double quote) in your column name,
you need to use another double quote as an escape character for it.
Sample Table = DATATABLE(
"1First Name",STRING,
"@Last Name",STRING,
"""Time",DATETIME,
{
{"Reza","Rad",0},
{"Leila","Etaati",},
{"someone",,"2019-2-10"},
{"Unknown",blank(),"2019-2-10"}
}
)
"First Name",STRING,
"Last Name",STRING,
"Time",DATETIME,
{
{"Reza","Rad",0},
{"Leila","Etaati",},
{"someone",,"2019-2-10"},
{"Unknown",blank(),"2019-2-10"}
}
),
"Full Name",[First Name]&" "&[Last Name]
)
Limitation
If you use Table Constructor to build a table, you can use any expression as
the value. Here is an example that uses the time now() function as a value in
a data cell using table constructor:
Table = {(now())}
{now()}
}
)
You will get the error: The tuple at index ‘1’ from the table definition of the
DATATABLE function does not have a constant expression in the column at
index ‘1’.
The value in each cell can be only constant, not any scalar expression.
An alternative would be using SelectColumns, AddColumns, or other
methods to get an expression-based column added to the table.
Summary
There are different ways to create a table in Power BI using DAX. Using
the Datatable function will give you flexibility in defining each column
name and data type specifications. At the same time, the table constructor is
just an easy and fast way of creating a data table with no specific metadata
setup. The syntax of the Datatable function is explained in the screenshot
below:
Chapter 42: Some Simple Ways to
Debug Your DAX Measure Code in
Power BI: Debugging Virtual
Tables
I call this virtual table, some others call it with all other different names,
and some event doesn’t call it anything but use it. The fact is that in DAX, it
is very common that you use nested functions. Here is a straightforward
example of the usage of nested tables or virtual tables:
DimDate[FullDateAlternateKey].[Date]))
The part that is not clear for me in the expression is what is the period of
the SamePeriodLastYear(DimDate[FullDateAlternateKey].
[Date])?
Because I don’t see that table, I can use methods like this to get that value:
The First Value
I create a new Measure and also use variables to get the virtual table into
that, then using the FirstDate() function in DAX, I get the first Value of it:
Same Period Last Year Debug =
var vTable=SAMEPERIODLASTYEAR(
DimDate[FullDateAlternateKey].[Date])
return
FIRSTDATE(vTable)
Or even better, you can combine the two (first and last value):
Same Period Last Year Debug =
var vTable=SAMEPERIODLASTYEAR(
DimDate[FullDateAlternateKey].[Date])
return
FIRSTDATE(vTable)&" - "&LASTDATE(vTable)
This gives you a clear idea of values in that table because you have the first
and the last value visualized now.
The Row Count
Another helpful function that I use often is the CountRows. You can also
use Count, or CountX, or other variations of it. This gives you information
about how many rows you have in that table.
Concatenated Values
Most of the times, I get what I want with only using the first, the last, and
the count. However, sometimes, you might need to see every individual
value and how they ordered. ConcatenateX is a very good function for that.
It gives you those values as a string concatenated value. like this:
Same Period Last Year Debug =
var vTable=SAMEPERIODLASTYEAR(
DimDate[FullDateAlternateKey].[Date])
return
FIRSTDATE(vTable)&" - "&LASTDATE(vTable)
&" - Row Count: "&COUNTROWS(vTable)
Summary
Inline, nested, or virtual tables in DAX is one of the structures that is a bit
of a challenge to debug. The trick for those is to produce a single value
output of those tables for debug purposes. This method gives you an
understanding of what you have in that table. In this chapter, you have seen
how it is simply possible with simple functions such as FirstDate or
FirstNonBlank, LastDate or LastNonBlank, CountRows, and ConcatenateX
to achieve that goal.
Chapter 43: How to use
AddColumns function in DAX and
Power BI
SUM(FactInternetSales[SalesAmount]),
RELATEDTABLE(FactInternetSales)
)
)
The code above adds a new column to the DimCustomer, named “Total
revenue from the customer”. The expression for this new calculated column
is the CALCULATE part of the expression above.
The above expression can be written as a new table in Power BI;
AddColumn DAX function in Power BI
You can use the AddColumns to add more than one column, like below;
ADDCOLUMNS(
DimCustomer,
'Total revenue from the customer',
CALCULATE(
SUM(FactInternetSales[SalesAmount]),
RELATEDTABLE(FactInternetSales)
),
'Order Count',
COUNTROWS(
RELATEDTABLE(FactInternetSales)
)
)
The code above adds two columns to the DimCustomer table;
Now the result of the expression above is in a table variable. You can use
that to pick the one with the highest random value;
var one_Customer=TOPN(1,customers,[Rand],DESC)
As you can see, the TOPN function uses the result of AddColumns (the
customers variable) as the input table.
and finally, we can return the customer’s full name for that one customer;
CONCATENATEX(one_Customer,[FullName])
Summary
AddColumns is a DAX function that returns a table. The returned table
includes all the columns from the input table plus the new calculated
columns. The expression written for the AddColumns will run for every
row in the input table and generates the new column using it.
AddColumns is a table manipulation function, it does not change the
existing rows and columns, but it adds new columns.
AddColumn in DAX and Power BI adds new columns to the existing table
AddColumns can be used to create a calculated table. But the primary usage
of that is inside measures to add columns to a virtual table.
Chapter 44: Create a subset of the
table in Power BI and add
calculations using
SELECTCOLUMNS DAX
Function
SELECTCOLUMNS(
DimCustomer,
'Firstname',[FirstName],
'last name',[LastName]
)
The code above gives us a subset of the DimCustomer column with the two
columns of FirstName and LastName. but the two columns’ names can be
edited through the formula as you see.
Basics of SelectColumns DAX function in Power BI
In the example above, the column names only changed, but not the
expression. Expressions are merely the column value itself. However, you
can use an expression that changes the value like below;
select col example with calculation =
SELECTCOLUMNS(
DimCustomer,'Full Name',DimCustomer[FullName],
'Revenue',
CALCULATE(
SUM(FactInternetSales[SalesAmount]),
RELATEDTABLE(FactInternetSales)
)
)
The code above will generate a table with the same number of rows as the
DimCustomer, but only with the full name column and a new calculated
column of Revenue.
Summary
SelectColumns and AddColumns are very much the same. SelectColumns
is a tabular function that returns a table with a subset of columns (but the
same number of rows) from the original table. It may have additional
calculated columns in it. SelectColumns can be used instead of
AddColumns in many scenarios. And one of the most use-cases of this
function is when used inside another function to create a virtual table to
help the final calculation of a measure.
Chapter 45: TOPN DAX Function:
How it works in Power BI?
Comparison against the top group
TOPN is a function in DAX that allows you to select the top items from a
table based on an expression. In this chapter, I’ll explain how to use the
TopN function in DAX either to create a calculated table or to use it in a
measure to achieve analysis such as; comparison with the average amount
of the top group.
DimProduct,
CALCULATE(sum(FactInternetSales[SalesAmount]))
)
Using TopN to create the top 10 products based on their sales in Power BI
The input table of the TOPN function can be any function that returns a
table as well. for example, the expression below is the top two product
colors based on the sales;
TopN 2nd example =
TOPN(
2,
VALUES(DimProduct[Color]),
CALCULATE(SUM(FactInternetSales[SalesAmount]))
)
Using virtual tables with TOPN
The example above doesn’t show the sales amount, so there isn’t an easy
way to test it. We can use a function such as Summarize to build a virtual
table with the Sales amount and then get the top items.
TopN 3rd example =
TOPN(
2,
SUMMARIZE(
DimProduct,
DimProduct[Color],
'Sales',
SUM(FactInternetSales[SalesAmount])
),
[Sales]
)
The whole Summarize section is used as the input table to the TOPN
function.
Get the top items with their sales amount using TOPN and Summarize
Bottom N
There are no function names as BottomN, but you can simply change the
order in the TOPN to ASC, and then you will get BottomN, as below;
Get bottom rows from a table using TOPN function in Power BI
DimProduct[Color],
'Sales',
SUM(FactInternetSales[SalesAmount])
),
[Sales]
)
Then we can calculate the average from the outcome of that as below;
var averageOfTopGroup=AVERAGEX(topGroup,[Sales])
This average then can be used in a visual as a target. Here is the whole
expression:
TOPN used to dynamically calculate the average amount of top items in Power BI
This can be even used further to do conditional formatting with the help of
DAX with the measure below;
Back Color based on top group =
var topGroupValue=[Target High (average sales of top colors)]
var selectedColorSale=SUM(FactInternetSales[SalesAmount])
var SalesVsTarget=DIVIDE(selectedColorSale,topGroupValue)
return
SWITCH(
TRUE(),
SalesVsTarget>=1,'Green',
SalesVsTarget>=0.7,'Orange',
SalesVsTarget>=0.45,'Red',
'Black'
)
And the result is a table visual in both the target and the conditional
formatting calculated dynamically based on the average of the top 3 colors.
Summary
TOPN is a beneficial function when a calculation is required based on top
or bottom items in a list based on an expression. TOPN is a tabular
function, but if used in a measure, it gives a dynamic calculation possibility
which is helpful to create reports like above. TOPN can be used with the
ascending order to get the bottom rows as well. And TOPN will bring all
ties if they fit in the top items.
Chapter 46: Building a Virtual
Relationship in Power BI – Basics
of TREATAS DAX Function
The reason, of course, is not having the relationship between the two tables:
DimCustomer and FactInternetSales.
Now, to understand the TreatAs function, let’s see how the structure of
function usage is;
TreatAs(<expression>,<column 1>,<column 2>…)
TREATAS(VALUES(DimCustomer[CustomerKey]),FactInternetSales[CustomerKey])
We connected the two tables using the CustomerKey. We are saying
that TREAT DimCustomer[CustomerKey] AS FactInternetSales[Custome
rKey]. It means to filter the FactInternetSales[CustomerKey] as of it is
DimCustomer[CustomerKey]. Or in other words; If
DimCustomer[CustomerKey] is filtered to show only CustomerKey XYZ,
then FactInternetSales[CustomerKey] would also be filtered to show only
CustomerKey XYZ. It is the same concept of having a relationship, but let’s
say a virtual relationship.
The expression above will return the same result as Values in the visual
mentioned above.
but you cannot just say as below:
TREATAS(DimCustomer[CustomerKey],FactInternetSales[CustomerKey])
This will give you an error that you cannot use a column name in the
expression that expects a table expression.
CALCULATE(
COUNTROWS(DimCustomer),
TREATAS({'High School'},DimCustomer[EnglishEducation])
)
and the result will be the count of all customers with their EnglishEducation
as High School;
The {“High School”} is a single value table (single row and single column)
that filters the DimCustomer[EnglishEduction].
TreatAs is helpful for filtering, and it doesn’t filter only based on one
column. It can filter based on as many columns as you want. One of the
challenges in Power BI relationships is creating a relationship based on
multiple fields. I have explained in a blog article a method you can use to
create a compound key and use that for the relationship. Another approach
is to use TreatAs. Let’s see how TreatAs can help in that scenario.
Sample Model
The sample model that I have includes two tables, one for Rating of Movies
and one for Sales of Movies. There is no relationship between the two
tables. The reason is that these two tables should be related based on two
fields: Title and Year in each table;
The table expression should return precisely the same number of columns
that we refer to in TreatAs. I need the table expression to replace the two
columns Title and Rating from the Rating table, and then use the two
columns Title and Rating from the Sales table as parameters of TreatAs
columns.
Before using TreatAs, if I filter the Lifetime Gross field in the Sales table
by the Title and Year from the Rating table, I will get something like the
below;
Because there is no relationship to filter[https://radacad.com/back-to-basics-
power-bi-relationship-demystified] the Sales table, it shows the total value
regardless of the Title and Year. We need to say that the Title and Year
columns of the Rating table can filter the Title and Year columns of the
Sales table.
I can write a measure like below, but it won’t work;
The rating table includes both the Title and Year columns. However, it also
consists of a few other columns, as you see below:
So the Rating as a table expression returns four columns, but I just need
two; one for Title and one for Year.
The above expression will only return a table with two columns; Title and
Year. This table can be used as the table expression of TreatAs function like
below;
LifeTime Gross Using TreatAs =
CALCULATE(
SUM(Sales[Lifetime Gross]),
TREATAS(
SELECTCOLUMNS(
Rating,
'Title',Rating[Title],
'Year',Rating[Year]),
Sales[Title],
Sales[Year]
)
)
To understand how this works, I have explained it through the shape below;
The SelectColumns expression returns a table with only two columns: Title
and Year. Then the values of this table are used to filter the values of
subsequent columns of Title and Year from the Sales table. The order of
columns should be the same. You cannot have a table returning Title, Year,
and then filter the Year, Title with it. You probably won’t get any results
with that combination. The name of the columns is not important. The
values in each column are.
The result of the expression below is as below:
Even though there is no relationship between the two tables, using TreatAs,
we created that relationship for this measure using the two columns; Title
and Year. You see some blank values in the result. That is because not every
movie that is in the Rating table exists in the Sales table.
So I sum up the learning of this chapter for you:
The table expression should return a table with the same
number of columns with the same order of the columns used
in the list of columns for the TREATAS function.
Last but not least, this chapter was explaining the functionality of
TREATAS for learning. It is not recommended, though, to create a
relationship like this; I always recommend scenarios like this to create a
shared dimension[https://radacad.com/creating-a-shared-dimension-in-
power-bi-using-power-query-basics-and-foundations-of-modeling] and use
that for filtering both tables, like what I
explained here[https://radacad.com/creating-a-shared-dimension-in-power-
bi-using-power-query-basics-and-foundations-of-modeling]. Another thing
that I recommend is to use compound
keys[https://radacad.com/relationship-in-power-bi-with-multiple-
columns] to create a relationship like what I
described here[https://radacad.com/relationship-in-power-bi-with-multiple-
columns].
Chapter 48: Age Banding in Power
BI Using TREATAS DAX Function
– Relationship Based on Between
Sample Model
I am using a straightforward data model in this example. The table below is
what I use as the Sample Data;
Sample Data =
DATATABLE(
'First Name',STRING,
'Last Name',STRING,
'Age',INTEGER,
{
{'Reza','Rad',40},
{'Mick','Peterson',34},
{'Joe','White',23}
}
)
The goal is to have an age group banding for customers and get a count of
customers in each group. Something similar to this:
Other Methods for Banding
You can use Power Query with a conditional column to create banding or
use the grouping and binning option in Power
BI[https://radacad.com/grouping-and-binning-step-towards-better-data-
visualization] to achieve the same. Here, in this chapter, however, I will
explain how that is possible through a measure using the TREATAS
function.
When you see ten as the band up there, it means from 1 to 10. When you
see 20, it means from 11 to 20 and so on.
This table shouldn’t have a relationship with the Sample Data table because
if you create the relationship, it would only filter data for the top value of
each band. So the tables remain unrelated, like a standard way of using
a What-if parameter table.
The expression can be split into multiple sections. First is the variable that
fetches the current age band (the age band in the visualization’s filter
context);
var _currAgeBand=SELECTEDVALUE('Age Band'[Age Band])
Then the next variable is a list (table) of values from the selected age band
minus nine to the value itself, increasing one at a time. For example, if the
age band value is 40, this list would be from 31 to 40: 31, 32, 33, …., 40.
var _currAgeList=GENERATESERIES(
_currAgeBand-9,_currAgeBand,1)
Now that we have a list of possible age values for this band, we can use that
to filter the Sample Data table using TREATAS;
CALCULATE(
COUNTROWS('Sample Data'),
TREATAS(_currAgeList,'Sample Data'[Age])
)
Altogether, this works like a scenario that you have created a relationship
between the Age Band table and the Sample Data but on a BETWEEN
condition, not an exact equal condition.
This example shows a fascinating use case for TREATAS, creating a
relationship based on not-equal criteria. Relationships in Power BI are
based on equality of values. You cannot create a relationship that says this
value should be less than or equal, or between, or anything like that of the
other value in the other table. However, using TREATAS combined with
other functions, you can do that. I’ll write about this design pattern
separately later in detail.
'Sort Order',INTEGER,
'Start',INTEGER,
'End',INTEGER,
{
{'1-10',1,1,10},
{'11-20',2,11,20},
{'21-25',3,21,25},
{'26-30',4,26,30},
{'31-35',5,31,35},
{'36-40',6,36,40}
}
This chapter will explain how you can use Summarize function for
aggregation and grouping of a data table. Summarize function gives you
more control over how to create your aggregated table with some extra
functions. Let’s see how it works. Creating aggregated tables using DAX
functions is particularly very useful when creating virtual tables inside
DAX measures.
Sample Dataset
My sample dataset table is DimCustomer as below;
Summarize Function
Summarize is a DAX function that gives you an aggregated result from a
table. This is how you can use Summarize function:
Summarize(<table>,<grouping column>,[<name>,<expression>])
You can have more than one aggregation if you want to. Just add the name
of each column and the aggregation expression.
The above expression, not only create the aggregated result per each
Gender, but it also will have one extra ROW in the table for the totals (all
genders);
The RollUp comes in the place that the grouping column should be, and it
means the grouped results, PLUS the total.
What if Two or More RollUps
Like a matrix way of grouping, if you have more columns inside the
RollUp, Rolling up values (or total calculation, let’s say) goes through them
one by one in the order in which they are written inside the RollUp
function.
Summarize - with Two Rollups =
SUMMARIZE(
DimCustomer,
ROLLUP(DimCustomer[Gender],DimCustomer[EnglishEducation]),
'Row Count',
COUNT(DimCustomer[CustomerKey]))
This means that after doing all the grouping, roll up on EnglishEduction
first, but with the grouping on Gender (highlighted green below with the
number 1), and then roll up on Gender (highlighted yellow below with the
number 2);
Changing the order of using columns inside RollUp will change the result
of roll-up columns.
RollUpGroup
RollUpGroup can be used similarly to RollUp for bringing the totals and
sub-totals into the aggregated results. If we replace the RollUp with
RollUpGroup in the previous expression, we get precisely the same result;
Summarize - with Two RollupGroups =
SUMMARIZE(
DimCustomer,
ROLLUPGROUP(
DimCustomer[Gender],
DimCustomer[EnglishEducation]
),
'Row Count',
COUNT(DimCustomer[CustomerKey]))
So, you can use either RollUp or RollUp Group to get totals and subtotals.
Preventing Subtotals: Combining RollUp
and RollUpGroup
One of the main usages of RollUpGroup, is to combine it with RollUp and
use it as a parameter inside the RollUp function. This will lead to the
removal of subtotal values and only showing the totals.
In the expression below, you can see that the RollUpGroup is used inside
the RollUp function;
Summarize - with Rollup and Group =
SUMMARIZE(
DimCustomer,
ROLLUP(ROLLUPGROUP(DimCustomer[Gender],DimCustomer[EnglishEducation])),
'Row Count',
COUNT(DimCustomer[CustomerKey]))
'Total',ISSUBTOTAL(DimCustomer[EnglishEducation])&&ISSUBTOTAL(DimCustomer[Gender]))
The result would have three columns showing where is the subtotal and
where not
Each IsSubtotal used inside a new column, and if the result row is a subtotal
on that field, then it returns true for that row.
As an example, If you want to calculate the percentage of the count of
customers against the total for every row, but not for subtotal, you can do
this:
07 Summarize - with IsSubtotal for % calc =
var _allCustomers=COUNTX(DimCustomer,DimCustomer[CustomerKey])
return
SUMMARIZE(
DimCustomer,
ROLLUP(DimCustomer[Gender],DimCustomer[EnglishEducation]),
'Row Count',
COUNT(DimCustomer[CustomerKey]),
'%',if(
NOT(ISSUBTOTAL(DimCustomer[EnglishEducation])||ISSUBTOTAL(DimCustomer[Gender]))
,DIVIDE(COUNT(DimCustomer[CustomerKey]),_allCustomers)
),
'Gender Subtotal',ISSUBTOTAL(DimCustomer[Gender]),
'Education Subtotal',ISSUBTOTAL(DimCustomer[EnglishEducation]),
'Total',ISSUBTOTAL(DimCustomer[EnglishEducation])&&ISSUBTOTAL(DimCustomer[Gender]))
There are many different ways you can create aggregations in Power BI.
You can do it in the source (using the database t-SQL language) or using
Group By operation in Power Query. You can also do it in DAX using some
functions. One of the functions that can be used for grouping and
aggregation is Group By. This chapter is about how to use Group By in
DAX. Creating aggregation using DAX is a very useful skill because you
can use it to create virtual tables in your measures and have better dynamic
calculations in Power BI.
Sample Data
My sample dataset table is DimCustomer as below;
GroupBy Function
GroupBy DAX function can be used as below:
GROUPBY( <table>, <grouping column1>, [<output aggregation column name>, <expression for
aggregation column>]…)
In the above example, the table is DimCustomer, and the Grouping happens
on the EnglishEducation column of that table. The result is the grouped list
of EnglishEducation, which is the same as DISTINCT or VALUES
functions. If you want a distinct list, you might use one of the other two
functions rather than GroupBy. Using the GroupBy function usually comes
with a new column which is the aggregated result.
COUNTX(
CURRENTGROUP(),
DimCustomer[CustomerKey]
)
)
Power Query is often the engine used for combining data tables, especially
using Merge or Append[https://radacad.com/append-vs-merge-in-power-bi-
and-power-query]. However, sometimes, you might need to do that
operation in DAX. An example is when you want to create that combination
only virtually as part of a measure calculation that evaluates dynamically.
This chapter will explain three DAX functions and their meanings: Union,
Except, and Intersect.
Sample Data
I have two really simple data tables, each with one column: Column 1;
sample data tables
For the operations below, each table can have more than one column.
However, I keep it simple to understand.
Union
If you want to have all the data rows of the two tables appended to each
other, you can use the UNION function in DAX. This function simply gets
the two input tables and returns the appended result.
UNION(Table1,Table2)
Intersect
Intersect only returns the rows that exist in both tables. All of those rows
that exist in only one of the tables will be removed from the resultset. This
is how you can use Intersect;
INTERSECT(Table1,Table2)
As you can see, the syntax that INTERSECT and UNION are used are
precisely the same. The same rule applies to EXCEPT as well. For these
three functions, you just need two input parameters; the two tables.
Intersect function in DAX
Except
For the UNION and INTERSECT, the order of passing the tables to the
function doesn’t matter (the only impact would be the final order of items in
the result set). However, for the Except, the order of tables is important.
If you want all rows from table1 that does not exist in table2, then you can
write as below;
EXCEPT(Table1,Table2)
Important considerations
In all of the functions above, you need two tables to have the same
structure. The exact structure means the same number of columns. The
matching is based on the position of the column in the table.
If you use the techniques above to create a calculated table, I strongly
recommend you look at Append and Merge[https://radacad.com/append-vs-
merge-in-power-bi-and-power-query] transformations in Power Query.
Often they can be a much better option if the purpose of this work is
transformation. Only use it in DAX if you are targeting a dynamic combine
approach.
Chapter 52: Creating a List of
Numbers or Dates in Power BI
using GenerateSeries Function in
DAX
If you ever need to create a list of numbers (either decimal or whole number)
or a list of dates and times, a straightforward and useful function in DAX
helps. GenerateSeries is a simple function to use to create a list. In this
chapter, I’ll explain how you can use this function.
Table Generators
There are a set of functions in DAX which generates a table. Some of these
functions are from the form of table constructors. I have written about
the Table Constructor in DAX and also the DataTable() function.
There is another set of functions to generate a table. I have written
about Calendar() and CalendarAuto() functions and explained how they
could create a table with a list of dates. This chapter describes
the GenerateSeries() function in DAX and how you can create a table with it.
GenerateSeries
GenerateSeries is a function in DAX that generates a list of values. The list
starts from a Start value and ends at an End value. You can also specify an
increment. However, the increment value is optional, and if you don’t set
that value, the default increment would be 1.
GenerateSeries(<start value>,<end value>,[increment value])
You might, however, need to set the number of decimal place characters to
the correct value to see the effect.
List of Dates
GenerateSeries is not just for numeric values. It also works for date values.
Here is an example:
Sample Table = GENERATESERIES(
DATE(2019,10,1),
DATE(2019,10,15)
)
The default increment is one value, which for the data type of DateTime
means one day. You can, however, change it to weekly or any other
durations with changing the increment:
Sample Table = GENERATESERIES(
DATE(2019,10,1),
DATE(2019,10,15),
7
)
If you are looking for other ways of creating a list of dates, check out the
chapter about Calendar() and CalnedarAuto() functions in DAX.
List of Times
You can also generate a list of Times using the same function;
Table = GENERATESERIES(
Time(1,0,0),
TIME(2,0,0),
1/24/60/60)
Summary
In summary, if you want to create a list of values in Power BI using DAX,
GenerateSeries is an excellent function to do that. It works not only with
numeric values but also with date and time values.
Chapter 53: Create a Table with A
to Z Character Values in Power BI
Using DAX
I have explained that you can use the GenerateSeries function in DAX to
create a list of numbers, dates or times, or even currency values. However,
sometimes you might need to create a list of text values, such as alphabet,
from “a” to “z” lowercase or uppercase. The good news is that you can also
do that with GenerateSeries, and a bit of a trick. Let’s see how it works.
GenerateSeries for Numbers
I explained that you could create a calculated table in Power BI using DAX
expression such as below and get the result as a one-column table;
Sample Table = GENERATESERIES(1,10)
You can use GenerateSeries to create a list of dates, times, and currency
values too.
However, the trick is that every character has a numeric code assign to it in
the Unicode world. The UNICODE function will give you the code of that
character:
for example, an expression like below:
Code of the character = UNICODE("a")
List of Codes
So, now you can simply create a table like this:
Codes = GENERATESERIES(
UNICODE("a"),
UNICODE("z")
)
and you will have the list of all codes in one place:
UNICHAR: Returns the Character of the
Code
The point, however, is not to have the list of codes but to have the list of
characters. You can use the UNICHAR function in DAX to return the
character related to the code.
For example, the expression below;
Character of the code = UNICHAR(97)
GENERATESERIES(UNICODE("a"),UNICODE("z")),
"Character",
UNICHAR([Value])
)
and here is the result, which is the list from “a” to “z”;
All Characters
You can modify the expression a bit and get a list of all primary Latin
characters like this:
Alphabet =
SELECTCOLUMNS(
GENERATESERIES(UNICODE("a"),UNICODE("z")),
"Character",
UNICHAR([Value])
)
Sample Data
I have a sample customer table as below;
Substring
Substring means saying from character indexed N, extract M characters:
Substring (N, M)
This can be implemented in DAX in different ways. This is one of the
methods:
Substring = LEFT(
RIGHT(
DimCustomer[EmailAddress],
LEN(DimCustomer[EmailAddress])-1
),
3)
1, in the expression above, is the starting index. If you want to start from
the beginning of the text, use zero here.
3, in the expression above, is the length of the output from the starting
index.
here is another example:
There is an easier way to do substring too, using MID function;
MID = MID(DimCustomer[EmailAddress],5,7)
Using the MID function, you just specify the starting index and the length
of characters to extract, similar to substring in many other languages.
Reverse Substring
Sometimes you want substring to start from the end of the text. For
example, ReverseSubString (N, M) means to start from N, which is the
index from the right end of the string, and extract M characters. For
example, “Reza Rad”, with ReverseSubstring(3,2), means “Ra”. You can
implement the Reverse substring as below:
Reverse Substring = LEFT(
RIGHT(DimCustomer[FullName],3)
,2)
This method is usually more useful when the value you want to extract is
closer to the end of the string rather than the start.
Chapter 55: Find a Text Term in a
Field in Power BI Using DAX
Functions
In Power BI, there are multiple ways of searching for a text term inside a
text field. You can use Power Query for doing this operation or calculations
in DAX. In this chapter, I’ll explain some functions in DAX that you can
use to do this calculation. Most of these functions can be used inside a
measure for dynamic calculation. In this chapter, you will learn about a few
DAX functions that deal with searching a text term in a text field.
Sample Data
I am using the DimCustomer table from the AdventureWorks excel file, and
only two columns of that, which are CustomerKey and FullName;
FIND
Find is a DAX function that searches for a term inside a text field and
returns the starting position of that item (position index starts from one).
The way that you can use this function is like below:
FIND(<text term to search for>,<the column you are searching into>,[<starting index for search>],
[<result if the text term is not found>])
The above expression searches for the term “A” inside the column
FullName of DimCustomer table, starting from the very first of the value in
that column’s cell, and if it can’t find the value, it returns -1.
The expression above is defined as a column, so as a result, it will run for
every row (however, you can use the FIND function in a measure if you
want). If it cannot find the value, it returns -1, and if it can find it, it returns
the index of that in the text (it returns the first index of that term if it
appears multiple times). For example, The customer's full name “Janet
Alvarez” contains the character “A” as the seventh character in the text, so
the return is 7. However, “Ruben Torres” doesn’t contain “A”, and it returns
-1.
FIND Is Case Sensitive
You might have wondered why the result of the above expression for “Jon
Yang” is still -1, although we have character “a” in there. The reason is that
FIND is a case-sensitive function. There is a difference between the above
expression if you use “A” or “a” in the FIND;
Another thing is that although the last parameter of the FIND is optional, if
you don’t pass a value to it, it returns an ERROR.
SEARCH
Search is very similar to FIND. The only difference is that Search is NOT
case sensitive. There is no difference between “A” or “a” when you use
the Search function.
Search = SEARCH("A",DimCustomer[FullName],,-1)
The above expression uses UPPER to make the FullName’s value all
uppercase, and then compare it with “A”, or you can do lowercase, and then
compare it with “a”.
ContainsString
FIND and SEARCH functions are returning the starting index of the search
term. However, the ContainsString function returns a boolean result that is
that term found in the text or not. The result of this function is true or false.
ContainsString just need to parameters;
ContainsString(<the column you are searching into>,<text term to search for>)
ContainsString is not case sensitive, and it returns true for any of those
values that the Search function returns a value not equal to -1 in our
example.
ContainsStringExact
There is a case-sensitive version of the ContainsString, called
ContainsStringExact. The function can be used similar to the previous one;
Exact
Exact is not a function to search through a text. This is a function to check
the equality of value with a text. The two texts should be exactly the same.
This function is case-sensitive. Exact, get two text values and check if
they are the same or not, the result is a true or false value;
Exact(<text 1>,<text 2>)
All in One
Here is a summary of these functions;
Chapter 56: Search in Power BI
Table Visual Using a Slicer For
Contains Character Criteria
If you have a lot of text in a table visual in Power BI and want to search to
find all texts with a specific character in them, I have a solution for you.
You can have an alphabet slicer and use it to filter the table. The trick is to
combine it with a measure and use it as a parameter table. Let’s see how the
solution works.
The Challenge
I have a table for all customers, showing them all in a table visual in Power
BI. However, there are many customers on the list, let’s say 18K+. If I want
to search for all customers who have “q” in the name, then I need to either
scan the table myself, Or use a slicer with a search box, and search for
character “q”, and then select all the names with “q” one by one! Something
like below is tedious!
This is great, isn’t it? Now that you can see what is expected and can be
done let’s see how you can do that.
Alphabet Table: Parameter Table
I started this by creating a parameter table for the Alphabet. The parameter
table is a table that can be used to filter the result of visualizations, but not
through the relationship, through some DAX measures. You can usually
create a parameter table using What-If parameters in DAX if your
parameter table consists of numeric values. However, in this case, our table
includes a list of characters, so we need to create that ourselves using a
DAX expression:
Alphabet =
SELECTCOLUMNS(
GENERATESERIES(UNICODE('a'),UNICODE('z')),
'Character',
UNICHAR([Value])
)
SELECTEDVALUE(Customer[FullName]),
,-1)
I have used the Search DAX function here. The search function will return
the character index in the text if it finds the value (starting from one), and as
the last parameter I mentioned, “-1” will return -1 if it cannot find the
character in the text. You can use FIND or other functions in DAX to
achieve similar results.
Visual Filtering
Now you can create visualization like below, the slicer value is coming
from the Alphabet table, and the table visual is from the Customer table;
In the visual level filter of the table visual, Add the Exists measure (the
measure we have created in the previous step), and set the filter to “is
greater than or equal to” and type “1” in the text box, and apply filter.
That’s it. This will give you the result below:
I’m sure soon, the Slicer in Power BI will somehow have a feature like this,
but this is a solution you can implement and use until then.
Chapter 57: Search for All the
Texts in Power BI Table Visual
with the First Three Characters
Selected in the Slicer
What if we want to search for the first few characters of a text using a
slicer? that means selecting the first character, seeing all the possible second
characters, selecting from that list, and then all possible third characters.
And the table visual shows all text values with the result of all these
selections. So, the result is this chapter. I’ll show you how this is possible;
Three Slicers
Use the three tables respectively in three slicers as below;
DAX Measures
There is a bit of measure work involved in this solution. A measure that can
check the first, the second, and the third characters, and also measures to
filter the visuals. Below is the list of measures one by one:
First Character Matched
This measure checks if the first slicer's value exists as the first character in
the FullName column of the customer table. If the result of this measure is
1, then it means a match.
First character matched = SEARCH(
SELECTEDVALUE(Alphabet[Character]),
SELECTEDVALUE(Customer[FullName]),
1,
-1)
The second Character Matched
This measure checks if the second slicer's value exists as the second
character in the FullName column of the customer table. If the result of this
measure is 2, then it means a match.
Second character matched =
var _selectedChar=SELECTEDVALUE('2nd Char'[Character])
return
if(ISBLANK(_selectedChar),2,
SEARCH(
_selectedChar,
SELECTEDVALUE(Customer[FullName]),
2,
-1)
)
SEARCH(
_selectedChar,
SELECTEDVALUE(Customer[FullName]),
3,
-1)
)
Now using the three measures above, we create some more measures for
filtering as below;
Second Characters
This measure filters the second character slicer with all possible options
based on the first character slicer selection;
Second Characters =
var _firstchars=
FILTER(
Customer,
[First character matched]=1
)
var _secondchars=
SELECTCOLUMNS(
ADDCOLUMNS(
_firstchars,
'second char',
RIGHT(LEFT(Customer[FullName],2),1)),
'char',[second char])
var _distictcharlist=
DISTINCT(_secondchars)
return
COUNTROWS(
FILTER(
_distictcharlist,
[char]=SELECTEDVALUE('2nd Char'[Character])
)
)
Third Characters
This measure filters the third character slicer with all possible options based
on the first and the second character slicer selections;
Third Characters =
var _firsttwochars=
FILTER(
Customer,
[First character matched]=1 && [Second character matched]=2
)
var _thirdchars=
SELECTCOLUMNS(
ADDCOLUMNS(
_firsttwochars,
'third char',
RIGHT(LEFT(Customer[FullName],3),1)),
'char',[third char])
var _distictcharlist=
DISTINCT(_thirdchars)
return
COUNTROWS(
FILTER(
_distictcharlist,
[char]=SELECTEDVALUE('3rd Char'[Character])
)
)
The three measures filter the table visual; the First character matched, the
Second character matched, and the Third character matched to be equal to
1, 2, and 3, respectively.
The final result is as below;
Summary
Using parameter tables and filtering visuals based on DAX measures, you
can achieve interesting results. I’m sure a filter capability like the above
will be added soon in Power BI. However, until then, this can be a helpful
solution for you. On the other hand, it teaches you how you can combine
parameter tables, DAX, and filtering visuals to get some results out of the
standard possibilities of the tool.
Part 9: Parameter Table
Chapter 58: Power BI What If
Parameter for Getting the Sales of
X months ago: Use Case Scenario
What If Parameters
There are two types of parameters in Power BI; Power Query parameters
and What If parameters. Power Query parameters are used for creating a
dynamic structure for the data transformation phase. Here is an example of
using Power Query parameters for changing the data source. Another
example is creating a custom function to loop through some steps for a
single data structure.
The What If parameters, on the other hand-side, are for end-users. It
empowers them to make changes and see the effect of their changes
immediately on the report. For example, let’s say you have written a DAX
expression that calculates sales of last month. After building the solution
using this calculation and delivering it to your users, they come to you and
ask you that can we have this calculation for two months ago? They come
after a while and ask if we can have it for three months or even six months?
The user is seeking a way to change a calculation by their selection in the
slicer. They want to see what would happen if they change some of the
values. They want to do a What IF analysis. That is precisely why this type
of parameter is called What If parameters. Let’s see that through an
example.
Using the calculation above, you can see that we navigate one month back
using the -1 as the second parameter of the ParallelPeriod function to
calculate the last month's sales.
here is the result of that calculation:
For every month, the value of this measure would be the sales of the month
before that.
What If X Months?
After delivering a solution with the above calculation to your users, you
will likely get another request: what if I want to see the sales of 2 months
ago? Three months ago, five months, etc. What if I want to see the sales of
X months ago. You don’t expect end-users to go and change the “-1” in the
DAX expression to another number and get the result (even if they do have
the edit access to the report, or they do have Power BI Desktop installed). It
would be best if you gave them the ability to change the “-1” in the
expression above. And they do that by merely changing a value in a slicer.
For this purpose, the data type of the parameter can be the Whole number. I
named it “How Many Months Back”, and set the values as below. Having
the “Add slicer to this page” ensures that there will be a slicer created for
this parameter in the current report page.
After this step, you’ll see a new slicer created and a new table, column, and
measure with this name.
Power BI uses the GenerateSeries DAX function to create the list of values
as a calculated table when you make a parameter.
There is also a measure created with this new table that uses the
SelectedValue function to access the value selected by the slicer. You can
add that measure to a card visual and see how it changes when you change
the slicer value.
Note that in the above expression, we have used the selected value measure
multiplied by -1. The reason is that our parameter is a positive value. Still,
for moving back a few months ago, we need a negative one. You can create
parameters with negative values too. However, it might not look great from
the user's point of view because they have to understand why the slicer
value is negative.
The first parameter of the format function is the value to which we want the
formatting to be applied, and the second parameter is its format. There are
many formatting options available, which are suitable for number, date, etc.
Here is an excellent detailed guide about it;
Pre-Defined Numeric Formats for the FORMAT
function[https://docs.microsoft.com/en-us/dax/pre-defined-
numeric-formats-for-the-format-function?WT.mc_id=DP-
MVP-4030647]
Custom Numeric Formats for the FORMAT
function[https://docs.microsoft.com/en-us/dax/custom-
numeric-formats-for-the-format-function?WT.mc_id=DP-
MVP-4030647]
Pre-defined date and time formats for the
F[https://docs.microsoft.com/en-us/dax/pre-defined-date-and-
time-formats-for-the-format-
function]O[https://docs.microsoft.com/en-us/dax/pre-defined-
date-and-time-formats-for-the-format-function?
WT.mc_id=DP-MVP-4030647]RMAT
function[https://docs.microsoft.com/en-us/dax/pre-defined-
date-and-time-formats-for-the-format-function]
Custom date and time formats for the FORMAT
function[https://docs.microsoft.com/en-us/dax/custom-date-
and-time-formats-for-the-format-function?WT.mc_id=DP-
MVP-4030647]
Now, the FORMAT function can be combined with other methods to make
the dynamic formatting possible.
Parameter table
The parameter table is a disconnected table from the rest of the model. This
table can act like a parameter for other calculations. Here in the example
below, I showed how it could be used to select between measures:
Now, these two methods can work together to build dynamic formatting.
The code above checks what value is selected from the Currency table
(using the SELECTEDVALUE function) and then uses it inside a
conditional expression and assigns the equivalent currency's format string to
it (using the SWITCH function). Finally, this format string is used inside a
FORMAT function to format the measure’s value.
The below screenshot is a glimpse of the result;
'.'&_decimalplaces)
return
FORMAT(_sales,
SWITCH(
SELECTEDVALUE('Currency'[Currency Format],'USD'),
'USD','$',
'GBP','£',
'Euro','€'
)&_NumericFormat
)
This means that now we can have dynamic formatting like below in Power
BI. This allows enabling or disabling the thousand-separator, removing or
adding decimal places, and currency change.
dynamic formatting in Power BI
Consideration
A critical consideration of using this method is that the return value of your
measure or column would be of the TEXT data type (within the format
string defined).
Part 10: Parent-Child Functions
Chapter 60: Parsing Organizational
Hierarchy or Chart of Accounts in Power
BI with Parent-child Functions in DAX
Introduction
Organizational charts or the chart of accounts are specific types of
hierarchy. Because it is not usually apparent how many hierarchy levels you
get, the hierarchy structure is stored in two columns across the table; ID and
Parent ID. ID usually points to the existing row as the unique key, and
Parent ID usually means to another row in the same table as the ID of
manager, parent, or higher level’s member. Only these two columns
together build the hierarchy. Here is an example;
DAX has a set of functions named Parent-child functions handy for parsing
this type of hierarchy. Let’s see how these functions work.
Sample Dataset
If you want to walk through the example of this chapter, create a new Power
BI Desktop file, and get data from AdventureWorksDW and select
DimEmployee as the only table to get data from.
To find out the size of the hierarchy, you need to find out the maximum
PathLength value. You can create a report visual and show Maximum of
Path Length field to see the maximum number of levels in your dataset.
As you can see, the maximum number of levels in the example dataset in
this chapter is 5.
As you can see, 112 is the ID of the big boss and the first level of
management in the Path column.
PathItemReverse; start from the lowest level
If you don’t want to start from the highest level, you can use the
PathItemReverse function. Everything will be similar to using the PathItem
function; the only difference is that this time, the position starts with index
1 for the lowest level of the hierarchy.
LookupValue; to find the name of the
employee
Having just the ID of the manager is not usually enough. You may need to
get the name of the employee too. In the DimEmployee table, we do not
have a full name field. So first add a full name field as below;
Now you can use the LookupValue function to get the employee's full name
that we found by the PathItem function. LookupValue asks for three
parameters;
The output column
The column to search into for the keyword
the keyword (keyword in our scenario is coming from the
result of the PathItem function)
Here is the code altogether for organization level 1:
Organization Level 1 =
LOOKUPVALUE(
DimEmployee[FullName],
DimEmployee[EmployeeKey],
PATHITEM(
DimEmployee[Path],
1,
1)
)
Summary
Parent-child functions in DAX are simple to use but very powerful and
functional in Power BI and DAX. Using these functions will allow you to
parse hierarchies such as organizational charts or the chart of accounts in a
recursive mode. In this chapter, you have seen an example of using parent-
child functions for parsing an organizational chart.
Book wrap up
Congratulations on finishing the book. I hope you enjoyed reading, and this
book took you through some of the pathways to learn DAX. Always
remember that there are many ways to accomplish a calculation in DAX.
Happy DAXing!
To leverage the learnings from this book, I encourage you to start applying
the learning right away in your Power BI implementations. If you feel
concerned or have a question about a particular scenario, feel free to reach
out to me directly using RADACAD website[https://radacad.com/], I’d be
more than happy to look into your question.
Wishing you the best
Reza Rad
July 2021
Other books from Reza Rad
Power BI from Rookie to Rock Star
This is a series of four books, over 1200 pages, available for free to
download from here [https://radacad.com/online-book-power-bi-from-
rookie-to-rockstar]