Summary: Advanced DAX Functions
SESSION OVERVIEW:
By the end of this session, students will be able to:
● Grasp Date Time functions and Time Intelligence Functions.
● Write complex DAX queries using advanced DAX functions.
KEY TOPICS AND EXAMPLES:
Note: In this session we will use this dataset to practice the concepts.
Note: Before using date functions, we must ensure that the date columns in our dataset are read
properly as date data type. Further we need to ensure that the format is correct. For example, we may
get data from Singapore in a MM-DD-YYYY format but our Power BI system setting (United States)
may recognise it in a different format (e.g. DD-MM-YYYY). If it does so then our values will be
incorrect or it will throw an error. Hence it is important to ensure that we change the Locale in the
Power BI settings.
To do this, go to File → Options & Settings → Options → Regional Settings → in the Locale for
Import box select the desired locale: e.g. English (United States).
This will determine how Power BI interprets the dates in our dataset.
1. DAX Date Time Functions
a. TODAY
It returns the current date.
Syntax: TODAY()
b. NOW
It returns the current date and time.
Syntax: NOW()
c. DATE
It returns a date value given the specified year, month, and day.
Syntax: DATE(<year>, <month>, <day>)
d. TIME
It returns a time value based on the specified hour, minute, and second.
Syntax: TIME(<hour>, <minute>, <second>)
e. YEAR
It returns the year component from a date.
Syntax: YEAR(<date>)
1
f. MONTH
It returns the month component from a date.
Syntax: MONTH(<date>)
g. DAY
It returns the day component from a date.
Syntax: DAY(<date>)
h. HOUR, MINUTE, SECOND
These functions return the hour component of a timestamp (0 to 23), minute
component of a timestamp (0 to 59) and second component of a timestamp (0 to 59)
respectively.
Syntax: HOUR(<time>), MINUTE(<time>), SECOND(<time>)
i. WEEKDAY
It returns the day of the week as a number from 1 to 7. 1 represents Sunday, 2
represents Monday, and so on, up to 7 for Saturday.
Syntax: WEEKDAY(<date>, [<return_type>])
Note: Here [<return_type>] is optional and can be used to specify the numbering
convention for the day of the week. These are the various numbering conventions:
1: Numbers the days from Sunday (1) to Saturday (7).
2: Numbers the days from Monday (1) to Sunday (7).
3: Numbers the days from Monday (0) to Sunday (6).
j. EOMONTH
It returns the last day of the month after adding the specified number of months to the
start date.
Syntax: EOMONTH(<start_date>, <months>)
k. DATEDIFF
It returns the difference between two dates in the specified interval.
Syntax: DATEDIFF(<start_date>, <end_date>, <interval>)
Here, the <interval> is the interval to use when comparing dates. The value can be
one of the following:
- SECOND
- MINUTE
- HOUR
- DAY
- WEEK
- MONTH
- QUARTER
- YEAR
2
l. DATEADD
It returns a new date after adding or subtracting a specified number of intervals to the
start date.
Syntax: DATEADD(<start_date>, <number>, <interval>)
The list of <interval> values are given below:
- year
- quarter
- month
- day
-
Note: A DateTime value can be stored as a decimal number, where:
● The integer part represents the date.
● The decimal part represents the time.
Example:
2024-03-04 12:00 PM → 45264.5 (where .5 represents half a day, i.e., 12 hours).
This concept is crucial when performing calculations like adding hours, days, or handling
time-based transformations.
For example, in our Vehicle Orders dataset, we need to define a target delivery date. Our
company targets to deliver orders within 48 hours to our users. If we fail to do so, we will
breach the delivery promise to our users. Let us find in how many cases we have breached the
delivery promise. For this there are multiple ways, one of which is to add 48 hours (2 days) to
the Order Date and compare this value with the actual delivery date. If the difference
(Promised Delivery Date - Actual Delivery Date) is positive then we have delivered on our
promise otherwise we have failed to do so.
Here the following formula can be used:
Target Delivery Date = DATEADD(Sheet1[ORDERDATETIME].[Date],2,DAY)
3
Note: Here Target Delivery Date has added 2 days without considering the timestamp of Order Date.
DATEADD just adds 2 days without considering the timestamp. A better way here would be to add 48
hours without using the DATEADD function, but here we just intend to demonstrate the use of
DATEADD.
Now, we can simply use DATEDIFF to find the difference between the target delivery date and the
actual delivery date, as shown below.
If the difference is positive, then we have kept the 2 day delivery promise otherwise we have broken
the delivery promise.
2. DAX Time Intelligence Functions
Time Intelligence functions in DAX are designed to perform calculations and analysis based
on dates and times within Power BI. When we have columns that include date and timestamp,
these functions become very useful. These functions enable you to perform calculations,
comparisons, and identify trends within your data based on specific time periods.
Uses:
● Understanding trends and seasonality: Time Intelligence functions help with
identifying patterns and trends over periods of time. They help us understand growth
or decline in parameters like sales, rainfall, temperature, etc. Further, they help us
compare values with previous years, quarters, months, days etc.
● Helping companies with performance monitoring: They help companies achieve
targets through effective monitoring and analysis of the current trend/pattern and the
progress towards goals. They help in meeting the targets through Evaluating progress
and forecasting.
● Sharper reporting and drill downs: They help in narrowing down data using
functions easily. We can filter for specific dates/timestamps, or the period between
4
dates/timestamps easily using Time Intelligence functions. This helps in improving
reporting efficiency and enables us to make clear and concise reports.
● Simpler calculations: They make date/time calculations simpler through many
powerful functions.
Below are some of the commonly used Time Intelligence functions:
a. TOTALYTD
It calculates the year-to-date total of the expression for the specified dates.
Syntax: TOTALYTD(<expression>, <dates>)
b. TOTALMTD
It calculates the month-to-date total of the expression for the specified dates.
Syntax: TOTALMTD(<expression>, <dates>)
c. TOTALQTD
It calculates the quarter-to-date total of the expression for the specified dates.
Syntax: TOTALQTD(<expression>, <dates>)
d. DATESYTD
It returns a table of dates from the beginning of the year up to the specified date.
Syntax: DATESYTD(<dates>, [<year_end_date>])
e. DATESMTD
It returns a table of dates from the beginning of the month up to the specified date.
Syntax: DATESMTD(<dates>)
f. DATESQTD
It returns a table of dates from the beginning of the quarter up to the specified date.
Syntax: DATESQTD(<dates>)
g. DATESINPERIOD
It returns a table of dates for a specified number of intervals before or after a given
start date.
Syntax: DATESINPERIOD(<dates>, <start_date>, <number_of_intervals>,
<interval>)
h. DATESBETWEEN
It returns a table of dates that fall between the specified start date and end date,
inclusive of both endpoints.
Syntax: DATESBETWEEN(<dates>, <start_date>, <end_date>)
i. SAMEPERIODLASTYEAR
It returns a table of dates for the same period in the previous year.
Syntax: SAMEPERIODLASTYEAR(<dates>)
5
j. PARALLELPERIOD
It returns a table of dates for the same period in a previous or future period.
Syntax: PARALLELPERIOD(<dates>, <number_of_intervals>, <interval>)
k. PREVIOUSYEAR
It returns the value from the previous year, relative to the current date.
Syntax: PREVIOUSYEAR(<dates>)
l. PREVIOUSQUARTER
It returns the value from the previous quarter, relative to the current date.
Syntax: PREVIOUSQUARTER(<dates>)
3. Advanced DAX Functions
a. CALCULATE
It enables us to do a calculation and modify the context in which other functions or
expressions are evaluated. It allows us to apply additional filters, change row or
column context, and manipulate the calculation context in various ways.
Syntax: CALCULATE(<expression>, <filter1>, <filter2>, ...)
Example:
CALCULATE(
SUM(Sales[Amount]),
Sales[Region] = "North"
)
This can be used to find the total sales amount for the North Region.
b. FILTER
FILTER(<table>, <filter_expression>)
It returns a filtered table based on some condition. It iterates over each row of the
table and evaluates a logical expression for each row. If the expression evaluates to
TRUE for a particular row, that row is included in the resulting table; otherwise, it is
excluded.
Syntax:
Example: In our dataset, we can use this:
FILTER(Sheet1,Sheet1[QUANTITYORDERED]>40)
This will give us a table with the rows in which the number of items ordered is more
than 40.
The FILTER function is commonly used in combination with other DAX functions,
such as CALCULATE, to apply filters to calculations and aggregations. For example:
CALCULATE(
SUM(Sales[Amount]),
FILTER(Sales, Sales[Product] = "ProductA")
)
6
This will give us the total sales for ProductA.
Note: FILTER can be used to filter down a table to create a new table. For example,
in our dataset, we have a sales table with data from 2012 to 2017 but suppose we
want only 2014 data. We can create a new calculated table using the following:
2014_Sales = FILTER(Sheet1, Sheet1[ORDERDATETIME].[Year]="2014")
In this manner we can filter an existing table to create a new, more specific table.
c. SUMMARIZE
It is used to create a summary table by grouping data based on one or more columns
and aggregating values using specified expressions.
Syntax: SUMMARIZE(<table>, <group_by_column1>, <group_by_column2>, ...,
<name1>, <name2>, …, <expression1>, <expression2>, ...)
Example: In our dataset, we can use the SUMMARIZE function in this manner:
If we want to see the total sales for each country, each city and each product line, we
can use this function as shown below:
Total Sales =
SUMMARIZE(Sheet1,Sheet1[COUNTRY],Sheet1[CITY],Sheet1[PRODUCTLINE],
“Total Sales”, SUM(Sheet1[Sales]))
d. ADDCOLUMNS
It creates a new table by adding one or more columns to an existing table or table
expression. It allows you to derive new columns based on calculations or expressions
applied to existing columns within the table.
Syntax: ADDCOLUMNS(<table>, <name>, <expression1>, <name>, <expression2>,
...)
7
Example: In our dataset, we can create a new table with the Sales Column, which
was originally not there in the dataset. Let’s suppose the company is running a 20%
discount scheme. In that case, we can also add the discounted price column to the
table.
This can be done using this formula, as shown below:
New Table = ADDCOLUMNS(Sheet1, "SalesAmt",
Sheet1[QUANTITYORDERED]*Sheet1[PRICEEACH], "Discounted Price",
Sheet1[PRICEEACH]*0.8)
e. GROUPBY
In DAX, GROUPBY is a clause used within ADDCOLUMNS or SUMMARIZE
functions to group data from a table based on specified columns and perform
aggregation on each group. It enables us to create summary tables by defining
grouping criteria and using aggregate functions to aggregate values for each group.
Syntax: GROUPBY (<table>, [<groupBy_columnName>],
[<groupBy_columnName>], …, [<name1>], [<expression1>], [<name2>],
[<expression2>], …)
Example: In our dataset, we can use the GROUP BY function to create a new table
with total sales in each country and city. For this, we will group by country and city
and will find the sum of sales.
Note: we are using another function: CURRENTGROUP() here.
CURRENTGROUP refers to the current group that has been created and is being
evaluated. It allows you to access the values of columns within the current group and
perform calculations or comparisons based on those values. It returns the rows in the
table argument of the GROUPBY function which correspond to one group of values
of the groupBy_columnName arguments.
8
The formula used here is given below:
Sales by country, city = GROUPBY(Sheet1, Sheet1[COUNTRY], Sheet1[CITY],
"Total Sales", SUMX(CURRENTGROUP(),Sheet1[Sales])))
Note: Here instead of a sum, we need to use the SUMX because otherwise it would
SUM over the entire table and not the current group that we have created (using
country and city).
f. VAR and RETURN
It is used to define variables in DAX. Variables allow us to store intermediate results
and we can reuse them in the same DAX expression.
Uses of VAR:
● Improves readability
● Enhances performance by reducing redundant calculations
● Simplifies complex calculations
Syntax: VAR <variable_name> = <expression>
RETURN <result_expression>
Example: In our dataset, we can use VAR as follows:
DiscountedPrice =
VAR BasePrice =
'Sheet1'[PRICEEACH]
VAR Discount =
IF('Sheet1'[PRODUCTLINE] = "Motorcycles", 0.9,
IF(‘Sheet1'[PRODUCTLINE] = "Classic Cars", 0.85, 0.8))
RETURN
BasePrice * Discount
9
This can be seen in the image below from our dataset.
Here, we are calculating the discounted price. If the product line is Motorcycles then
discount is 10%, if the product line is Classic Cars then discount is 15%, otherwise
the discount is 20%. Here the VAR function is used to simplify our calculation.
As demonstrated above, the RETURN keyword is used within calculated columns,
measures, and table expressions to specify the value or result that should be returned
by the expression. It marks the end of the expression and determines the final value to
be used in the expression.
g. RELATED
It returns the value from a column in a related table. The data model should have the
relationship defined for this function to work.
Syntax: RELATED(<column_name>)
For example, if we have sales orders in one table and product data in another table,
and we want to create a calculated column for one particular product, we can use
RELATED(Product[Product Name]). For each order ID this would return the product
name.
Note: It is important to note here that RELATED will not work if relationships are not
defined.
h. FORMAT
It converts a value to text based on the specified format.
Syntax: FORMAT(<value>, <format_string>, [<locale_name>])
Here, format_string is the string with the formatting template.
locale_name is the name of the locale.
10
For example: = FORMAT( 700.17, "Percent") returns 70017.00%
i. CROSSFILTER
It is used to define the directions of the relationship between 2 columns.
Syntax: CROSSFILTER(<columnName1>, <columnName2>, <direction>)
Direction can have the following values:
None, Both: creates a bidirectional filter
OneWay: filters applied on one side filter the other side
OneWay_LeftFiltersRight: filters applied on left side filter the right side
OneWay_RightFiltersLeft: filters applied on right side filter the left side
j. ALL
It clears all the filters and returns the value. In other words, it returns all the values in
the table or column, ignoring any applied filters or context.
Syntax: ALL (<table>)
or
ALL (<table>[column])
4. Other basic functionalities in DAX
DAX is an easy to understand language and very intuitive. For example, the SUM, COUNT
functions work just like Excel. If we want to define a column as a particular fixed value, we
can simply write: Value = 1. This is shown below:
Similarly, if we want to add ‘1’ to a column, we can do so by simply adding a “+1” in the
DAX code of the column. For example, let’s say we want to add a ₹ 5 logistics fee in the
Discounted price. We can simply add a “+5” at the end of the formula in DAX.
11
We can also use any other DAX operator to do other manual calculations in a similar manner.
4. Keeping all DAX in one table
It is always helpful if we create a new table and store all DAX in that table. This keeps things
organized and makes it easier for us to use measures and calculated columns during
visualization.
Steps to store all DAX in one table:
1. Select Enter Data option to create a new table.
2. Load the table without entering any data
3. Right click on the newly created table and start creating new measures
4. All DAX should now be defined in this ‘Measures’ table.
One of the benefits of this is that if we need to change any measure/calculation, we just need
to change it in the measures table instead of changing it in the source tables. For example, if
we have a measure ‘Sales’ in 5 tables, we should simply create a measure for Total Sales in
the Measures table and then use it. Now if we need to do any calculation change to the Sales
column, instead of changing it in 5 different tables, we only need to change it in one table, i.e.
the Measures table.
12