DAX Formulas
CALCULATE Β· FILTER Β· ALL Β· SUMX Β· RANKX Β· RELATED Β· time intelligence Β· VAR
Sheet 2 of 3
Power BI / SSAS
Intermediate
Printable
Measures vs Calculated Columns vs Calculated Tables
| Measure | Calculated Column | Calculated Table | |
|---|---|---|---|
| Evaluated | At query time (dynamic) | At refresh time (static) | At refresh time (static) |
| Context | Respects filter/row context | Row context only | No filter context |
| Storage | Not stored β computed on demand | Stored in model (uses RAM) | Stored as a full table |
| Aggregates | Yes β SUM, AVG, COUNTβ¦ | No β row by row only | Yes |
| Use in visual | Values well, Y-axis | Axis, Legend, Slicer, Table | As a dimension table |
| Create with | Modeling β New Measure | Modeling β New Column | Modeling β New Table |
| Example | Total Sales = SUM(Sales[Amount]) | Full Name = [First] & " " & [Last] | DateTable = CALENDARAUTO() |
Prefer measures over calculated columns for aggregations. Calculated columns consume model memory for every row and don't respond to filter context β a common DAX mistake for beginners.
DAX Syntax & VAR / RETURN
Basic Syntax Rules
-- Reference a column: TableName[ColumnName] -- Reference a measure: [MeasureName] -- String: "text" Number: 42 Decimal: 3.14 -- Boolean: TRUE() FALSE() BLANK() -- Simple measure Total Sales = SUM(Sales[Amount]) -- Operators: + - * / &(concat) = <> < > <= &>= -- Logical: && || NOT() -- IN operator Sales[Region] IN {"North", "South"}
VAR / RETURN β cleaner, faster DAX
-- VAR stores an intermediate result -- RETURN uses it β evaluated once (faster) Profit Margin % = VAR TotalSales = SUM(Sales[Amount]) VAR TotalCost = SUM(Sales[Cost]) VAR Profit = TotalSales - TotalCost RETURN DIVIDE(Profit, TotalSales) -- DIVIDE(numerator, denominator [, alternateResult]) -- Safe division β returns 0 (or alt) instead of error
Aggregation Functions
| Function | What it does | Example |
|---|---|---|
| SUM | Total of numeric column | SUM(Sales[Amount]) |
| AVERAGE | Mean of numeric column | AVERAGE(Sales[Amount]) |
| COUNT | Count of numeric values | COUNT(Sales[OrderID]) |
| COUNTA | Count of non-blank values (any type) | COUNTA(Customer[Name]) |
| COUNTROWS | Count rows in a table | COUNTROWS(Sales) |
| DISTINCTCOUNT | Count unique values | DISTINCTCOUNT(Sales[CustomerID]) |
| MIN / MAX | Smallest / largest value | MIN(Sales[OrderDate]) |
| SUMX | Row-by-row expression, then sum | SUMX(Sales, Sales[Qty]*Sales[Price]) |
| AVERAGEX | Row-by-row expression, then average | AVERAGEX(Sales, Sales[Qty]) |
| COUNTX | Row-by-row expression, then count | COUNTX(Sales, Sales[Discount]) |
| MINX / MAXX | Row-by-row expression, then min/max | MAXX(Sales, Sales[Amount]) |
SUMX vs SUM β key difference
-- SUM: aggregates an existing column directly Total Revenue = SUM(Sales[Revenue]) -- SUMX: evaluates expression ROW BY ROW first Total Revenue X = SUMX( Sales, -- table to iterate Sales[Qty] * Sales[UnitPrice] -- expression per row ) -- Use SUMX when the column you want doesn't exist
CALCULATE β The Most Important DAX Function
CALCULATE Syntax
-- CALCULATE(expression, filter1, filter2, ...) -- Evaluates expression in a MODIFIED filter context -- Basic: Sales for a specific region North Sales = CALCULATE( SUM(Sales[Amount]), Region[Name] = "North" ) -- Multiple filters (AND logic) North 2024 = CALCULATE( SUM(Sales[Amount]), Region[Name] = "North", Date[Year] = 2024 )
CALCULATE with FILTER
-- FILTER returns a table of matching rows -- Use when condition is complex / multi-column High Value Sales = CALCULATE( SUM(Sales[Amount]), FILTER( Sales, Sales[Amount] > 1000 ) ) -- Prefer simple column = value over FILTER -- when possible β it's faster
ALL β remove filters
-- ALL removes filters from a table or column -- % of total (ignores all filters on Sales) % of Total = DIVIDE( SUM(Sales[Amount]), CALCULATE( SUM(Sales[Amount]), ALL(Sales) ) ) -- ALLEXCEPT β remove all filters EXCEPT some Category % = DIVIDE( SUM(Sales[Amount]), CALCULATE( SUM(Sales[Amount]), ALLEXCEPT(Product, Product[Category]) ) ) -- ALLSELECTED β use slicer context only % of Visible = DIVIDE( SUM(Sales[Amount]), CALCULATE( SUM(Sales[Amount]), ALLSELECTED(Sales) ) )
KEEPFILTERS & REMOVEFILTERS
-- KEEPFILTERS β add filter without replacing -- existing context (intersection, not override) North Intersect = CALCULATE( SUM(Sales[Amount]), KEEPFILTERS( Region[Name] = "North" ) ) -- REMOVEFILTERS β explicit alias for ALL() CALCULATE( SUM(Sales[Amount]), REMOVEFILTERS(Sales[Region]) ) -- VALUES β distinct values in filter context -- SELECTEDVALUE β single selected value Selected Region = SELECTEDVALUE( Region[Name], "All Regions" -- default if multiple )
CALCULATE = Context Transition: When used inside a row context (calculated column or iterator), CALCULATE automatically converts row context to an equivalent filter context β this is one of DAX's most powerful and subtle behaviours.
RANKX, TOPN & PERCENTILEX
RANKX β rank within a table
-- RANKX(table, expression, [value], [order], [ties]) Sales Rank = RANKX( ALL(Product[Name]), -- table to rank over SUM(Sales[Amount]), -- expression to rank by , -- value (blank = current) DESC, -- DESC = 1 is highest DENSE -- DENSE = no gaps in rank ) -- SKIP (default): gaps after ties 1,1,3,4 -- DENSE: no gaps 1,1,2,3
TOPN β top N rows
-- TOPN(N, table, expression, [order]) -- Returns a table of top N rows Top 5 Revenue = CALCULATE( SUM(Sales[Amount]), TOPN( 5, ALL(Customer), SUM(Sales[Amount]) ) )
RELATED, LOOKUPVALUE & USERELATIONSHIP
RELATED β bring column from related table
-- Use in calculated column (row context) -- Traverses a many-to-one relationship -- In Sales table: get Category from Product Category = RELATED(Product[Category]) -- RELATEDTABLE β from the "one" side -- Returns a table of related rows Order Count = COUNTROWS(RELATEDTABLE(Orders))
LOOKUPVALUE & USERELATIONSHIP
-- LOOKUPVALUE β no relationship needed Manager Name = LOOKUPVALUE( Employees[Name], -- return column Employees[ID], -- search column Sales[ManagerID] -- lookup value ) -- USERELATIONSHIP β activate inactive relationship Delivery Sales = CALCULATE( SUM(Sales[Amount]), USERELATIONSHIP( Sales[DeliveryDate], Date[Date] ) )
Time Intelligence Functions
YTD / QTD / MTD
-- Requires a marked Date table! Sales YTD = TOTALYTD( SUM(Sales[Amount]), Date[Date] ) Sales QTD = TOTALQTD( SUM(Sales[Amount]), Date[Date] ) Sales MTD = TOTALMTD( SUM(Sales[Amount]), Date[Date] ) -- Custom fiscal year end (e.g. 31 March) FY YTD = TOTALYTD( SUM(Sales[Amount]), Date[Date], "03-31" )
SAMEPERIODLASTYEAR & DATEADD
-- Prior year comparison Sales PY = CALCULATE( SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date]) ) -- YoY Growth % YoY % = VAR CY = SUM(Sales[Amount]) VAR PY = CALCULATE( SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date]) ) RETURN DIVIDE(CY - PY, PY) -- DATEADD β shift by any interval Sales 3M Ago = CALCULATE( SUM(Sales[Amount]), DATEADD( Date[Date], -3, MONTH ) )
DATESYTD & Date Table Rules
-- DATESYTD returns a table of dates -- Use inside CALCULATE Sales YTD Alt = CALCULATE( SUM(Sales[Amount]), DATESYTD(Date[Date]) ) -- Other date table functions DATESMTD(Date[Date]) -- month to date DATESQTD(Date[Date]) -- quarter to date DATESINPERIOD( Date[Date], LASTDATE(Date[Date]), -30, DAY ) -- rolling 30 days
Date table must be marked! Go to Model View β right-click the Date table β Mark as date table β select the Date column. Without this, time intelligence functions won't work correctly.
Filter & Table Functions
Common Table Functions
-- VALUES β distinct values (includes blanks) VALUES(Product[Category]) -- DISTINCT β distinct values (excludes blanks) DISTINCT(Product[Category]) -- HASONEVALUE β is only 1 value selected? Safe Label = IF( HASONEVALUE(Product[Category]), VALUES(Product[Category]), "Multiple" ) -- EARLIER β reference outer row context -- (in nested iterators) Rank Col = COUNTROWS( FILTER( Sales, Sales[Amount] > EARLIER(Sales[Amount]) ) ) + 1
Logical, Text & Math Functions
Logical β IF, SWITCH, IFERROR
-- IF(condition, true, false) Grade = IF(Students[Score] >= 90, "A", IF(Students[Score] >= 80, "B", "C")) -- SWITCH β cleaner than nested IF Grade = SWITCH(TRUE(), Students[Score] >= 90, "A", Students[Score] >= 80, "B", "C" -- else ) -- IFERROR β catch errors IFERROR(DIVIDE(Sales[A], Sales[B]), 0)
Text & Math
-- Text functions CONCATENATE(C[First], " ", C[Last]) C[First] & " " & C[Last] -- shorter LEFT(C[Code], 3) -- first 3 chars RIGHT(C[Code], 2) -- last 2 chars MID(C[Code], 2, 3) -- 3 chars from pos 2 LEN(C[Name]) -- length UPPER(C[Name]) -- uppercase TRIM(C[Name]) -- remove spaces FORMAT(Sales[Date], "MMM YYYY") FORMAT(Sales[Amount], "$#,0.00") -- Math functions ROUND(3.14159, 2) -- 3.14 ABS(Sales[Variance]) INT(3.9) -- 3 (truncate) MOD(10, 3) -- 1 (remainder)
DAX Functions Quick Reference
| Aggregation | Purpose |
|---|---|
| SUM / SUMX | Total / row-by-row total |
| AVERAGE / AVERAGEX | Mean / row-by-row mean |
| COUNT / COUNTX | Count numbers / expressions |
| COUNTA | Count non-blanks |
| COUNTROWS | Count table rows |
| DISTINCTCOUNT | Count unique values |
| MIN / MAX | Smallest / largest |
| DIVIDE | Safe division |
| Filter / Context | Purpose |
|---|---|
| CALCULATE | Modify filter context |
| FILTER | Return filtered table |
| ALL | Remove all filters |
| ALLEXCEPT | Remove filters except some |
| ALLSELECTED | Use slicer context only |
| KEEPFILTERS | Intersect, don't replace |
| SELECTEDVALUE | Single selected value |
| HASONEVALUE | Check single selection |
| Time Intelligence | Purpose |
|---|---|
| TOTALYTD | Year to date |
| TOTALQTD | Quarter to date |
| TOTALMTD | Month to date |
| SAMEPERIODLASTYEAR | Same period prior year |
| DATEADD | Shift date by interval |
| DATESYTD | YTD date table |
| DATESINPERIOD | Rolling period dates |
| LASTDATE / FIRSTDATE | Last / first date in context |
Next up β Sheet 3: Power Query Β·
M language Β· transform Β· merge Β· pivot Β· unpivot Β· custom columns Β· append Β· data types