专栏名称: PowerBI战友联盟
微软 Power BI MVP BI佐罗 带你学习BI真经。
目录
相关文章推荐
51好读  ›  专栏  ›  PowerBI战友联盟

DAX 中行上下文的直观解释

PowerBI战友联盟  · 公众号  ·  · 2024-07-30 21:25

正文

请到「今天看啥」查看全文


本文翻译自 Marco Russo & Alberto Ferrari 的文章《 Row context in DAX explained visually 》。

在学习 DAX 的过程中,相信不少小伙伴都被抽象的计算上下文折磨过,之前我们翻译了 SQLBI 大佬关于筛选上下文的直观解释文章,今天我们再来介绍下 SQLBI 出版的关于行上下文的直观解释。

筛选上下文解释参考: DAX 中筛选上下文的直观解释

行上下文和筛选上下文都是编写 DAX 代码的重要基本概念,接下来我们将从不同的角度探讨行上下文。


列引用需要行上下文

每当在 DAX 表达式中引用列时,我们都需要行上下文来计算表达式。例如,请考虑以下 DAX 表达式:

Sales[Quantity] * Sales[Net Price]
该公式将销售额表中的数量乘以单价,准确地说,该公式执行以下步骤:
  1. 获取销售表当前行中数量列的值
  2. 获取销售额当前行的单价列的值
  3. 将前面步骤中获得的两个值相乘
每个列引用都需要评估当前行。但是,当前行是什么意思呢?我们使用当前行作为通用方法来识别 DAX 特有的概念:行上下文。
行上下文标识表中的一行。例如,考虑以下销售表。

我们通过高亮显示销售表第二行来表示该行的行上下文。

但是,我们也可以使用只有一行的表(行上下文指向的行)来表示行上下文。


如何获取行上下文


通过在 DAX 中使用迭代器函数迭代表可以获取行上下文。在 DAX 指南中,每个迭代表的函数都有一个迭代器标记,用于迭代的表,并为每个参数提供一个行上下文标记,这些参数提供在迭代表的行上下文中执行的 DAX 表达式。例如,ADDCOLUMNS 的第一个参数是迭代的表,并且迭代表的每一行可能在行上下文中计算一个或多个表达式。

因此,迭代器总是逐行扫描表。即使我们的表达式仅使用了几列,行上下文也始终提供对整行的访问。

虽然引擎可能会仅考虑 DAX 表达式中引用的列来优化执行,但从概念的角度来看,我们可以访问迭代表的所有列。但是,行上下文没有实现成本,因为它仅表示表中的位置。同样,我们可以用一个包含原始表的所有列且只有一行的表来表示行上下文,但该表不重复任何数据,它只是一个概念模型,可以更轻松地解释 DAX 的工作原理。

计算列是迭代器的一种特殊情况。刷新模型时,引擎会针对每个表行执行计算列的表达式,并将其结果存储在单独的列中。与迭代器(迭代筛选上下文中可见的行)不同,计算列是在空的筛选上下文中进行计算的,因此它们始终会迭代所有表行。


筛选上下文进行筛选,行上下文进行迭代

执行 DAX 表达式时,通常会涉及筛选器和行上下文。例如,考虑以下销售额度量值的定义:

Sales Amount =SUMX




    
 (    Sales,    Sales[Quantity] * Sales[Net Price])

SUMX 是一个 DAX 迭代器函数,它对第一个参数中表的每一行执行第二个参数。我们说 SUMX 迭代第一个参数中指定的表表达式。筛选上下文会筛选该表表达式。我们可以使用的最简单表表达式就是 Sales 表引用。DAX 中的表引用始终由筛选上下文筛选,因此 SUMX 会迭代筛选上下文中可见的 Sales 行。例如,下图显示了当我们在 2024 年 6 月 12 日应用筛选器时的这种行为

销售名称用于识别两个不同的概念:
在左侧,我们有语义模型中的 Sales 表。当我们谈论“模型表”时,我们考虑语义模型中的物理表及其所有行,忽略任何筛选。在右侧,我们有 Sales 表引用。DAX 中的表引用始终受到安全筛选和筛选上下文的影响。表引用就像是模型表的一个“视图”,只返回“可见”行。DAX 无法覆盖安全筛选,而筛选上下文可以通过使用 CALCULATE 和 CALCULATETABLE 添加和删除筛选来进行操作。
SUMX 表达式执行以下操作:
  1. 在筛选上下文中评估第一个参数。
  2. 对在步骤 (1) 中获取的表的每一行,在相应的行上下文和相同的筛选上下文中评估第二个参数。如果我们在第二个参数中仅使用列引用,则筛选上下文不再相关。然而,如果我们有其他表达式,这可能很重要。例如,以下度量值将每笔交易的金额除以一个由切片选择定义的数字:
Sales Amount Scale A =SUMX (    Sales,    DIVIDE (        Sales[Quantity] * Sales[Net Price],        SELECTEDVALUE ( Scale[Scale] )    ))

SELECTEDVALUE 函数返回切片器上 Scale 的当前选择,因为在该表达式中筛选上下文仍然是活动的。所示的代码并不理想,因为如果表达式不依赖于行上下文,它可以在迭代器之前被评估,明确表明这种依赖关系不存在:

Sales Amount Scale B =VAR _Factor = SELECTEDVALUE ( Scale[Scale], 1 )RETURN    SUMX (        Sales,        (Sales[Quantity] * Sales[Net Price]) / _Factor    )
在这种特定情况下,除法应该在 SUMX 之外进行。然而,我们只是想澄清,在迭代器中行上下文中评估的表达式中,筛选上下文仍然是可用的。我们使用这些最后的例子是为了教育目的。代码优化不是本文的目标。

表表达式定义基数

行上下文遍历迭代器函数提供的表表达式返回的所有行。因此,迭代的基数由表表达式定义。例如,考虑以下两个度量值:

Sales Amount Projection =VAR SalesProjection =    SELECTCOLUMNS (        Sales,        Sales[Quantity],        Sales[Net Price]    )RETURN    SUMX (        SalesProjection,        Sales[Quantity] * Sales[Net Price]    )
Sales Amount Grouped =VAR SalesGrouped =    SUMMARIZE (        Sales,        Sales[Quantity],        Sales[Net Price]    )RETURN    SUMX (        SalesGrouped,        Sales[Quantity] * Sales[Net Price]    )
Sales Amount Projection 度量值返回的结果与 Sales Amount 相同,因为迭代的行数相同。实际上,即使 SalesProjection 变量只有两列,行数也是相同的。从性能角度来看,只要表未具体化,我们就不必为在内存中分配可能引用的未使用模型列付出代价。但是,就本文而言,我们可以忽略这一点:重要的是 SUMX 的结果取决于迭代的行数,并且这个数字是相同的。SELECTCOLUMNS 函数不会更改迭代表的基数。
Sales Amount Grouped 度量值返回不同的结果,因为它迭代了 SUMMARIZE 返回的销售额中数量和单价的唯一组合数。事实上,SUMMARIZE 可以从第一个参数提供的表中返回较少的行数,这通常会导致结果的基数较小。同样的较小基数解释了不同的结果。下图显示了两个度量值使用的 SalesProjection 和 SalesGrouped 变量的内容。


总结

行上下文可以通过在表中选择一行或将该行表示为具有所有列和单行的表来直观地表示。当我们在以后的文章中讨论上下文转换时,这后一种表示方法将非常有用。

迭代器获取行上下文并通过迭代的表表达式控制迭代的基数。实际上,由于筛选上下文,简单的表引用通常仅显示模型中可用行的子集。以图形方式表示提供给迭代器的表表达式有助于理解迭代的基数和行上下文中可用的列。

数据分析精英都会遇到的二十大分析问题

分析师必看: 业财人面临的二十大数据分析问题

数据分析精英都在学习的五大能力境界


分析师必备: 业务数据分析能力五层成熟度路线图框架全解


↓ 数据分析精英正在学习的课程 ↓

他们是:企业老板,高管,CFO,分析...
可以体验百万级真实企业项目案例,彻底打通任督二脉

数据分析师训练营 课程表

🚀 《业财分析之道》 业务财务人数字化能力必修课
📈 《经营分析之道》 企业指标拆解及经营分析框架

🔍 数据分析之道 精通分析十大方法加十大模型
💡
商业智能之道 更专业更系统化学习 Power BI

🔗 如何获取更多信息?扫描下方二维码具体咨询。

如果您觉得这篇文章对您有帮助或启发
感谢您【点赞】、【在看】
如果您需要添加老师,也请先 【点赞】、【在看】

这样老师才能更快地识别并回应

跟BI佐罗老师更专业更系统学企业数据分析
点击“阅读原文”获取更多资源







请到「今天看啥」查看全文