
Dax Mastery
- 79 installs
- 50 repo stars
- Updated June 18, 2026
- josiahsiegel/claude-plugin-marketplace
Write optimized Data Analysis Expressions (DAX) for Power BI and Microsoft Fabric.
About
Plugin guidance for DAX query optimization in Power BI and Fabric analytics. Covers function patterns, performance tuning, and context handling.
- DAX function patterns and optimization
- Power BI and Fabric analytics
Dax Mastery by the numbers
- 79 all-time installs (skills.sh)
- +4 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #866 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill dax-masteryAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 79 |
|---|---|
| repo stars | ★ 50 |
| Last updated | June 18, 2026 |
| Repository | josiahsiegel/claude-plugin-marketplace ↗ |
What it does
Write optimized Data Analysis Expressions (DAX) for Power BI and Microsoft Fabric.
Files
DAX (Data Analysis Expressions) Mastery
Overview
Complete DAX reference covering evaluation contexts, CALCULATE, time intelligence, iterators, table functions, performance optimization, and advanced patterns. DAX is the formula language for Power BI measures, calculated columns, calculated tables, and RLS filters.
Evaluation Contexts
Row Context
- Created by: Calculated columns, iterators (SUMX, FILTER, AVERAGEX, etc.), row-by-row evaluation
- Each row in the table has its own row context
- Access columns directly:
Sales[Amount] - Nested iterators create nested row contexts
Filter Context
- Created by: Slicers, visual filters, page filters, report filters, CALCULATE arguments
- Determines which rows are visible to aggregation functions
- Does NOT provide row-level access (cannot use
Sales[Amount]directly in a measure without aggregation)
Context Transition
- CALCULATE converts row context into filter context
- Happens when a measure is referenced inside an iterator
- Each row's column values become filter arguments
// Context transition example:
Sales Amount = SUM(Sales[Amount])
// Inside SUMX, each row triggers context transition:
Weighted Amount =
SUMX(
Products,
Products[Weight] * [Sales Amount] // [Sales Amount] triggers CALCULATE internally
)CALCULATE - The Most Important Function
CALCULATE(<expression>, <filter1>, <filter2>, ...)Filter argument types:
| Type | Example | Behavior |
|---|---|---|
| Boolean (table filter) | Products[Color] = "Red" | Adds filter, keeps existing context |
| Table expression | FILTER(ALL(Products), Products[Price] > 100) | Replaces filter on affected columns |
| REMOVEFILTERS | REMOVEFILTERS(Products[Color]) | Removes existing filter on column |
| ALL | ALL(Products) | Removes all filters on table |
| KEEPFILTERS | KEEPFILTERS(Products[Color] = "Red") | Intersects with existing filter |
| USERELATIONSHIP | USERELATIONSHIP(Sales[ShipDate], Date[Date]) | Activates inactive relationship |
| CROSSFILTER | CROSSFILTER(Sales[ProductID], Products[ID], Both) | Changes cross-filter direction |
Critical rules:
- Boolean filters are syntactic sugar for FILTER(ALL(column), condition)
- Boolean filters REPLACE the existing filter on that column
- Use KEEPFILTERS to ADD to (intersect with) existing filters
- CALCULATE modifiers (ALL, REMOVEFILTERS) execute BEFORE filter arguments
Time Intelligence Quick Reference
Prerequisite: A proper Date table marked as a date table with a continuous date column.
| Function | Purpose | Example |
|---|---|---|
| TOTALYTD | Year-to-date | TOTALYTD([Sales], Date[Date]) |
| TOTALMTD | Month-to-date | TOTALMTD([Sales], Date[Date]) |
| TOTALQTD | Quarter-to-date | TOTALQTD([Sales], Date[Date]) |
| SAMEPERIODLASTYEAR | Same period, prior year | CALCULATE([Sales], SAMEPERIODLASTYEAR(Date[Date])) |
| DATEADD | Shift by interval | CALCULATE([Sales], DATEADD(Date[Date], -1, MONTH)) |
| PARALLELPERIOD | Entire shifted period | CALCULATE([Sales], PARALLELPERIOD(Date[Date], -1, QUARTER)) |
| DATESYTD | Date table filtered to YTD | CALCULATE([Sales], DATESYTD(Date[Date])) |
| DATESBETWEEN | Date range | CALCULATE([Sales], DATESBETWEEN(Date[Date], start, end)) |
| PREVIOUSMONTH | Entire previous month | CALCULATE([Sales], PREVIOUSMONTH(Date[Date])) |
| PREVIOUSYEAR | Entire previous year | CALCULATE([Sales], PREVIOUSYEAR(Date[Date])) |
Common time intelligence patterns:
// Year-over-Year Growth %
YoY Growth % =
VAR CurrentSales = [Total Sales]
VAR PriorYearSales = CALCULATE([Total Sales], SAMEPERIODLASTYEAR(Date[Date]))
RETURN
DIVIDE(CurrentSales - PriorYearSales, PriorYearSales)
// Rolling 12-Month Total
Rolling 12M =
CALCULATE(
[Total Sales],
DATESINPERIOD(Date[Date], MAX(Date[Date]), -12, MONTH)
)
// Moving Average (3 months)
3M Moving Avg =
AVERAGEX(
DATESINPERIOD(Date[Date], MAX(Date[Date]), -3, MONTH),
CALCULATE([Total Sales])
)Variables (VAR/RETURN)
Always use variables for readability and performance:
Profit Margin % =
VAR TotalRevenue = SUM(Sales[Revenue])
VAR TotalCost = SUM(Sales[Cost])
VAR Profit = TotalRevenue - TotalCost
RETURN
DIVIDE(Profit, TotalRevenue)Rules:
- Variables are evaluated once (performance benefit when reused)
- Variables capture filter context at the point of definition
- Variables can hold scalar values or tables
- Use meaningful names (not
x,temp)
Iterator Functions
Iterators scan a table row by row, creating row context:
| Function | Purpose |
|---|---|
| SUMX | Sum of expression evaluated per row |
| AVERAGEX | Average of expression per row |
| MINX / MAXX | Min/Max of expression per row |
| COUNTX | Count of non-blank expression results |
| RANKX | Rank based on expression |
| FILTER | Returns table rows matching condition |
| ADDCOLUMNS | Adds calculated columns to table |
| SELECTCOLUMNS | Returns table with selected/calculated columns |
| GENERATE | Cross-join with row context |
// Weighted average price
Weighted Avg Price =
SUMX(
Sales,
Sales[Quantity] * RELATED(Products[UnitPrice])
) / SUM(Sales[Quantity])Calculation Groups
Reduce measure sprawl by defining reusable calculation patterns:
// Instead of creating YTD, PY, YoY for EVERY measure:
// Create ONE calculation group with items:
// - Current: SELECTEDMEASURE()
// - YTD: CALCULATE(SELECTEDMEASURE(), DATESYTD(Date[Date]))
// - PY: CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR(Date[Date]))
// - YoY%: VAR Curr = SELECTEDMEASURE()
// VAR PY = CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR(Date[Date]))
// RETURN DIVIDE(Curr - PY, PY)Create via Tabular Editor, TMDL view in Desktop, or TOM/.NET SDK.
Field Parameters
Enable users to dynamically switch dimensions or measures in visuals:
// Created via Modeling tab > New parameter > Fields
// Generates a calculated table:
Parameter =
{
("Revenue", NAMEOF(Sales[Total Revenue]), 0),
("Profit", NAMEOF(Sales[Total Profit]), 1),
("Units", NAMEOF(Sales[Total Units]), 2)
}User-Defined Functions (September 2025 Preview)
The most significant DAX language update since variables (2015). Define reusable parameterized functions:
// Define a UDF in DAX query view or model
DEFINE
FUNCTION AddTax = (amount : NUMERIC) => amount * 1.1
// Nest UDFs
FUNCTION AddTaxAndDiscount = (amount : NUMERIC, discount : NUMERIC) =>
AddTax(amount - discount)
EVALUATE { AddTaxAndDiscount(100, 20) } // Returns 88Parameter types: NUMERIC, Scalar, Table, AnyVal, AnyRef, CalendarRef, ColumnRef, MeasureRef, TableRef
Parameter modes: val (eager evaluation) or expr (lazy/context-sensitive)
Usage: Once defined and saved to the model, call UDFs from measures, calculated columns, visual calculations, and other UDFs.
Enable: File > Options > Preview features > DAX user-defined functions
Window Functions (WINDOW, INDEX, OFFSET)
DAX window functions for row-relative and range calculations:
// Running total using WINDOW
Running Total =
CALCULATE(
[Total Sales],
WINDOW(1, ABS, 0, REL, ALLSELECTED(Date[Month]),
ORDERBY(Date[MonthNumber], ASC))
)
// Previous row value using OFFSET
Previous Month Sales =
CALCULATE(
[Total Sales],
OFFSET(-1, ALLSELECTED(Date[Month]),
ORDERBY(Date[MonthNumber], ASC))
)
// Nth row using INDEX
First Month Sales =
CALCULATE(
[Total Sales],
INDEX(1, ALLSELECTED(Date[Month]),
ORDERBY(Date[MonthNumber], ASC))
)Key clauses:
ORDERBY-- sort order within the windowPARTITIONBY-- subset of rows (the "window" partition)MATCHBY-- identify the current row in ambiguous contexts
Visual Calculations (2024-2026)
Calculations scoped to the visual matrix, not the data model:
| Function | Purpose |
|---|---|
| FIRST | Value from first row of axis |
| LAST | Value from last row of axis |
| PREVIOUS | Value from previous row |
| NEXT | Value from next row |
| LOOKUP | Value with filter (June 2025) |
| LOOKUPWITHTOTALS | Value with filter, respects totals (June 2025) |
Visual calculations are defined per-visual and do not affect the semantic model.
Calendar-Based Time Intelligence (September 2025 Preview)
Define custom calendars (fiscal, retail, 13-month, lunar) with 8 new week-based functions:
| Function | Purpose |
|---|---|
| TOTALWTD | Week-to-date running total |
| CLOSINGBALANCEWEEK | Closing balance for the week |
| OPENINGBALANCEWEEK | Opening balance for the week |
| STARTOFWEEK | First date of current week |
| ENDOFWEEK | Last date of current week |
| NEXTWEEK | Table of dates for next week |
| PREVIOUSWEEK | Table of dates for previous week |
| DATESWTD | Week-to-date date filter |
Enable: File > Options > Preview features > Enhanced DAX Time Intelligence
Dynamic Format Strings
Apply context-dependent formatting without converting to text (GA in Desktop and Report Server Jan 2025+):
// Dynamic format string for currency
Total Sales =
SUM(Sales[Amount])
// Format string expression (set in measure properties):
// = IF(SELECTEDVALUE(Currency[Code]) = "EUR", "€#,##0.00", "$#,##0.00")Advantage over FORMAT(): Keeps numeric data type, enabling correct chart rendering and sorting.
TABLEOF and NAMEOF (February 2026)
Reference model objects that auto-adapt to renames:
// NAMEOF returns the name of a column/measure/calendar as text
NAMEOF(Sales[Amount]) // Returns "Amount"
// TABLEOF returns a reference to the table of a column/measure
TABLEOF(Sales[Amount]) // Returns reference to Sales tableUseful inside UDFs for safer, rename-proof code.
Common Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
FILTER(table, ...) as CALCULATE arg | Full table scan, no engine optimization | Use boolean filter: column = value |
| Nested CALCULATE | Confusing context overrides | Use single CALCULATE with multiple filters |
| SUMX over entire table for simple sum | Unnecessary iterator | Use SUM() for simple column aggregation |
| FORMAT() in measures for sorting | Returns text, cannot sort numerically | Use separate sort column |
| Calculated columns for aggregation | Stored per row, wastes memory | Use measures instead |
| COUNTROWS(FILTER(table,...)) | Slower than CALCULATE(COUNTROWS(table), filter) | Use CALCULATE with filter |
| Copy-pasting DAX across measures | Hard to maintain, error-prone | Use UDFs (preview) to define reusable logic |
| FORMAT() for conditional display | Returns text, breaks sorting/charts | Use dynamic format strings instead |
| Overusing EARLIER() | Confusing, legacy pattern | Use VAR to capture outer context |
| Ignoring MATCHBY in window functions | Ambiguous row identity | Always specify MATCHBY when partition has duplicates |
Additional Resources
Reference Files
- `references/dax-function-categories.md` -- Complete function reference organized by category including INFO functions, window functions, and 2025-2026 additions
- `references/dax-patterns-advanced.md` -- Advanced patterns: virtual relationships, dynamic segmentation, parent-child hierarchies, basket analysis
DAX Function Categories Reference
Aggregation Functions
| Function | Syntax | Purpose |
|---|---|---|
| SUM | SUM(column) | Sum of column values |
| AVERAGE | AVERAGE(column) | Average of column values |
| MIN | MIN(column) or MIN(expr1, expr2) | Minimum value |
| MAX | MAX(column) or MAX(expr1, expr2) | Maximum value |
| COUNT | COUNT(column) | Count of numeric non-blank values |
| COUNTA | COUNTA(column) | Count of non-blank values (any type) |
| COUNTBLANK | COUNTBLANK(column) | Count of blank values |
| COUNTROWS | COUNTROWS(table) | Count of rows in table |
| DISTINCTCOUNT | DISTINCTCOUNT(column) | Count of distinct non-blank values |
| DISTINCTCOUNTNOBLANK | DISTINCTCOUNTNOBLANK(column) | Count distinct including BLANK |
| PRODUCT | PRODUCT(column) | Product (multiplication) of values |
| MEDIAN | MEDIAN(column) | Median value |
| PERCENTILE.INC | PERCENTILE.INC(column, percentile) | Inclusive percentile |
| PERCENTILE.EXC | PERCENTILE.EXC(column, percentile) | Exclusive percentile |
Iterator (X) Functions
| Function | Syntax | Purpose |
|---|---|---|
| SUMX | SUMX(table, expression) | Sum of expression per row |
| AVERAGEX | AVERAGEX(table, expression) | Average of expression per row |
| MINX | MINX(table, expression) | Min of expression per row |
| MAXX | MAXX(table, expression) | Max of expression per row |
| COUNTX | COUNTX(table, expression) | Count non-blank expression results |
| RANKX | RANKX(table, expression[, value[, order[, ties]]]) | Rank based on expression |
| PRODUCTX | PRODUCTX(table, expression) | Product of expression per row |
| CONCATENATEX | CONCATENATEX(table, expression, delimiter[, orderBy[, order]]) | Concatenate expression results |
Filter Functions
| Function | Syntax | Purpose |
|---|---|---|
| CALCULATE | CALCULATE(expression, filter1, ...) | Evaluate in modified filter context |
| CALCULATETABLE | CALCULATETABLE(table, filter1, ...) | Return table in modified context |
| FILTER | FILTER(table, condition) | Return rows matching condition |
| ALL | ALL(table/column) | Remove all filters |
| ALLEXCEPT | ALLEXCEPT(table, column1, ...) | Remove all filters except specified |
| ALLSELECTED | ALLSELECTED([column]) | Restore filters to query context |
| REMOVEFILTERS | REMOVEFILTERS([table/column]) | Remove filters (clearer than ALL) |
| KEEPFILTERS | KEEPFILTERS(filter) | Intersect with existing filter |
| SELECTEDVALUE | SELECTEDVALUE(column[, alternateResult]) | Return value if single selection |
| HASONEVALUE | HASONEVALUE(column) | True if single value in context |
| HASONEFILTER | HASONEFILTER(column) | True if single filter on column |
| ISFILTERED | ISFILTERED(column) | True if column is filtered |
| ISCROSSFILTERED | ISCROSSFILTERED(column) | True if column is cross-filtered |
| VALUES | VALUES(column/table) | Distinct values respecting filters |
| DISTINCT | DISTINCT(column/table) | Distinct values respecting filters |
| FILTERS | FILTERS(column) | Table of filter values |
| EARLIER | EARLIER(column[, number]) | Value in outer row context |
| EARLIEST | EARLIEST(column) | Value in outermost row context |
Table Functions
| Function | Syntax | Purpose |
|---|---|---|
| ADDCOLUMNS | ADDCOLUMNS(table, name, expression, ...) | Add calculated columns |
| SELECTCOLUMNS | SELECTCOLUMNS(table, name, expression, ...) | Project columns |
| SUMMARIZE | SUMMARIZE(table, groupCol, ...) | Group by with optional extensions |
| SUMMARIZECOLUMNS | SUMMARIZECOLUMNS(groupCol, ..., filterTable, ..., name, expression, ...) | Optimized group by (preferred) |
| GROUPBY | GROUPBY(table, groupCol, ..., name, CURRENTGROUP(), ...) | Group with current group access |
| CROSSJOIN | CROSSJOIN(table1, table2, ...) | Cartesian product |
| UNION | UNION(table1, table2, ...) | Combine tables (append rows) |
| INTERSECT | INTERSECT(table1, table2) | Common rows between tables |
| EXCEPT | EXCEPT(table1, table2) | Rows in table1 not in table2 |
| NATURALINNERJOIN | NATURALINNERJOIN(table1, table2) | Inner join on common columns |
| NATURALLEFTOUTERJOIN | NATURALLEFTOUTERJOIN(table1, table2) | Left join on common columns |
| GENERATE | GENERATE(table1, table2Expression) | Cross apply (row context) |
| GENERATEALL | GENERATEALL(table1, table2Expression) | Cross apply (preserves blanks) |
| ROW | ROW(name, expression, ...) | Single-row table |
| DATATABLE | DATATABLE(name, type, ...) | Inline table definition |
| TREATAS | TREATAS(table, column1, ...) | Apply table as filter on columns |
| TOPN | TOPN(n, table, expression[, order]) | Top N rows by expression |
Time Intelligence Functions
| Function | Syntax | Purpose |
|---|---|---|
| DATESYTD | DATESYTD(dateColumn[, yearEndDate]) | Date table filtered YTD |
| DATESMTD | DATESMTD(dateColumn) | Date table filtered MTD |
| DATESQTD | DATESQTD(dateColumn) | Date table filtered QTD |
| TOTALYTD | TOTALYTD(expression, dateColumn[, filter[, yearEndDate]]) | Year-to-date total |
| TOTALMTD | TOTALMTD(expression, dateColumn[, filter]) | Month-to-date total |
| TOTALQTD | TOTALQTD(expression, dateColumn[, filter]) | Quarter-to-date total |
| SAMEPERIODLASTYEAR | SAMEPERIODLASTYEAR(dateColumn) | Same dates, prior year |
| PREVIOUSMONTH | PREVIOUSMONTH(dateColumn) | Entire previous month |
| PREVIOUSQUARTER | PREVIOUSQUARTER(dateColumn) | Entire previous quarter |
| PREVIOUSYEAR | PREVIOUSYEAR(dateColumn) | Entire previous year |
| NEXTMONTH | NEXTMONTH(dateColumn) | Entire next month |
| NEXTQUARTER | NEXTQUARTER(dateColumn) | Entire next quarter |
| NEXTYEAR | NEXTYEAR(dateColumn) | Entire next year |
| DATEADD | DATEADD(dateColumn, intervals, interval) | Shift dates |
| DATESINPERIOD | DATESINPERIOD(dateColumn, startDate, intervals, interval) | Date range |
| DATESBETWEEN | DATESBETWEEN(dateColumn, startDate, endDate) | Date range (explicit) |
| PARALLELPERIOD | PARALLELPERIOD(dateColumn, intervals, interval) | Entire shifted period |
| OPENINGBALANCEMONTH | OPENINGBALANCEMONTH(expression, dateColumn[, filter]) | Opening balance |
| CLOSINGBALANCEMONTH | CLOSINGBALANCEMONTH(expression, dateColumn[, filter]) | Closing balance |
| FIRSTDATE | FIRSTDATE(dateColumn) | First date in context |
| LASTDATE | LASTDATE(dateColumn) | Last date in context |
| STARTOFMONTH | STARTOFMONTH(dateColumn) | First day of month |
| ENDOFMONTH | ENDOFMONTH(dateColumn) | Last day of month |
| STARTOFQUARTER | STARTOFQUARTER(dateColumn) | First day of quarter |
| ENDOFQUARTER | ENDOFQUARTER(dateColumn) | Last day of quarter |
| STARTOFYEAR | STARTOFYEAR(dateColumn[, yearEndDate]) | First day of year |
| ENDOFYEAR | ENDOFYEAR(dateColumn[, yearEndDate]) | Last day of year |
Logical Functions
| Function | Syntax | Purpose |
|---|---|---|
| IF | IF(condition, trueResult[, falseResult]) | Conditional |
| SWITCH | SWITCH(expression, value1, result1, ...[, else]) | Multi-branch |
| AND | AND(cond1, cond2) or cond1 && cond2 | Logical AND |
| OR | OR(cond1, cond2) or `cond1 \ | \ |
| NOT | NOT(condition) | Logical NOT |
| TRUE | TRUE() | Boolean true |
| FALSE | FALSE() | Boolean false |
| COALESCE | COALESCE(expr1, expr2, ...) | First non-blank |
| IFERROR | IFERROR(expression, alternateResult) | Error handling |
Text Functions
| Function | Syntax | Purpose |
|---|---|---|
| CONCATENATE | CONCATENATE(text1, text2) | Join two strings |
| FORMAT | FORMAT(value, formatString) | Format as text |
| LEFT | LEFT(text, numChars) | Left substring |
| RIGHT | RIGHT(text, numChars) | Right substring |
| MID | MID(text, startPos, numChars) | Middle substring |
| LEN | LEN(text) | String length |
| UPPER | UPPER(text) | Uppercase |
| LOWER | LOWER(text) | Lowercase |
| TRIM | TRIM(text) | Remove extra spaces |
| SUBSTITUTE | SUBSTITUTE(text, oldText, newText[, instance]) | Replace text |
| SEARCH | SEARCH(findText, withinText[, startPos]) | Find position (case-insensitive) |
| FIND | FIND(findText, withinText[, startPos]) | Find position (case-sensitive) |
| BLANK | BLANK() | Return blank value |
| ISBLANK | ISBLANK(value) | Test for blank |
| COMBINEVALUES | COMBINEVALUES(delimiter, value1, ...) | Concatenate with delimiter |
| CONTAINSSTRING | CONTAINSSTRING(withinText, findText) | Case-insensitive contains |
| CONTAINSSTRINGEXACT | CONTAINSSTRINGEXACT(withinText, findText) | Case-sensitive contains |
Math and Statistical Functions
| Function | Syntax | Purpose |
|---|---|---|
| DIVIDE | DIVIDE(numerator, denominator[, alternateResult]) | Safe division |
| ABS | ABS(number) | Absolute value |
| ROUND | ROUND(number, digits) | Round |
| ROUNDUP | ROUNDUP(number, digits) | Round up |
| ROUNDDOWN | ROUNDDOWN(number, digits) | Round down |
| INT | INT(number) | Integer (floor) |
| MOD | MOD(number, divisor) | Modulo |
| POWER | POWER(number, power) | Exponentiation |
| SQRT | SQRT(number) | Square root |
| LN | LN(number) | Natural logarithm |
| LOG | LOG(number[, base]) | Logarithm |
| RAND | RAND() | Random 0-1 |
| SIGN | SIGN(number) | Sign (-1, 0, 1) |
Relationship Functions
| Function | Syntax | Purpose |
|---|---|---|
| RELATED | RELATED(column) | Value from related table (many-to-one) |
| RELATEDTABLE | RELATEDTABLE(table) | Related rows (one-to-many) |
| USERELATIONSHIP | USERELATIONSHIP(column1, column2) | Activate inactive relationship |
| CROSSFILTER | CROSSFILTER(col1, col2, direction) | Change cross-filter |
| LOOKUPVALUE | LOOKUPVALUE(resultColumn, searchColumn, searchValue, ...) | Lookup without relationship |
| TREATAS | TREATAS(table, column, ...) | Virtual relationship |
Window Functions (2023+)
| Function | Syntax | Purpose |
|---|---|---|
| WINDOW | WINDOW(from, from_type, to, to_type[, relation][, orderBy][, blanks][, partitionBy][, matchBy][, reset]) | Return rows within a window range |
| INDEX | INDEX(n[, relation][, orderBy][, blanks][, partitionBy][, matchBy]) | Return the nth row |
| OFFSET | OFFSET(delta[, relation][, orderBy][, blanks][, partitionBy][, matchBy]) | Return row offset from current |
| RANK | RANK([ties][, relation][, orderBy][, blanks][, partitionBy][, matchBy][, reset]) | Rank in partition |
| ROWNUMBER | ROWNUMBER([relation][, orderBy][, blanks][, partitionBy][, matchBy][, reset]) | Unique row number in partition |
| ORDERBY | ORDERBY(column[, order], ...) | Define sort order for window |
| PARTITIONBY | PARTITIONBY(column, ...) | Define partition columns for window |
| MATCHBY | MATCHBY(column, ...) | Identify current row in window |
Visual Calculation Functions (2024-2026)
| Function | Syntax | Purpose |
|---|---|---|
| FIRST | FIRST(expression[, axis][, blanks][, reset]) | Value from first row of axis |
| LAST | LAST(expression[, axis][, blanks][, reset]) | Value from last row of axis |
| PREVIOUS | PREVIOUS(expression[, axis][, blanks][, reset]) | Value from previous row |
| NEXT | NEXT(expression[, axis][, blanks][, reset]) | Value from next row |
| LOOKUP | LOOKUP(expression, filter1, value1, ...) | Filtered lookup in visual matrix (June 2025) |
| LOOKUPWITHTOTALS | LOOKUPWITHTOTALS(expression, filter1, value1, ...) | Filtered lookup respecting totals (June 2025) |
New Functions (2025-2026)
| Function | Syntax | Purpose | Added |
|---|---|---|---|
| TABLEOF | TABLEOF(column/measure/calendar) | Table reference that auto-adapts to renames | Feb 2026 |
| TOTALWTD | TOTALWTD(expression, dateColumn[, filter]) | Week-to-date total | Sep 2025 |
| CLOSINGBALANCEWEEK | CLOSINGBALANCEWEEK(expression, dateColumn[, filter]) | Closing balance for the week | Sep 2025 |
| OPENINGBALANCEWEEK | OPENINGBALANCEWEEK(expression, dateColumn[, filter]) | Opening balance for the week | Sep 2025 |
| STARTOFWEEK | STARTOFWEEK(dateColumn) | First date of current week | Sep 2025 |
| ENDOFWEEK | ENDOFWEEK(dateColumn) | Last date of current week | Sep 2025 |
| NEXTWEEK | NEXTWEEK(dateColumn) | Table of dates for next week | Sep 2025 |
| PREVIOUSWEEK | PREVIOUSWEEK(dateColumn) | Table of dates for previous week | Sep 2025 |
| DATESWTD | DATESWTD(dateColumn) | Week-to-date date filter | Sep 2025 |
| LINEST / LINESTX | See Statistical Functions below | Linear regression (least squares) | Feb 2023 |
Statistical Functions
| Function | Syntax | Purpose |
|---|---|---|
| LINEST | LINEST(table, yColumn, xColumn, ...) | Least-squares linear regression |
| LINESTX | LINESTX(table, yExpression, xExpression, ...) | Least-squares with expressions per row |
Information Functions
| Function | Syntax | Purpose |
|---|---|---|
| ISBLANK | ISBLANK(value) | Test blank |
| ISERROR | ISERROR(value) | Test error |
| ISLOGICAL | ISLOGICAL(value) | Test boolean |
| ISNUMBER | ISNUMBER(value) | Test number |
| ISTEXT | ISTEXT(value) | Test text |
| ISNONTEXT | ISNONTEXT(value) | Test non-text |
| USERPRINCIPALNAME | USERPRINCIPALNAME() | Current user UPN (for RLS) |
| USERNAME | USERNAME() | Current user (domain\user or UPN) |
| SELECTEDMEASURE | SELECTEDMEASURE() | Current measure (calculation groups) |
| SELECTEDMEASURENAME | SELECTEDMEASURENAME() | Name of current measure |
| NAMEOF | NAMEOF(column/measure/calendar) | Name as text string |
| TABLEOF | TABLEOF(column/measure/calendar) | Table reference (Feb 2026) |
INFO DAX Functions (Model Metadata)
INFO functions return metadata about the semantic model as tables. They replace DMVs with native DAX capability.
INFO.VIEW Functions (Usable in calculated tables, columns, measures, and DAX queries)
| Function | Returns |
|---|---|
INFO.VIEW.TABLES() | All tables (name, description, storage mode, hidden) |
INFO.VIEW.COLUMNS() | All columns (name, data type, hidden, table) |
INFO.VIEW.MEASURES() | All measures (name, expression, format string) |
INFO.VIEW.RELATIONSHIPS() | All relationships (from/to table/column, cardinality, direction) |
INFO Functions (DAX query view only, require semantic model admin permissions)
| Function | Returns |
|---|---|
INFO.TABLES() | All tables (schema rowset format) |
INFO.COLUMNS() | All columns (schema rowset format) |
INFO.MEASURES() | All measures (schema rowset format) |
INFO.RELATIONSHIPS() | All relationships (schema rowset format) |
INFO.PARTITIONS() | All partitions |
INFO.ROLES() | All security roles |
INFO.ROLEMEMBERSHIPS() | Role membership details |
INFO.TABLEPERMISSIONS() | Table-level permissions |
INFO.COLUMNPERMISSIONS() | Column-level permissions (OLS) |
INFO.CALCULATIONGROUPS() | Calculation group definitions |
INFO.CALCULATIONITEMS() | Calculation items in groups |
INFO.EXPRESSIONS() | M expressions (partitions) |
INFO.HIERARCHIES() | Hierarchy definitions |
INFO.LEVELS() | Hierarchy level details |
INFO.CULTURES() | Translation cultures |
INFO.PERSPECTIVES() | Perspectives |
INFO.FUNCTIONS() | Available DAX functions |
INFO.USERDEFINEDFUNCTIONS() | User-defined functions (March 2026) |
INFO.STORAGETABLES() | In-memory table statistics |
INFO.STORAGETABLECOLUMNS() | In-memory column statistics |
INFO.STORAGETABLECOLUMNSEGMENTS() | Column segment storage info |
INFO.ANNOTATIONS() | Model annotations |
INFO.DATASOURCES() | Data source definitions |
INFO.REFRESHPOLICIES() | Incremental refresh policies |
INFO.FORMATSTRINGDEFINITIONS() | Dynamic format string definitions |
INFO.DEPENDENCIES() | Calculation dependency graph |
Example -- self-documenting model:
// Create a calculated table that lists all measures
EVALUATE
ADDCOLUMNS(
SELECTCOLUMNS(
INFO.VIEW.MEASURES(),
"Measure", [Name],
[Description],
"DAX Formula", [Expression],
"State", [State]
),
"Model name", "My Semantic Model",
"As of date", NOW()
)Advanced DAX Patterns
1. Virtual Relationships with TREATAS
Use TREATAS to create virtual relationships without physical model relationships:
Sales by Budget Category =
CALCULATE(
[Total Sales],
TREATAS(
VALUES(Budget[CategoryID]),
Sales[CategoryID]
)
)When to use: Connecting tables that share a key but should not have a physical relationship (e.g., budget vs actual from different sources).
2. Dynamic Segmentation
Create dynamic segmentation without adding columns to the model:
// Step 1: Create a disconnected segmentation table
// (using DATATABLE or a calculated table)
Segments = DATATABLE(
"Segment", STRING, "Min", INTEGER, "Max", INTEGER,
{
{"Low", 0, 100},
{"Medium", 100, 500},
{"High", 500, 10000}
}
)
// Step 2: Measure using the segmentation
Sales by Segment =
CALCULATE(
[Total Sales],
FILTER(
ALL(Sales[Amount]),
VAR CurrentAmount = Sales[Amount]
VAR SegMin = SELECTEDVALUE(Segments[Min])
VAR SegMax = SELECTEDVALUE(Segments[Max])
RETURN CurrentAmount >= SegMin && CurrentAmount < SegMax
)
)3. Parent-Child Hierarchy (Unary Operator)
Flatten a parent-child hierarchy for Power BI:
// PATH function creates a pipe-delimited path
EmployeePath = PATH(Employee[EmployeeID], Employee[ManagerID])
// PATHLENGTH for depth
Depth = PATHLENGTH(Employee[EmployeePath])
// Extract each level
Level1 = LOOKUPVALUE(
Employee[EmployeeName],
Employee[EmployeeID],
VALUE(PATHITEM(Employee[EmployeePath], 1))
)
Level2 = LOOKUPVALUE(
Employee[EmployeeName],
Employee[EmployeeID],
VALUE(PATHITEM(Employee[EmployeePath], 2))
)
// Rollup measure across hierarchy
Team Sales =
VAR CurrentPath = SELECTEDVALUE(Employee[EmployeePath])
RETURN
CALCULATE(
[Total Sales],
FILTER(
ALL(Employee),
PATHCONTAINS(Employee[EmployeePath],
SELECTEDVALUE(Employee[EmployeeID]))
)
)4. Basket Analysis (Products Bought Together)
// Customers who bought Product A
CustomersWithA =
CALCULATE(
DISTINCTCOUNT(Sales[CustomerID]),
FILTER(ALL(Sales), Sales[ProductID] = SELECTEDVALUE(Products[ProductID]))
)
// Customers who bought BOTH Product A and current product
CustomersWith Both =
VAR SelectedProduct = SELECTEDVALUE(Products[ProductID])
RETURN
CALCULATE(
DISTINCTCOUNT(Sales[CustomerID]),
FILTER(
ALL(Sales),
Sales[CustomerID] IN
SELECTCOLUMNS(
FILTER(ALL(Sales), Sales[ProductID] = SelectedProduct),
"CID", Sales[CustomerID]
)
)
)5. New vs Returning Customers
New Customers =
VAR CurrentDate = MAX(Date[Date])
VAR CurrentMonth = EOMONTH(CurrentDate, 0)
VAR MonthStart = EOMONTH(CurrentDate, -1) + 1
RETURN
CALCULATE(
DISTINCTCOUNT(Sales[CustomerID]),
FILTER(
ALL(Sales),
Sales[OrderDate] >= MonthStart && Sales[OrderDate] <= CurrentMonth
),
FILTER(
ALL(Sales),
NOT(
Sales[CustomerID] IN
SELECTCOLUMNS(
FILTER(ALL(Sales), Sales[OrderDate] < MonthStart),
"CID", Sales[CustomerID]
)
)
)
)
Returning Customers =
[Total Customers] - [New Customers]6. ABC Classification (Pareto)
ABC Category =
VAR TotalSales = CALCULATE([Total Sales], ALL(Products))
VAR ProductSales =
ADDCOLUMNS(
ALL(Products[ProductID], Products[ProductName]),
"ProdSales", [Total Sales]
)
VAR RankedProducts =
ADDCOLUMNS(
ProductSales,
"RunningTotal",
SUMX(
FILTER(ProductSales, [ProdSales] >= EARLIER([ProdSales])),
[ProdSales]
)
)
VAR CurrentRunning =
MAXX(
FILTER(RankedProducts,
[ProductID] = SELECTEDVALUE(Products[ProductID])),
[RunningTotal]
)
VAR Percentage = DIVIDE(CurrentRunning, TotalSales)
RETURN
SWITCH(TRUE(),
Percentage <= 0.7, "A",
Percentage <= 0.9, "B",
"C"
)7. Semi-Additive Measures (Snapshot/Balance)
For measures that should not sum across time (e.g., inventory balance, account balance):
// Last known balance
Current Balance =
CALCULATE(
SUM(AccountBalance[Balance]),
LASTDATE(Date[Date])
)
// Average daily balance
Average Daily Balance =
AVERAGEX(
VALUES(Date[Date]),
CALCULATE(SUM(AccountBalance[Balance]))
)
// Opening balance
Opening Balance =
CALCULATE(
SUM(AccountBalance[Balance]),
FIRSTDATE(Date[Date])
)8. Dynamic Top N with "Others"
Sales with Others =
VAR TopN = 10
VAR RankVal =
RANKX(
ALL(Products[ProductName]),
[Total Sales],
,
DESC
)
RETURN
IF(
RankVal <= TopN,
[Total Sales],
CALCULATE(
[Total Sales],
FILTER(
ALL(Products[ProductName]),
RANKX(ALL(Products[ProductName]), [Total Sales], , DESC) > TopN
)
)
)9. Currency Conversion
Sales in USD =
SUMX(
Sales,
VAR SaleDate = Sales[OrderDate]
VAR SourceCurrency = RELATED(Region[CurrencyCode])
VAR ExchangeRate =
CALCULATE(
SELECTEDVALUE(ExchangeRates[Rate]),
ExchangeRates[Date] = SaleDate,
ExchangeRates[FromCurrency] = SourceCurrency,
ExchangeRates[ToCurrency] = "USD"
)
RETURN Sales[Amount] * ExchangeRate
)10. Disconnected Slicer Tables
Create slicers that control measure behavior without filtering data:
// Disconnected table for metric selection
MetricSelector = DATATABLE(
"Metric", STRING,
{{"Revenue"}, {"Profit"}, {"Units"}, {"Margin %"}}
)
// Dynamic measure
Selected Metric =
SWITCH(
SELECTEDVALUE(MetricSelector[Metric]),
"Revenue", [Total Revenue],
"Profit", [Total Profit],
"Units", [Total Units],
"Margin %", [Profit Margin %],
BLANK()
)11. Last Non-Blank Value
Last Reported Value =
CALCULATE(
SELECTEDVALUE(Metrics[Value]),
LASTNONBLANK(
Date[Date],
CALCULATE(COUNTROWS(Metrics))
)
)12. Cumulative Total
Cumulative Sales =
CALCULATE(
[Total Sales],
FILTER(
ALL(Date[Date]),
Date[Date] <= MAX(Date[Date])
)
)
// More performant version:
Cumulative Sales v2 =
VAR LastDate = MAX(Date[Date])
RETURN
CALCULATE(
[Total Sales],
Date[Date] <= LastDate,
REMOVEFILTERS(Date[Date])
)13. Percentage of Parent (Visual Totals)
// % of parent category
% of Category =
DIVIDE(
[Total Sales],
CALCULATE([Total Sales], ALLSELECTED(Products[SubCategory]))
)
// % of grand total
% of Grand Total =
DIVIDE(
[Total Sales],
CALCULATE([Total Sales], ALL(Products))
)
// % of column total (respecting slicer)
% of Column =
DIVIDE(
[Total Sales],
CALCULATE([Total Sales], ALLSELECTED())
)14. Handling Many-to-Many Relationships
// Using bridge table with TREATAS
Sales for Student =
VAR StudentCourses =
CALCULATETABLE(
VALUES(StudentCourse[CourseID]),
TREATAS(VALUES(Students[StudentID]), StudentCourse[StudentID])
)
RETURN
CALCULATE(
[Total Sales],
TREATAS(StudentCourses, CourseSales[CourseID])
)15. SWITCH with Multiple Conditions
Customer Tier =
VAR TotalSpend = [Customer Lifetime Value]
VAR OrderCount = [Total Orders]
RETURN
SWITCH(
TRUE(),
TotalSpend > 10000 && OrderCount > 50, "Platinum",
TotalSpend > 5000 && OrderCount > 20, "Gold",
TotalSpend > 1000 && OrderCount > 5, "Silver",
"Bronze"
)16. Window Functions - Running Total
Running Total =
CALCULATE(
[Total Sales],
WINDOW(1, ABS, 0, REL,
ALLSELECTED(Date[YearMonth]),
ORDERBY(Date[YearMonth], ASC)
)
)17. Window Functions - Moving Average
3M Moving Average =
VAR WindowSize = 3
RETURN
AVERAGEX(
WINDOW(-WindowSize + 1, REL, 0, REL,
ALLSELECTED(Date[YearMonth]),
ORDERBY(Date[YearMonth], ASC)
),
[Total Sales]
)18. Window Functions - Rank with PARTITIONBY
Product Rank in Category =
RANKX(
ALLSELECTED(Products[ProductName]),
[Total Sales],
,
DESC
)
// Using RANK function (cleaner for window operations):
Product Rank v2 =
RANK(
DENSE,
ALLSELECTED(Products[ProductName]),
ORDERBY([Total Sales], DESC),
PARTITIONBY(Products[Category])
)19. User-Defined Functions (UDF) Pattern (September 2025 Preview)
// Define once, reuse across measures
DEFINE
FUNCTION SafeGrowth = (current : NUMERIC, previous : NUMERIC) =>
IF(previous = 0 || ISBLANK(previous),
BLANK(),
DIVIDE(current - previous, ABS(previous))
)
// Use in multiple measures:
// YoY Growth = SafeGrowth([Total Sales], [PY Sales])
// MoM Growth = SafeGrowth([Total Sales], [PM Sales])Parameter modes:
val(eager): Evaluated before function executes -- use for simple scalar inputsexpr(lazy): Evaluated in the function's context -- use for context-sensitive calculations
20. Dynamic Format Strings with Calculation Groups
// Calculation group: Currency Converter
// Calculation item: "Convert to Target"
// Expression:
VAR TargetCurrency = SELECTEDVALUE(CurrencySelector[Currency], "USD")
VAR Rate = LOOKUPVALUE(ExchangeRates[Rate],
ExchangeRates[ToCurrency], TargetCurrency,
ExchangeRates[Date], MAX(Date[Date]))
RETURN
SELECTEDMEASURE() * Rate
// Format string expression:
VAR TargetCurrency = SELECTEDVALUE(CurrencySelector[Currency], "USD")
RETURN
SWITCH(TargetCurrency,
"USD", "$#,##0.00",
"EUR", "€#,##0.00",
"GBP", "£#,##0.00",
"JPY", "¥#,##0",
"#,##0.00"
)21. INFO Functions for Self-Documenting Models
// Create a calculated table that lists all measures
Model Documentation =
ADDCOLUMNS(
SELECTCOLUMNS(
INFO.VIEW.MEASURES(),
"Measure", [Name],
"Description", [Description],
"DAX", [Expression],
"Format", [FormatString]
),
"Table", RELATED(INFO.VIEW.TABLES()[Name]),
"Updated", NOW()
)22. Calendar-Based Time Intelligence (September 2025 Preview)
// Week-to-date total using custom calendar
WTD Sales = TOTALWTD([Total Sales], Date[Date])
// Previous week comparison
PW Sales = CALCULATE([Total Sales], PREVIOUSWEEK(Date[Date]))
// Week-over-Week growth
WoW Growth % =
VAR Current = [Total Sales]
VAR PW = CALCULATE([Total Sales], PREVIOUSWEEK(Date[Date]))
RETURN DIVIDE(Current - PW, PW)Requires a calendar defined in the model via Enhanced Time Intelligence preview feature.