语义模型中的 DAX 基础

此内容由人工智能翻译,尚未经过人工编辑审核。图像和图表保持其原始语言。

要点总结

  • 使用 DAX 在语义模型中创建计算。 要为 Report 定义这些计算,你需要引用语义模型中已有的表、列和关系。 
  • DAX(Data Analysis Expressions)是一种用于在 Power BI 和其他应用中分析数据 的查询编程语言。 你编写 DAX 表达式 在模型内部定义自定义计算或逻辑,而 Power BI 会使用 DAX 查询 从 Report 中查询该模型。 
  • DAX 是你的 Visual 与语义模型沟通、获取所需数据的方式。 尽管语法和结构相对简单,DAX 仍不容易精通。  

本摘要由作者撰写,并非由 AI 生成。 

\n

\n

编写计算,以便对数据进行分组和分析。

\n\n
DAX basics in a semantic model
\n

在本系列中,我们分享如何在 Power BI 或 Analysis Services 中高效构建 语义模型(以前称为 Dataset)的技巧。 在 上一篇文章 中,我们介绍了如何验证模型关系——它们是表之间的链接,也是业务逻辑的基础。 创建并验证关系后,你的模型基础就已就绪,可以进入下一步了。

\n\n

本文将介绍构建语义模型的下一步:通过编写 DAX 创建计算。 你将引用前几步已添加到模型中的表、列和关系,为 Report 定义这些计算。 这些计算用于 Report Visual 或 Excel 数据透视表,帮助你分析数据。

\n

本文旨在概述基础知识——何时需要编写 DAX,以及如何在 Power BI Desktop、Tabular Editor 或 Microsoft Fabric 中完成这件事。

\n
\n\n\n\n\n\n\n
\n

注意

\n
\n
\n
\n

构建满足你数据需求的语义模型有很多同样可行的方法。 本系列介绍其中一种做法,并分享我们经验中的技巧与最佳实践。 请参阅下方本系列已发布的其他文章:

\n
\n
\n \n
\n
\n
\n

什么是 DAX?

\n

数据分析表达式(DAX)是一种函数式和查询型编程语言,用于在 Power BI 和其他应用中分析数据。 编写 DAX 表达式,在模型内定义自定义计算或逻辑;而 Power BI 会使用 DAX 查询从你的 Report 中查询该模型。 DAX 是你的 Visual 与语义模型通信、检索所需数据的方式。 你编写的函数式 DAX 就像一组指令,告诉模型该使用哪些表和列,以及要对它们做什么。 编写 DAX 时,你会引用表、关系、列以及其他计算(度量值),然后使用 SUMAVERAGE 或 FILTER 等函数来聚合或筛选数据。

\n
\n\n\n\n\n\n\n
\n

提示

\n
\n
\n
\n

要查找某个 DAX 函数,或想了解其工作方式的更多信息,请使用 dax.guide。 在 Tabular Editor 3 中编写 DAX 时,工具提示也会说明它们的作用、语法,并链接到相关的 dax.guide 文档页面(s)。

\n

In Tabular Editor, you can get information about functions as you write them, including function syntax, explanations, and links to documentation.图 1: 在 Tabular Editor 中,你可以在编写函数时获取相关信息,包括函数语法、说明以及文档链接。

\n
\n
\n
\n
\n
\n
下图展示了 DAX 表达式与 DAX 查询之间的区别。 DAX 查询通常由 Power BI 生成,用于从模型获取数据。但高级用户可能会出于测试或验证,或为了自动化某些任务而自行编写 DAX 查询。 我们会在本系列后续文章中进一步讨论这一点。
\n
 A DAX expression consists of one or more functions that provide instructions about how to aggregate different parts of your model. \n
图 2: DAX 表达式由一个或多个函数组成,用于说明如何对模型的不同部分进行聚合。 度量值中编写的内容就是一个 DAX 表达式示例。 一个 DAX 查询(可选)以 DEFINE 部分开头,你可以在其中放置查询作用域的度量值或变量;随后以一个或多个 EVALUATE 部分结束,每个部分都指定一个产生表结果的 DAX 表表达式。 当你在 Report Visual 中添加列和度量值时,Power BI 生成的内容就是一个 DAX 查询示例。
\n
\n


前面的示例可以这样理解:

\n
    \n
  • 第一张图展示了一个度量值 [Selling Margin (Value)]。 它会对 ‘Invoices’ 表中每一行的 ‘Invoices’[Net Invoice Value] 减去 ‘Invoices’[Net Invoice Cost] 的结果使用 SUM 进行求和。 由于销售毛利只应针对发票单据计算(而非其他单据类型),CALCULATE 函数会使用相关的 ‘Invoice Document Type’[Invoice Type] 列对结果应用 FILTER 筛选,使其仅包含发票;该列位于一个维度表中,并与事实表 ‘Invoices’ 相关联。 度量值通常在 DAX 查询中使用,其结果会随当前激活的 FILTER 筛选条件而变化。
  • \n
  • 第二张图展示了按月统计发票行的 DAX 查询。该查询在 DEFINE 中定义了度量值 [Invoice Lines](因此它只存在于该查询中),并在查询中(EVALUATE 之后)使用它来计算每月的行数。 开发人员可能会定期运行这样的查询,用于验证模型或底层数据。 查询也可以指在模型中定义的度量值。

DAX 贯穿整个 Power BI 解决方案。 下图对在 Power BI 的模型和 Report 中,你可能在不同位置编写 DAX 的情况做了一个基本概览。

 DAX objects consist of DAX expressions and object properties
图 3:DAX 对象由 DAX 表达式和对象属性组成,它们存在于 (1) 语义模型、(3) Report,以及 (4) Report 的 Visual;而 (2) DAX 查询 用于在 Report 中从语义模型检索数据,或在各种客户端工具中编写并执行这些查询时,从语义模型检索数据。


DAX 贯穿整个 Power BI

  1. 语义模型:你会在模型中编写 DAX,用于定义可即时计算的度量值,或在模型刷新时计算的计算表格与计算列。 DAX 表达式也用于定义行级安全性 (RLS) 规则,也称为表格权限。 DAX 也用于更高级的模型对象,例如计算组项、动态格式字符串和明细行。
  2. 查询:DAX 查询是 Power BI 从模型检索数据、为 Report 中的 Visual 填充数据的方式。 你也可以在 Power BI Desktop 中自行编写查询,也可以使用 Tabular Editor、DAX Studio 等其他工具,或在带有 Semantic Link 的 Fabric 笔记本里编写查询。
  3. Report:你可以在连接到已发布语义模型的“轻量级”Report 中编写 DAX 度量值。 这通常只用于该 Report 特有的度量值,而不是计算(计算更适合放在模型中)。
  4. Visual:你可以在 Power BI Visual 中编写一种特殊类型的 DAX,称为视觉计算。 视觉计算不属于模型,只使用该 Visual 中的数据来生成计算结果。

“DAX 很简单,但并不容易”

DAX 在语法和结构上很简单,但众所周知,DAX 并不容易学习。 乍一看,DAX 很像 Excel 公式语言那样简洁,但这很容易产生误解:为了覆盖比 Excel 更丰富的场景,它实际上要复杂得多。 由于 DAX 依赖于 Data model,而不是工作表上的单元格位置,作者必须“用数据模型来思考”,这对刚接触数据建模的新手来说尤其有挑战性。 下面这些是有经验的开发人员习以为常的概念,但新开发人员必须在实践中逐步掌握:

  • 一个模型包含多个表格,这些表格通过关系连接。
  • 筛选会沿着关系按方向从一个表传播到另一个表。
  • 在一条关系链中,“一”端的属性可以对“多”端的值进行分组。

DAX 之所以难学,还有一个原因是:它的运行方式与其他编程语言不同。 DAX 中有些约定是 DAX 独有的;它有自己独特的“思考”方式。 这意味着,一些有其他语言经验的开发人员可能会套用在 DAX 中不适用的约定或实践——更糟的是,它们看似能跑通,但结果并不理想。

  • Python 示例:你不应该像在 Python 中使用 for loopwhile loop 那样,在 DAX 中使用“循环”。
  • SQL 示例:你不应该像在 SQL 中用 CASE/WHEN 那样,在 DAX 里用 IF/THEN 来做条件聚合。

下面再举几个例子:这些特定的约定或规则,在你刚开始写 DAX 时往往很难理解。

  • 你可以通过 CALCULATECALCULATETABLE 等函数来修改计算。 使用这些函数时,你会提供两类不同的参数:筛选器(用于筛选结果)和 CALCULATE 修饰符(用于改变这些函数的行为)。
    • CALCULATE 修饰符可以让你更精细地调整计算结果,但前提是你理解正在修改的中间结果。
  • DAX 会在决定结果的语境中进行求值。 编写 DAX 时需要了解两种主要语境:筛选语境和行语境。
 Filter concept is an important concept to understand in DAX
图 4:在 DAX 中,理解筛选概念非常重要。 上图展示了一个筛选语境的示例。
  • 筛选语境会对数据进行筛选。 筛选语境的示例包括:
    • 当 Report 中的筛选器或切片器处于活动状态,或用户通过交叉筛选、下钻或钻透等方式应用了筛选,并且在该 Report 的某个 Visual 中对 DAX 表达式求值时。
    • 当在 Visual 中针对某个特定数据点(例如某个类别或类别组合)对 DAX 表达式求值时。
    • 当某个计算将筛选语境作为 CALCULATE 的参数传入时(例如 ‘Date'[Month] = “January” )。 该筛选语境会覆盖引用列上的其他筛选,除非使用 KEEPFILTERS 这个 CALCULATE 修饰符。
Row context is different from filter context
图 5:行语境不同于筛选语境;行语境会迭代表格,而筛选语境会对计算进行筛选。 行语境同样是一个需要理解的重要概念。
  • 行语境会迭代表格。 行语境的示例包括:
    • 当你在计算列中对 DAX 表达式求值时。 例如,如果你在计算列中对 ‘Invoices’ 表里的 ‘Invoices’[Sales] – ‘Invoices’[Cost] 求值,它会对 ‘Invoices’ 表使用行语境,从而为每一行计算出一个结果。
    • 当你在迭代器函数中对 DAX 表达式求值时。 例如,如果你在某个 DAX 度量值中对 SUMX( ‘Invoices’, ‘Invoices’[Sales] – ‘Invoices’[Cost] ) 求值,它同样会对 ‘Invoices’ 表使用行语境。
  • 语境转换这一概念:即通过使用 CALCULATE 函数,将行语境转换为等价的筛选语境。 很多人在刚开始编写 DAX 时,语境转换往往是性能问题的来源之一。
 Context transition is an advanced but important concept in DAX where a row context is transformed to a filter context
图 6:语境转换是 DAX 中一个高级但重要的概念:它会将行语境转换为筛选语境。 语境转换之所以重要,是因为它常常会引发性能问题;如果新开发人员不使用格式化和变量来组织 DAX 代码,又难以在脑海中还原中间求值结果,那么就很难调试语境转换相关问题。 在这个示例中,如果不使用 CALCULATE,就不会发生语境转换;[@Sales] 列只会包含所有月份的 [Total Quantity] 结果,并在 _SalesByMonth 表的每一行中重复,从而产生错误结果。

提示

你可以在 Tabular Editor 中使用 DAX调试器查看 DAX 表达式的中间求值结果。 这对学习和排查 DAX 代码问题都很有帮助。 我们会在后续文章中讲解该调试器。

提示

当你在 Tabular Editor 3 中编写 DAX 时,它会为你指出这些特殊约定。 例如,工具提示会告诉你 calculate 的参数、当前激活的语境,以及语境转换发生的时机。 这样你就可以专注于编写 DAX。 你可以按下快捷键 Ctrl + Shift + Spacebar,即可切换此工具提示,使其在光标所在位置弹出。

In Tabular Editor, DAX conventions like Row Context or Context Transition are highlighted as you write DAX图 7: 在 Tabular Editor 中,当你编写 DAX 时,会高亮显示行语境或语境转换等 DAX 约定,以便你更有把握地决定如何调整代码,或理解它将执行什么操作。

上面的概念听起来可能有点吓人,但别怕 DAX;它非常优雅且功能强大。 尽管很多 LinkedIn 网红会告诉你,你 完全可以不必精通某样东西,也能从中获益并获得价值。 例如,在少数情况下,你的语义模型甚至可能不需要 DAX。

我的模型需要编写 DAX 吗?

你可以创建一个完全不含 DAX 的模型,并使用该 Data model 制作 Report。 例如,如果你只会用 Report 展示带描述性属性的数据或未汇总的数据,就没有必要编写任何 DAX。 例如,用于产品目录的报表,或用于按行项目浏览订单单据的报表;这类报表展示的是未汇总在一起的原始交易记录。 不过,这类 Report 并不常见,因为 Power BI Report(以及它使用的 VertiPaq 引擎)的优势在于高效汇总并通过 Visual 可视化规模大、结构复杂的数据。

你也可以使用 隐式度量值,在不编写 DAX 的情况下分析数据。

隐式度量值

隐式度量值指的是:在 Power BI 中对数值列进行聚合而无需编写任何 DAX。 相反,你只需添加该列,系统就会应用默认的汇总方式,并按某个维度对数据进行分组。 你可以从一组默认选项中更改这种汇总方式。 之所以称为 隐式度量值,是因为在后台,Power BI 会在 Visual 生成的 DAX 查询中即时为你创建该度量值。

下图展示了一个隐式度量值示例:从 Quantity 列生成 SUM of Quantity ,并在 Visual 中按文本列 Category 分组。

Implicit measures in a semantic model
图 8: 语义模型中的一个隐式度量值示例。 在此示例中,将 Quantity 列(点线框)添加到条形图的 x 轴(虚线框)后,系统会自动汇总为 Sum of Quantity。 你可以通过右键菜单(框选部分)更改隐式度量值的聚合方式。

对于业务用户或刚接触 Power BI 的人来说,隐式度量值可能是个不错的选择,尤其是当他们没有任何编写代码的经验时。 此外,即使是有经验的开发者,在做快速的 ad hoc 即席分析时也可能会用到隐式度量值(例如在探索数据或分析一个短期模型时)。

如果你确实打算使用隐式度量值,请考虑以下几点。

  • 将“Default Summarization”属性设置为合适的聚合方式。 对于不应聚合的列,例如 ‘Orders’[Order Number],将其设置为 None
  • 使用显示文件夹将可聚合的数值列与不可聚合的列分开整理。
  • 为列设置描述,以明确应使用哪些聚合方式。
  • 如果隐式度量值无法带来明确价值,就不要使用它。 尤其要避免对计算列使用隐式度量值,例如把它当作绕开棘手 DAX 场景的手段。

隐式度量值可能会带来一些问题和注意事项,需要提前了解。

  • 可选项仅限于最常用的几种聚合。
  • 由于你无法控制 DAX,因此也就无法修改计算逻辑。
  • 这些计算不会出现在字段列表中,这会让开发者更难理解模型的使用方式,也更难在不同报表之间保持一致。
  • 添加计算组会在模型中抑制隐式度量值,这意味着用户将无法创建并使用新的隐式度量值。 Report 中已有的隐式度量值仍然可以正常工作。

显式度量值

虽然隐式度量值有一些合理的使用场景,但通常更建议你创建 显式度量值。 显式度量值就是你在模型中显式定义的 DAX 度量值(或在连接的“轻量”报表中定义)。 下面的示例是上一张图中隐式度量值对应的显式度量值版本。

Explicit measures in a semantic model
图 9: 一个显式度量值示例。 在此示例中,将一个度量值(虚线点框)添加到条形图的 x 轴(虚线框)。 该度量值在 DAX 中对 Quantity 列进行聚合(实线框)。

提示

想知道某个隐式度量值采用了哪种聚合,唯一的办法是手动检查每个 Visual,或查看 Report 元数据(PBIP 中的 report.json,或 PBIX 文件,信息就存储在其中)。 这也是你可以找到 Visual Calculations 表达式的地方。

以下部分将更详细地介绍显式 DAX 度量值,以及在什么情况下你会选择使用它们,而不是计算列或计算表格等其他 DAX 对象。

度量值、计算列和计算表格

编写 DAX 表达式时,你可以在不同类型的对象中编写它们:度量值、计算列或计算表格。

The most common DAX objects are measures, calculated columns, and calculated tables
图 10:最常见的 DAX 对象是度量值、计算列和计算表格。
  1. 度量值:包含表达式(以 DAX 编写)的定义以及属性(如说明、显示文件夹和格式)。 度量值本质上只是元数据;为了便于组织,它会关联到某个表。 度量值只有在被查询时才会计算(直接查询,或由引用它的其他度量值间接触发)。 度量值的一个例子是 Sales Amount 度量值,它用于计算所有销售额的总和 SUM。
  2. 计算列:包含表达式(以 DAX 编写)的定义和属性(如说明、数据类型和格式),以及计算得到的数据结果。 计算列的数据结果会让你的模型占用更多内存。 计算列在表中创建,并会对表的每一行计算其 DAX 表达式。 它们只会在刷新时计算。 计算列的一个例子是:用于标识本月有销售的新客户的标记列,你可以在其他计算中使用它。
  3. 计算表格:类似计算列,但其计算不依赖任何现有表和列,除了在其 DAX 表达式中引用的那些表和列。 计算表格会生成一个或多个列的独立结果。 与计算列一样,它也只会在刷新时计算。 计算表格的一个例子是:使用 CALENDAR DAX 函数创建的日期表。

注意

DAX 和 Power BI 新手常问的一个问题是 我应该创建度量值还是计算列? 通常,很多新用户更喜欢计算列,因为它们会计算出结果,并且你能像在单元格和表格中立即看到反馈一样(就像 Excel 一样)。 相较之下,度量值要求你考虑该度量值将在什么上下文中被计算,以及你是否需要在计算本身中使用 CALCULATE 来修改该上下文。

在以下场景中使用计算列:

  • 你会把计算结果作为属性,用来对其他计算进行分组。 一个例子是:你想在直方图 Visual 中按区间 bins 展示销售额。
  • 你会使用计算结果来筛选数据。 一个例子是:你想动态“标记”某些交易,以便在另一个计算或 Report 中将其过滤掉。
  • 一般来说,如果你确实需要计算列,应考虑 将计算前移 到 Power Query,或进一步前移到数据源。 不过,在某些场景下,计算列完全是合理的做法。 例如:
  • 表很小,计算很简单,并且你只会新增少量计算列。
  • 模型由自助分析用户开发,他们对 Power Query 了解有限,且没有上游源系统(即 Power BI 连接到 SharePoint 或 OneDrive 上的 Excel 文件)。
  • 计算依赖于模型中其他动态数据或属性。

根据你正在构建的模型类型,你可能无法创建计算列或计算表格。 如果你的特定用例需要其中一种对象,这可能会促使你选择某一种存储模式。

下图概述了在 Power BI 目前提供的不同语义模型存储模式中,你可以创建哪些 DAX 对象。

 Depending on the storage mode of your semantic model, you cannot create certain objects
图 11:根据语义模型的存储模式,你可能无法创建某些对象。 注意:有些存储模式是按表来设置的(Import、DirectQuery 和 Hybrid),并且可以在同一个模型中组合使用;而 Direct Lake 只支持使用 Direct Lake 存储模式的表。 如果你在一个模型中混用存储模式,这称为 复合模型


总结一下,以下限制适用于某些类型的语义模型。 注意:存储模式是按表设置的,但 Direct Lake 例外,它不支持混合不同存储模式的表(整个模型都使用 Direct Lake 存储模式)。

  1. Import:将数据导入内存的模型是默认(也是最常见)的存储模式。 在这些模型中,你可以创建任意 DAX 对象。
  2. DirectQuery:直接连接到受支持的数据源的模型,会在运行时将 DAX 查询实时转换为等效的 SQL 查询。 这些模型不支持计算列或计算表格。
  3. Hybrid:包含混合存储模式或混合表格的模型,不允许在模型中的 DirectQuery 表或混合表格上创建计算列。
  4. Direct Lake:连接到 Fabric Lakehouse 或 Warehouse 中的 Delta 表的模型不支持计算列;并且仅在计算表格不引用 Direct Lake 表格或这些表格中的列时,才支持计算表格。 Direct Lake 仅在你拥有 Fabric capacity,并且数据以 Delta 表形式存放在 OneLake 数据项中时才可用。

注意

作为本系列的一部分,我们之后会写一篇文章,帮助你理解并在不同可用的存储模式之间做出选择。 不过,这篇文章要等 Direct Lake 正式全面可用后才会发布。

总结

DAX 是大多数语义模型的基础组成部分。 它是一门简单而强大的语言,但也确实包含一些较为复杂的概念;要想最有效地使用它,需要一定的理论理解。 在 Power BI 解决方案的不同部分,你会结合语义模型使用 DAX 表达式或查询。 在下一篇文章中,我们会提供一些技巧和指导,帮助你在 Power BI 语义模型中高效编写 DAX。

提示

如果你想深入学习 DAX,可以选择一些书籍和视频课程。 SQLBI 的 DAX 课程被视为业界标准,是学习的绝佳起点。
潜在利益冲突声明:本系列作者与 SQLBI.com 存在职业合作关系。

BorpAfterScenario

注意

构建符合你数据需求的语义模型,有很多种同样可行的方法。 本系列介绍其中一种方法,并分享我们在实践中总结的技巧和最佳实践。 本系列已发布的其他文章如下:

Related articles