mcpField
D
dax-mastery

dax-mastery

github.com/josiahsiegel数据与数据库5997 次安装19 周前更新
代码仓库

关于 dax-mastery

面向 Power BI 模型作者的 DAX 参考资料,讲解上下文、计算、时间智能、迭代器和性能模式。还涵盖计算组、窗口函数及较新的语言功能。

主要功能

  1. 01行上下文与筛选上下文
  2. 02CALCULATE 筛选逻辑
  3. 03时间智能函数与模式
  4. 04迭代器及表函数
  5. 05计算组与字段参数
  6. 06窗口函数和视觉计算

适用场景

  1. 01编写度量值和计算列
  2. 02制作同比及滚动期间分析
  3. 03优化 DAX 表达式性能
  4. 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)替换受影响列上的筛选器
REMOVEFILTERSREMOVEFILTERS(Products[Color])移除列上的现有筛选器
ALLALL(Products)移除表上的所有筛选器
KEEPFILTERSKEEPFILTERS(Products[Color] = "Red")与现有筛选器取交集
USERELATIONSHIPUSERELATIONSHIP(Sales[ShipDate], Date[Date])激活非活动关系
CROSSFILTERCROSSFILTER(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 -- 高级模式:虚拟关系、动态分段、父子层级结构、购物篮分析

评论

支持 Markdown