
简介《DAX权威指南》第二版英文原版PDF由Marco Russo与Alberto Ferrari合著、微软授权Pearson出版面向数据分析师、商业智能开发者及Power BI、SQL Server Analysis Services与Excel使用者帮助系统掌握DAX数据建模与复杂计算。全书从DAX基础语法、表函数与求值上下文讲起深入CALCULATE、SUMX、FILTER等核心函数、表间关系与多对多建模、上下文转换等高级特性并覆盖性能优化、最佳实践与真实案例研究兼顾入门与进阶读者。资源包共1个PDF文件约13.03MB为完整原版电子书便于检索与随身查阅。目前已有827人学习下载适合希望深入理解DAX核心概念、提升商业智能分析效率的读者参考研读。1. DAX 知识地图从公式语言到引擎优化的完整链路很多人第一次接触 DAX是从 Power BI 里写一个SUM或CALCULATE开始的觉得它像 Excel 公式的加强版。但真正系统学下去会发现DAX 的难点从来不在函数数量而在于它背后那套「上下文 关系 引擎」的模型。这份《DAX权威指南》第二版原版 PDF 的价值恰恰在于它没有把 DAX 当成函数手册来写而是按「语言基础 → 计算上下文 → 高级建模 → 查询与优化」四层递进铺开二十章内容基本覆盖了 Power BI、SQL Server Analysis Services 和 Excel 三端使用 DAX 的完整链路。这份笔记面向的是已经在写度量值、但总觉得「结果对不上、性能上不去」的人。整张地图可以这样理解前三章打语言底子第四到第七章死磕求值上下文和 CALCULATE第八到第十二章进入时间智能、计算组、层级与表操作第十三到第十六章转向查询编写与高级关系最后四章落到 DAX 引擎、VertiPaq 优化和查询计划分析。复习时按这个顺序走比从函数列表硬背要稳得多。2. 语言底座数据类型、运算符与列/度量值的分工DAX 的语法门槛不高但类型系统和「列 vs 度量值」的分工是后面所有坑的源头。这一层没打牢写出来的公式往往能跑但结果玄学。2.1 数据类型与运算符的隐式转换DAX 的数据类型比想象中少核心就是整数、小数、货币、日期时间、字符串、布尔和二进制。麻烦在于它允许隐式转换而隐式转换是精度丢失和性能损耗的常见来源。比如把文本型日期和真日期比较引擎会尝试转换转换失败就报错转换成功也可能走不到索引。类型典型来源易错点整数 / 小数数值列、聚合结果除法结果默认转小数整数除法要用INT或DIVIDE控制货币财务金额列与小数混算时精度规则不同报表对账要留意日期时间日期表、时间戳文本日期参与比较会触发转换尽量在建模阶段就转好字符串文本列、拼接结果与数值比较时依赖隐式转换容易静默出错布尔逻辑表达式TRUE/FALSE与 1/0 在部分函数里不等价运算符方面、||、IN、NOT这些在筛选条件里用得很多但要注意IN后面跟的是表构造器或列引用不是随便一个列表。比较运算符对 BLANK 的处理也反直觉BLANK 与 0 比较时会被当作 0与空字符串比较时又按文本规则走这类细节在写条件列时最容易翻车。2.2 计算列与度量值什么时候用哪个这是新手第一个分水岭。计算列在数据刷新时逐行算好、存进模型占内存但查询快度量值在查询时按当前筛选上下文动态计算不占存储但依赖上下文。判断标准其实很朴素需要出现在切片器、行标签、关系键上的用计算列需要随筛选变化的聚合结果用度量值。-- 计算列逐行物化适合做分组标签 Customer[Age Group] SWITCH( TRUE(), Customer[Age] 18, Under 18, Customer[Age] 35, 18-34, Customer[Age] 55, 35-54, 55 ) -- 度量值随筛选上下文动态聚合 Total Sales : SUMX( Sales, Sales[Quantity] * Sales[Net Price] )上面两段的关键差别不在语法而在求值时机。计算列里的Customer[Age]是当前行的值天然有行上下文度量值里的SUMX自己开了一个行上下文去遍历Sales。如果把计算列的逻辑硬塞进度量值就会遇到「列引用没有行上下文」的报错反过来把度量值逻辑写进计算列则会得到不随筛选变化的死值。梳理下来会发现很多「结果不对」的问题第一步就该问这个公式到底该是列还是度量值。3. 求值上下文行上下文、筛选上下文与上下文转换DAX 所有让人困惑的行为几乎都能追溯到「当前是什么上下文」。这一章是整个学习路线的重心也是考试和实战里丢分最多的地方。3.1 两种上下文的本质区别行上下文row context出现在计算列和迭代函数内部表示「当前正在处理哪一行」它不会自动筛选关系另一侧的表。筛选上下文filter context出现在度量值求值和CALCULATE内部表示「当前可见的数据子集」它会沿着关系传播。两者最典型的对照就是计算列里写SUM(Sales[Amount])得到的是整表合计因为行上下文不会转成筛选而度量值里写同样的东西得到的是当前筛选下的合计。-- 计算列中行上下文存在但不筛选关系 Sales[Wrong Total] SUM( Sales[Amount] ) -- 每行都是全表合计 -- 度量值中筛选上下文生效 Total Amount : SUM( Sales[Amount] ) -- 随切片器变化3.2 上下文转换CALCULATE 的隐藏动作CALCULATE最核心的行为是上下文转换它把外层的行上下文逐行转成等价的筛选上下文再在这个新筛选上求值。这就是为什么在迭代函数里包一层CALCULATE原本「看不到关系」的行上下文突然能正确关联了。-- 没有上下文转换行上下文不筛选产品表 Sales[Qty All Products] SUM( Sales[Quantity] ) -- 有上下文转换当前行的产品键被转成筛选正确取到对应产品 Sales[Qty By Product] CALCULATE( SUM( Sales[Quantity] ) )参数说明CALCULATE第一个参数是要计算的表达式后面可以跟任意多个筛选修饰符。上下文转换只发生在「行上下文进入 CALCULATE」的那一刻转换的是当前行所有列的值而不只是你关心的那一列。这一点在宽表上会带来性能开销也是后面优化章节反复提到的点。3.3 嵌套行上下文与 EARLIER当迭代函数嵌套、内外层引用同一张表的列时内层默认解析到最近的行上下文想引用外层就得用EARLIER。这个函数名字起得不好实际含义是「上一层行上下文的值」。-- 求每个产品在其类别内的累计排名示例 Sales[Rank In Category] COUNTROWS( FILTER( Sales, Sales[ProductKey] EARLIER( Sales[ProductKey] ) Sales[Amount] EARLIER( Sales[Amount] ) ) ) 1这里两个EARLIER都指向外层Sales的行上下文内层FILTER的Sales[Amount]则是内层当前行。写错顺序或漏掉EARLIER结果会变成「自己和自己比」排名全乱。血泪经验是嵌套超过两层时优先用变量把外层值存下来可读性和正确率都更高。4. CALCULATE 与筛选修饰符KEEPFILTERS、USERELATIONSHIP、CROSSFILTERCALCULATE是 DAX 里唯一能主动改筛选上下文的函数它的修饰符决定了「新筛选是覆盖旧筛选还是叠加」。这一章的重点不是记住函数名而是理解每种修饰符对筛选上下文的作用方向。4.1 筛选覆盖与 KEEPFILTERS 的叠加语义默认情况下CALCULATE里的筛选参数会替换掉同一列上已有的筛选。比如外层已经筛了「2023 年」内层写CALCULATE(..., Date[Year] 2024)结果就是 20242023 被覆盖。如果想让两个条件同时生效交集就要用KEEPFILTERS包住内层筛选。-- 覆盖外层年份筛选被替换 Sales 2024 : CALCULATE( [Total Sales], Date[Year] 2024 ) -- 叠加外层与内层取交集无交集则为空 Sales 2024 Kept : CALCULATE( [Total Sales], KEEPFILTERS( Date[Year] 2024 ) )这个差别在做「占比」「同比」时特别关键。算占比时通常要用ALL或REMOVEFILTERS清掉某列筛选再除以全量如果误用KEEPFILTERS分母会被外层筛选限制住占比恒等于 1这种错误在报表里非常隐蔽。4.2 关系类修饰符USERELATIONSHIP 与 CROSSFILTER一个事实表对日期表往往有多条关系下单日期、发货日期、收货日期但同一时刻只有一条是活动的。USERELATIONSHIP让你在单个度量值里临时启用某条非活动关系而不必改模型。Sales Shipped : CALCULATE( [Total Sales], USERELATIONSHIP( Sales[ShipDateKey], Date[DateKey] ) )CROSSFILTER则用来临时改变关系的筛选传播方向或直接断开关系常见于多对多桥接场景。参数是「关系两端的列 方向」方向可选Both、OneWay、None。注意它只影响当前CALCULATE的求值范围不会改动模型里的关系定义所以适合做局部口径调整不适合当常规建模手段。4.3 求值顺序与循环依赖CALCULATE内部有明确的求值顺序先做上下文转换再依次应用筛选修饰符最后计算表达式。修饰符之间的顺序在多数情况下不影响结果但当多个修饰符作用于同一列时后写的会覆盖先写的。循环依赖则通常出现在计算列互相引用、或度量值通过关系反向依赖自身时报错信息往往指向不明显排查时优先看「这个列/度量值是否间接引用了自己所在的表」。提示调试CALCULATE时先把所有修饰符注释掉确认基础表达式在纯上下文下的结果再逐个加回修饰符比一次性写完再猜要快得多。5. 时间智能、计算组与表函数业务口径的落地模块过了上下文这一关后面的内容就进入「按业务场景组织」的阶段。时间智能解决同比环比计算组解决多指标复用表函数解决查询与中间结果构造三者构成了日常建模的主力工具箱。5.1 时间智能的前提与常见误用时间智能函数TOTALYTD、SAMEPERIODLASTYEAR、DATEADD等全部依赖一张标记为日期表的独立表且必须与事实表有活动关系。没有日期表这些函数要么报错要么给出看似合理实则错误的结果。函数作用前提TOTALYTD年初至今累计日期表已标记含连续日期SAMEPERIODLASTYEAR去年同期日期表覆盖完整年份DATEADD按指定粒度平移平移后区间在日期表范围内DATESINPERIOD任意滚动区间起止日期需明确最容易忽略的是「日期表必须连续且无重复」。如果日期表缺了某几天TOTALYTD的累计会在缺口处断掉报表上看就是某个月数字突然变小。另一个坑是财年与自然年不一致时忘了在日期表里配置财年列导致TOTALYTD按自然年算口径直接错位。5.2 计算组把「指标 × 时间」的重复劳动收拢计算组是第二版新增的重点内容本质是把一组计算逻辑如同比、环比、占比应用到任意基础度量值上避免为每个指标重复写一遍时间智能。它的核心是SELECTEDMEASURE()占位符代表「当前被计算组作用的基础度量值」。-- 计算组中的「同比」计算项示意 YoY % : VAR CurrentValue SELECTEDMEASURE() VAR PriorValue CALCULATE( SELECTEDMEASURE(), SAMEPERIODLASTYEAR( Date[Date] ) ) RETURN DIVIDE( CurrentValue - PriorValue, PriorValue )参数说明SELECTEDMEASURE()在计算项求值时被替换成实际的基础度量值CALCULATE负责把时间平移加进筛选上下文。计算组的价值在于「一次定义、全指标复用」但代价是排查变复杂——一个数字不对可能是基础度量值的问题也可能是计算项的问题复习时要养成「先单独验证基础度量值再套计算组」的习惯。5.3 表函数与查询编写FILTER、ALL、VALUES、SUMMARIZE、ADDCOLUMNS这些表函数既能在度量值里构造中间表也能在EVALUATE查询里直接输出结果。EVALUATE是 DAX 作为查询语言的入口配合SUMMARIZECOLUMNS可以复现报表页上的数据。EVALUATE SUMMARIZECOLUMNS( Product[Category], Date[Year], Sales, [Total Sales], Margin, [Total Margin] ) ORDER BY Date[Year], Product[Category]这段查询的关键点是SUMMARIZECOLUMNS自动处理筛选上下文和空值组合比手写ADDCOLUMNSFILTER更稳ORDER BY只能引用查询结果里已有的列。写查询时如果结果行数异常多先检查是不是漏了筛选条件或者某个关系方向导致笛卡尔式膨胀。6. 引擎与优化VertiPaq、查询计划与性能排查到了这一层DAX 从「写得对」进入「跑得快」。很多报表卡顿不是公式写错而是模型存储或查询计划出了问题这一章是区分「会用」和「用得好」的分水岭。6.1 VertiPaq 存储与模型瘦身VertiPaq 是列式内存引擎压缩效率极高但压缩的前提是列基数低、重复值多。高基数列如订单号、时间戳会显著撑大模型拖慢扫描。优化方向很明确删掉不需要的列、把高基数文本列拆成低基数键、避免在事实表里存长文本。优化动作收益注意删除未使用列直接减小模型体积先确认没有度量值或关系依赖高基数列改键值提升压缩率需要维护维度表映射避免计算列存大文本降低内存占用能放度量值就别物化控制日期表粒度减少行数业务需要小时级才建时间维度梳理下来会发现模型体积和查询速度高度相关但并非线性——真正拖慢查询的往往是「大表上的高基数筛选」而不是单纯的体积。6.2 查询计划读懂引擎在做什么DAX 查询会经过公式引擎和存储引擎两层查询计划里能看到扫描、聚合、连接等算子。分析计划的目的不是背算子名而是定位「哪一步扫了太多行」。常见信号是某个FILTER在大表上逐行迭代、上下文转换作用在宽表上、或者关系缺失导致跨表扫描。-- 反例在大表上逐行 FILTER代价高 Bad Measure : SUMX( FILTER( Sales, Sales[Amount] 1000 ), Sales[Amount] ) -- 改进把条件下推到扫描层减少迭代行数 Better Measure : CALCULATE( SUM( Sales[Amount] ), Sales[Amount] 1000 )两段逻辑等价但第二段把筛选交给CALCULATE引擎可以在存储层先过滤再聚合迭代行数大幅下降。这类改写是优化章节的核心套路能用筛选上下文表达的就别用逐行迭代硬算。7. 易错点清单五个最容易翻车的地方复习到这个阶段知识点已经铺开真正拉开差距的是对「哪里会错」的敏感度。下面五条是梳理过程中反复出现的坑每条按「易漏点 → 为什么漏 → 怎么补」展开。易漏点一把行上下文当成筛选上下文。为什么漏计算列里写聚合函数不报错结果却是全表值表面能跑通。怎么补凡是计算列里出现聚合先问「我是不是需要CALCULATE做上下文转换」或者干脆改成度量值。易漏点二CALCULATE筛选默认覆盖而非叠加。为什么漏单层筛选时覆盖和叠加结果一样只有多层嵌套才暴露。怎么补写占比、同比这类需要保留外层筛选的场景主动加KEEPFILTERS并用两层切片器验证。易漏点三时间智能缺日期表或日期不连续。为什么漏函数不报错只是数字偏小或断档肉眼难发现。怎么补建模阶段就标记日期表用COUNTROWS对比日期区间天数确认无缺口。易漏点四关系方向与多对多桥接处理不当。为什么漏单向关系在简单报表里正常一旦双向筛选需求出现就漏数。怎么补明确每条关系的筛选方向多对多场景优先用桥接表而非直接双向CROSSFILTER只做局部调整。易漏点五变量被当成「惰性求值」的万能药。为什么漏VAR看起来只是存值实际上它固定了求值时机表变量还会物化。怎么补变量里避免放大表理解「变量一旦赋值就不再随上下文变化」需要动态结果时别提前塞进变量。注意这五条里前两条属于上下文理解问题后三条属于建模与工程问题复习时分开练别混在一起背。8. 复习路线与查漏补缺一份可执行的清单把整本书压成一条复习路线核心是「先验证理解再补薄弱模块最后用查询计划反推」。下面这份清单按周推进适合已经通读过一遍、需要系统巩固的人。阶段目标自检方式第一周上下文与 CALCULATE手写 5 个「计算列 vs 度量值」对照预测结果再验证第二周时间智能与计算组用同一基础度量值套 3 个计算项核对同比环比第三周表函数与查询用EVALUATE复现一张报表页行数对得上第四周引擎与优化对一个慢度量值做改写对比查询计划扫描行数具体到查漏补缺我自己的习惯是「三问法」拿到一个结果不对的度量值先问上下文对不对行还是筛选再问关系通不通方向、活动关系最后问引擎亏不亏有没有大表逐行迭代。这三问覆盖了绝大多数翻车场景比漫无目的地翻函数文档高效得多。-- 自检模板把待排查度量值拆成可验证的中间步骤 Debug Measure : VAR Step1 [Base Measure] -- 基础值是否正确 VAR Step2 CALCULATE( [Base Measure], ALL( Date ) ) -- 去掉时间筛选 VAR Step3 DIVIDE( Step1, Step2 ) -- 占比是否合理 RETURN Step3用变量把中间结果拆开逐个在报表里显示比盯着一个最终数字猜要快。参数上没什么玄机关键是「每一步只改一个条件」这样出错时能立刻定位到是哪一层上下文或哪个修饰符的问题。从那以后我每次复习 DAX都强制走一遍「上下文 → 关系 → 引擎」这条链路哪怕只是改一个筛选条件也先在心里过一遍这三问再动手。这份原版 PDF 的章节顺序本身就是按这条链路排的配合上面的清单逐章对照比零散刷函数要扎实。希望帮到你。本文还有配套的精品资源点击获取