Power BI DAX Simplified B099SBN1XP
Power BI DAX Simplified B099SBN1XP
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
BOOK WRAP UP
OTHER BOOKS FROM REZA RAD
About the Author
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;
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
* multiplies two numbers are
/ divides some
Comparison = equal
<> not equal
=> greater than or equal
Text concatenation & concatenate texts
Logical && AND
|| OR
IN if the value is IN the list
samples of DAX expressions with operators;
=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 (//)
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.
Using functions inside a DAX expression
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.
Expression:
Profit = FactInternetSales[SalesAmount] - FactInternetSales[TotalProductCost]
Row by Row Calculation: Row Context
One of the fundamental concepts about the calculation that you apply in the
Calculated Column (In the majority of the cases, not always); is that the
calculation evaluates one row at a time. Or, in other words, row by row
calculation. In the below table; you can see the calculation result for every
row stored in the new column;
When there is no filter applied in the report, this will return a total of
$29.36M. However, if I add a slicer in the report and select a value in it, I’ll
see a different result;
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).
Hopefully, this chapter helped you to understand the difference between these
two types of calculation.
Chapter 5: Power BI DAX Back to
Basics: Scalar Vs. Tabular
Functions
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)
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.
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)
All the above expression says that if A is greater than B, return A, otherwise
B. Now it is much simpler to read it because we split the repetitive parts into
sections. That is what the DAX variable does for expressions.
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)
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.
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:
As you can see, the input is just one column name. It cannot be one column
minus another one; that means an expression. So, what is the way to do it?
One way is to use multiple sum functions, such as below code:
Sum of Margin = SUM(FactInternetSales[SalesAmount])-SUM(FactInternetSales[TotalProductCost])
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]
)
SUMX is the sum of an expression, but SUM is just
summarizing values of one single column.
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!
Ranks miscalculated in Power BI
Even if you change the table to FactInternetSales, you will get the same
result. The reason is that RANKX will evaluate the rank based on the values
generated for the table parameter. The table parameter still considers the
visual filter.
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;
The expression above returns the row number as an index descending by the
value of Internet Sales for each EnglishProductName.
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.
What if I say the ALL of the table?
That is a possible option, but you have to consider having a column in the
visualization with the same granularity as the table. The EnglishProductName
is not that column because we have multiple rows in the DimProduct table
with the same EnglishProductName (their color or other columns might be
different). This means an expression like below:
Row Number = RANKX(
ALL(DimProduct)
,[Internet Sales])
Will not show the value you expect in the below context;
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.
The “&&” in the expression above means AND. The output will be only
products with their color as red and their SizeUniteMeasureCode as CM.
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,
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.
As you can see, an ALL function has been used. However, because there is
another filter in Calculate (DatesBetween), the expression will consider the
intersection between these two filters. As a result, in this case, ALL will
ignores any filters coming from any other tables except the DimDate. There
will be a chapter with more explanation of this particulate example.
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.
The expression above ignores filters coming from the FactInternetSales itself,
which then means ignoring filters from all the dimensions around it.
Using ALL without Input Parameter: Ignore
Everything
Another approach is to use ALL without any input table or parameters, just as
ALL(). This will ignore everything, which can be a good option if your
calculation’s value comes from multiple tables and you don’t want any filters
to affect it.
Sales All =
CALCULATE([Sales],ALL())
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>
)
This would give me something like this:
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)
DAX has many functions to write conditional expressions. For example, you
might want to calculate the sum of sales amount for all “Red” products. You
can achieve it using SUMX or Calculate and functions such as IF or Filter to
write a conditional expression for product color to be equal to “Red”. At first,
you might think these functions will have the same result set, but there is a
difference that should not be overlooked. This chapter will explain what type
of problem might happen if you don’t use these functions wisely.
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.
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 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.
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.
Summary
The Related function in DAX can be used to fetch a value from a field of
another table. However, that table should be related to the existing table
somehow in the model. The relationship should be in a way that it returns one
value from that table per value in the main table. The Related function can
traverse multiple relationships in the model and can also be used inside
measures or other functions.
Chapter 20: Power BI DAX
RelatedTable Function: Get the
subtable related to the current row
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.
The input table can be a table in your dataset, let’s say FactInternetSales. If
this function is run while we are at the row context of the DimCustomer
table, the output will be all sales transactions are related to that specific
customer. Let’s see that through an example.
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.
In the expression above, the CrossFilter changes the direction of the existing
relationship between the DimProduct[ProductKey] and
FactInternetSales[ProductKey] to both-directional.
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;
As you can see, we can use multiple CrossFilters to change the direction of
multiple relationships.
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.
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.
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:
The entire DAX expression for the measure is now as below;
YTD Sales - stop last date =
var _lastActualValue = MAX(FactInternetSales[OrderDateKey])
return
IF(
SELECTEDVALUE(DimDate[DateKey])<=_lastActualValue,
CALCULATE(SUM(FactInternetSales[SalesAmount]),
DATESYTD(DimDate[FullDateAlternateKey]))
)
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;
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')
)+1-'Lower Bound'[Lower Bound Value],"White",
"Black"
)
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 be
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;
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.
You have to select a full-date column as the Date column of the Date Table
as well.
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.
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”.
DAX expression to calculate the day of the year in Power BI
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
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"
)
)
As you can see, this calculation accumulates sales values up to the end of
each quarter.
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:
Sales last month calculation in Power BI using a custom date table
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>)
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)
The first parameter is just the date field. The second parameter is the start
date that we have calculated, and the last parameter is the end date.
DatesBetween is an excellent function to use when the start and end of the
period are determined. Here is an example of calculating the sale of a specific
period.
Sales of Specific Period with DatesBetween =
CALCULATE(
SUM(FactInternetSales[SalesAmount]),
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])
)
)
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
The code above returns a table with one single column: date. This is not
returning one single value. This means you cannot use it directly in a
measure. You have to use this function as a filter function. I have used the
SamePeriodLastYear inside a LastDate and a FirstDate. This helps to get the
range of dates for each filter context selection in the screenshot above.
SamePeriodLastYear D To =
LASTDATE(
SAMEPERIODLASTYEAR(DimDate[FullDateAlternateKey].[Date])
)
You can choose the interval to be Month, Quarter, or Year. And the number
of intervals can be negative (to go to past) or positive (to go to the future).
This is an example of using ParallelPeriod:
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:
For August 2006, for example, the SamePeriodLastYear gives us the sales of
August 2005. However, the ParallelPeriod with year interval returns the sales
for the entire year 2005.
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 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.
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)}
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;
Values in each cell can be anything, here is another example:
Sample Table = {
(1,"the first row",DATE(2019,1,1)),
(3,"the second row",Date(2020,5,12))
}
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),
(3,"the second row","something")
}
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")
}
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"}
}
)
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
In addition to the context in which the DAX expressions apply, often in DAX
expressions, we use virtual tables. Here is what I mean by virtual table:
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:
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)
&" - Values: "&CONCATENATEX(vTable,DimDate[FullDateAlternateKey].[Date],",")
There are many other methods that you can use to get the feeling of what
values you have in your virtual table. Methods you have seen here are
methods that I use often.
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
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)
)
)
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
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.
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
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>…)
The VALUES part of the statement is because the EXPRESSION part should
be returning a table, not a column. VALUES is returning the unique list of
DimCustomer[CustomerKey] column. You can use other options such as
below too:
TREATAS(SELECTCOLUMNS(DimCustomer,'CustomerKey',DimCustomer[CustomerKey]),FactInternetSales[Custome
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.
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.
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:
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.
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.
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.
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]))
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]))
Summary
Summarize is another DAX function that can be used to create an aggregated
table in Power BI. This function can have advanced features of controlling
totals and subtotal with some extra options. You have seen some examples of
Summarize for grouping, aggregation, RollUp, RollUpGroup, and IsSubTotal
functions. The Summarize function can be used to create a virtual table or a
calculated table in Power BI. However, the first one is the one that is used a
lot inside measures for dynamic calculation’s purposes.
Chapter 50: Aggregated Table in
Power BI – Using GroupBy
Function in DAX
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>]…)
This is used to create a calculated table, and here you can see the result:
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.
The screenshot above is creating using Power Query Editor. I used it to show
you what the Current Group looks like, so don’t look for a visual way of
looking at CurrentGroup in DAX. As you see in the screenshot above, the
CurrentGroup, when our filter context is High School, is the sub-table of
DimCustomer with all of the columns, but filtered only for High School.
Now, using CurrenGroup, you can write the expression as below;
GroupBy - with aggregation = GROUPBY(
DimCustomer,DimCustomer[EnglishEducation],
'Row Count',
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)
The Union function is tabular and cannot be used directly in a measure. It has
to be used either in a calculated table or inside another function.
Union function in DAX
Union function does not remove duplicate values that might happen after the
operation. You can use Distinct or Values functions for that.
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])
The start and end value or/and the increment value can also be decimal
values.
Sample Table = GENERATESERIES(1.0,3.0,0.4)
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.
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)
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.
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.
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)
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])
)
)
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.
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.
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.
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]