关于 dax-mastery
面向 Power BI 模型作者的 DAX 参考资料,讲解上下文、计算、时间智能、迭代器和性能模式。还涵盖计算组、窗口函数及较新的语言功能。
主要功能
- 01行上下文与筛选上下文
- 02CALCULATE 筛选逻辑
- 03时间智能函数与模式
- 04迭代器及表函数
- 05计算组与字段参数
- 06窗口函数和视觉计算
适用场景
- 01编写度量值和计算列
- 02制作同比及滚动期间分析
- 03优化 DAX 表达式性能
- 04为模型复用计算模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill dax-mastery安装后在 Claude Code 中输入/dax-mastery确认已加载。
附带文件references/dax-function-categories.mdreferences/dax-patterns-advanced.md
详细介绍
DAX(数据分析表达式)精通指南
概述
完整的 DAX 参考指南,涵盖求值上下文、CALCULATE、时间智能、迭代器、表函数、性能优化和高级模式。DAX 是 Power BI 度量值、计算列、计算表和 RLS 筛选器所使用的公式语言。
求值上下文
行上下文
- 由以下内容创建:计算列、迭代器(SUMX、FILTER、AVERAGEX 等)、逐行求值
- 表中的每一行都有自己的行上下文
- 直接访问列:
Sales[Amount] - 嵌套迭代器会创建嵌套行上下文
筛选器上下文
- 由以下内容创建:切片器、视觉对象筛选器、页面筛选器、报表筛选器、CALCULATE 参数
- 确定聚合函数可以看到哪些行
- 不提供行级访问(度量值中不能在未经聚合的情况下直接使用
Sales[Amount])
上下文转换
- CALCULATE 将行上下文转换为筛选器上下文
- 在迭代器中引用度量值时会发生
- 每行的列值都会成为筛选器参数
// 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——最重要的函数
CALCULATE(<expression>, <filter1>, <filter2>, ...)
筛选器参数类型:
| 类型 | 示例 | 行为 |
|---|---|---|
| 布尔值(表筛选器) | Products[Color] = "Red" | 添加筛选器,保留现有上下文 |
| 表达式 | FILTER(ALL(Products), Products[Price] > 100) | 替换受影响列上的筛选器 |
| REMOVEFILTERS | REMOVEFILTERS(Products[Color]) | 移除列上的现有筛选器 |
| ALL | ALL(Products) | 移除表上的所有筛选器 |
| KEEPFILTERS | KEEPFILTERS(Products[Color] = "Red") | 与现有筛选器取交集 |
| USERELATIONSHIP | USERELATIONSHIP(Sales[ShipDate], Date[Date]) | 激活非活动关系 |
| CROSSFILTER | CROSSFILTER(Sales[ProductID], Products[ID], Both) | 更改交叉筛选方向 |
重要规则:
- 布尔筛选器是 FILTER(ALL(column), condition) 的语法糖
- 布尔筛选器会替换该列上的现有筛选器
- 使用 KEEPFILTERS 将条件添加到现有筛选器中(取交集)
- CALCULATE 修饰符(ALL、REMOVEFILTERS)会在筛选器参数之前执行
时间智能速查
前提条件: 使用有效的日期表,并将其标记为日期表,且包含连续的日期列。
| 函数 | 用途 | 示例 |
|---|---|---|
| TOTALYTD | 年初至今 | TOTALYTD([Sales], Date[Date]) |
| TOTALMTD | 月初至今 | TOTALMTD([Sales], Date[Date]) |
| TOTALQTD | 季初至今 | TOTALQTD([Sales], Date[Date]) |
| SAMEPERIODLASTYEAR | 上一年的同期 | CALCULATE([Sales], SAMEPERIODLASTYEAR(Date[Date])) |
| DATEADD | 按间隔偏移 | CALCULATE([Sales], DATEADD(Date[Date], -1, MONTH)) |
| PARALLELPERIOD | 完整的偏移周期 | CALCULATE([Sales], PARALLELPERIOD(Date[Date], -1, QUARTER)) |
| DATESYTD | 筛选为年初至今日期的日期表 | CALCULATE([Sales], DATESYTD(Date[Date])) |
| DATESBETWEEN | 日期范围 | CALCULATE([Sales], DATESBETWEEN(Date[Date], start, end)) |
| PREVIOUSMONTH | 上一个完整月份 | CALCULATE([Sales], PREVIOUSMONTH(Date[Date])) |
| PREVIOUSYEAR | 上一个完整年份 | CALCULATE([Sales], PREVIOUSYEAR(Date[Date])) |
常见时间智能模式:
// 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])
)
变量(VAR/RETURN)
始终使用变量以提高可读性和性能:
Profit Margin % =
VAR TotalRevenue = SUM(Sales[Revenue])
VAR TotalCost = SUM(Sales[Cost])
VAR Profit = TotalRevenue - TotalCost
RETURN
DIVIDE(Profit, TotalRevenue)
规则:
- 变量只求值一次(重复使用时有性能优势)
- 变量会捕获其定义位置的筛选器上下文
- 变量可以保存标量值或表
- 使用有意义的名称(不要使用
x、temp)
迭代器函数
迭代器逐行扫描表,并创建行上下文:
| 函数 | 用途 |
|---|---|
| SUMX | 对逐行计算的表达式求和 |
| AVERAGEX | 计算逐行表达式的平均值 |
| MINX / MAXX | 计算逐行表达式的最小值/最大值 |
| COUNTX | 统计非空表达式结果的数量 |
| RANKX | 根据表达式进行排名 |
| FILTER | 返回符合条件的表行 |
| ADDCOLUMNS | 向表中添加计算列 |
| SELECTCOLUMNS | 返回包含所选列或计算列的表 |
| GENERATE | 使用行上下文进行交叉联接 |
// Weighted average price
Weighted Avg Price =
SUMX(
Sales,
Sales[Quantity] * RELATED(Products[UnitPrice])
) / SUM(Sales[Quantity])
计算组
通过定义可重复使用的计算模式,减少度量值泛滥:
// 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)
可通过 Tabular Editor、Desktop 中的 TMDL 视图或 TOM/.NET SDK 创建。
字段参数
让用户能够在视觉对象中动态切换维度或度量值:
// 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)
}
用户定义函数(2025 年 9 月预览版)
这是自变量(2015 年)以来最重要的 DAX 语言更新。定义可重复使用的参数化函数:
// 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 88
参数类型: NUMERIC、Scalar、Table、AnyVal、AnyRef、CalendarRef、ColumnRef、MeasureRef、TableRef
参数模式: val(急切求值)或 expr(惰性/依赖上下文的求值)
用法: 定义并保存到模型后,可在度量值、计算列、视觉对象计算和其他 UDF 中调用 UDF。
启用: File > Options > Preview features > DAX user-defined functions
窗口函数(WINDOW、INDEX、OFFSET)
用于相对行和范围计算的 DAX 窗口函数:
// 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))
)
关键子句:
ORDERBY-- 窗口内的排序顺序PARTITIONBY-- 行的子集(“窗口”分区)MATCHBY-- 在上下文不明确时标识当前行
视觉对象计算(2024-2026)
作用域限定于视觉对象矩阵,而非数据模型的计算:
| 函数 | 用途 |
|---|---|
| FIRST | 获取轴的第一行中的值 |
| LAST | 获取轴的最后一行中的值 |
| PREVIOUS | 获取上一行中的值 |
| NEXT | 获取下一行中的值 |
| LOOKUP | 获取筛选后的值(2025 年 6 月) |
| LOOKUPWITHTOTALS | 获取筛选后的值,并遵循总计(2025 年 6 月) |
视觉对象计算按视觉对象定义,不会影响语义模型。
基于日历的时间智能(2025 年 9 月预览版)
定义自定义日历(财年、零售、13 个月、农历),并使用 8 个新的基于周的函数:
| 函数 | 用途 |
|---|---|
| TOTALWTD | 计算本周迄今的累计值 |
| CLOSINGBALANCEWEEK | 计算本周的期末余额 |
| OPENINGBALANCEWEEK | 计算本周的期初余额 |
| STARTOFWEEK | 获取当前周的第一天 |
| ENDOFWEEK | 获取当前周的最后一天 |
| NEXTWEEK | 返回下周的日期表 |
| PREVIOUSWEEK | 返回上周的日期表 |
| DATESWTD | 筛选本周迄今的日期 |
启用: File > Options > Preview features > Enhanced DAX Time Intelligence
动态格式字符串
应用依赖上下文的格式设置,而无需转换为文本(2025 年 1 月及之后在 Desktop 和 Report Server 中正式发布):
// 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")
相较于 FORMAT() 的优势: 保留数值数据类型,从而支持正确的图表呈现和排序。
TABLEOF 和 NAMEOF(2026 年 2 月)
引用模型对象,并在对象重命名时自动适配:
// 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 table
适用于 UDF 中,可编写更安全且不受重命名影响的代码。
常见反模式
| 反模式 | 问题 | 修复方法 |
|---|---|---|
将 FILTER(table, ...) 用作 CALCULATE 参数 | 全表扫描,无法进行引擎优化 | 使用布尔筛选:column = value |
| 嵌套使用 CALCULATE | 上下文覆盖关系难以理解 | 使用单个 CALCULATE 并设置多个筛选条件 |
| 对整个表使用 SUMX 进行简单求和 | 不必要的迭代器 | 对简单的列聚合使用 SUM() |
| 在度量值中使用 FORMAT() 进行排序 | 返回文本,无法进行数值排序 | 使用单独的排序列 |
| 使用计算列进行聚合 | 每行都要存储,浪费内存 | 改用度量值 |
| COUNTROWS(FILTER(table,...)) | 比 CALCULATE(COUNTROWS(table), filter) 慢 | 使用带筛选条件的 CALCULATE |
| 在多个度量值之间复制粘贴 DAX | 难以维护,容易出错 | 使用 UDF(预览版)定义可复用逻辑 |
| 使用 FORMAT() 进行条件显示 | 返回文本,会破坏排序和图表 | 改用动态格式字符串 |
| 过度使用 EARLIER() | 难以理解,是旧式模式 | 使用 VAR 捕获外层上下文 |
| 在窗口函数中忽略 MATCHBY | 行标识不明确 | 当分区中有重复项时,始终指定 MATCHBY |
其他资源
参考文件
references/dax-function-categories.md-- 按类别整理的完整函数参考,包括 INFO 函数、窗口函数以及 2025-2026 年新增内容references/dax-patterns-advanced.md-- 高级模式:虚拟关系、动态分段、父子层级结构、购物篮分析