CALCULATE 庖丁解牛系列- 扩展表 (3)

扩展表与当前行筛选

      (简体笔记整理版)

      本文描述了列表的扩展与当前筛选器的传递两个重要的DAX概念,以及如何理解和运用这两种行为,在避免、修复DAX表达式常见错误时的运用。
      花时间学习DAX这两种行为非常的重要。因为,如果你不注意正确的度量与错误的度量在方法上的细微差别,几乎不可能理解它。

    小测试

      在这篇文章中,我想与大家分享一些关于出现此类行为的代码,并考虑如何优化的例子。
      例如要求是:计算那些Sales[Quantity] > 3--销售数量大于3的销售额的平均销售额,然后按颜色对其进行切片。不要浪费你的时间: 这是一个毫无意义的度量,说这个问题的目的在于了解优化查询的行为,而不是找到它的任何计算公式。

      在尝试不同版本的度量时,有人使用了这个版本:

DEFINE
MEASURE Sales[Average Sales Amount] =
AVERAGEX ( Sales,'Sales'[Quantity] * 'Sales'[Net Price] )
EVALUATE
ADDCOLUMNS ( VALUES ( Product[Color] ),
"Sales",
CALCULATE(  [Average Sales Amount], // 扩展的Sales计算列表集
FILTER ( Sales,Sales[Quantity] > 3 )))    // 筛选条件(数量列)

查询不工作,它总是返回相同的值,你可以清楚地从下图中看到:

CALCULATE 庖丁解牛系列- 扩展表 (3)_第1张图片

        现在,在进一步阅读之前,花些时间回顾一下这个查询,并尝试自己找出这里面包含的一些小故障---以及造成这些问题的原因。相信我,发现它,需要一些 DAX 技能,这个时间是完全值得的。
        如果你只是阅读文章而不亲自尝试,很可能不会发现这些小细节:此代码中的问题在哪里? 以及为什么它总是返回相同的值?这里有两个非常常见的错误,值得指出:

      1.  如果使用 " CALCULATE" 筛选一个表,那么DAX 中的表总是会被扩展,并且还会基于多到一关系模式筛选所有相关的表格(扩展的表)。
      2.  CALCULATE虽然执行筛选转换,但它的筛选器参数是在原始计算筛选器中计算得到,而不是通过CALCULATE修改后得到。

    扩展表行为

        让我们从第一个问题开始。

        CALCULATE的筛选参数是:
FILTER (Sales,Sales[Quantity] > 3)
      此代码没有什么问题。但需要注意,FILTER返回整个Sales表,它看起来像是Sales[Quantity]列中的一个无用的筛选器,但是,它确实是问题的第一个来源。

      事实上,正如你可能知道的,在 DAX 表中,随着多个一对多关系的扩展,因此Sales表包括了其扩展列中的所有Product-产品列。因此,当将筛选器放在Sales表上时,它不仅会在Sales表上应用筛选器,而且还会在产品表 (以及模型的所有其他表 (即使这与此示例无关) 的情况下,对该列表执行筛选。          因此,在调用[Average Sales Amount]之前,通过CALCULATE创建的计算筛选也包括产品表上的筛选器,并替换其所有列上以前存在的所有筛选器。
        Product-产品筛选是什么?即引用Sales表中的所有产品。

      单独而言,这还不足以是产生错误的结果。事实上,为了理解整个场景,我们需要详细阐述第二个问题:即 ADDCOLUMNS 迭代过程中所产生的筛选转换。
        CALCULATE筛选器参数计算执行以下操作:
      (1)CALCULATE的FILTER参数。在本例中,筛选条件为:Sales[Quantity] > 3:销售表中销售数量大于3的那些行:
      (2)这将生成新的当前筛选器,并在它要筛选的同一列上(销售表数量列)覆盖以前的原有筛选器。这是众所周知的,但为什么它与当前计算环境有关?
      因为, ADDCOLUMNS会 循环遍历Product[Color]列,所以,它调用 CALCULATE来强制筛选转换,并在包含Product[Color]的当前筛选器中计算度量值。不过,在生成新的筛选器之前计算其筛选器参数。换言之:CALCULATE的筛选参数是在原始的筛选器中进行的,而不是在计算结果的筛选器。

      当你编写FILTER ( Sales )时,你定义的是针对Sales表的所有列表,而不仅仅是Color列上的筛选 (假设我们是筛选Red红色),这 仅在以后应用。

      混合的筛选行为

        阐述了这两点,让我们回顾一下,混合使用它们时的效果。到目前为止,我们发现筛选器的结果是:
      (1)销售量大于3的所有行的列表,不管它们的Color列颜色是多少。事实上,当筛选器正在循环遍历Sales-销售表时,Product[Color]并不是筛选器的一部分;
      (2)筛选应包含扩展的销售表的所有列。其中,当然包括Product表的Product[Color]列。

      当筛选转换发生时,计算需要根据当前实际行筛选 (由于筛选转换) 和其筛选器参数生成新的筛选器。问题是:在这个新的当前筛选器中选择哪种颜色?不幸的是,它不会是当前由 ADDCOLUMNS 迭代的颜色,该结果是完全不同的。实际上,计算将需要合并两个筛选器筛选:

    (1)  第一个,包含当前迭代的颜色,这由筛选转换生成;
    (2) 第二个,包含扩展表的所有列,其中包含一次售出数量超过3的所有产品。

      在两个筛选的合并中有非常明确的优先顺序:首先,应用筛选转换引起的筛选,然后再应用筛选器参数,这可能会覆盖由筛选转换生成的筛选。
        最后,这就是发生的事情:
      (1)  筛选转换需要对特定的颜色进行筛选,假设它是Red-红色的;
      (2)  FILTER-筛选器参数 (具有优先级) 再次要求Red颜色为 "销售数超过3的任何产品的颜色"。
      由于后面的筛选器是在其后被引用的,因此,它会覆盖Red-红色产品的筛选,这在迭代期间的所有行中都很明显。因此,该公式将为所有行返回相同的值。

      备注:前面看着有点晕,其实是列表筛选的成立条件:筛选的先后顺序引起的列表范围变化问题,先筛选的列表集范围(以数量列值 >3的内部筛选出列表子集),它包含外部筛选的列表子集(颜色列中为Red的行),那么,后面的筛选其实是没有意义的(筛选不起作用)。如果你将这两个列与其所在表的主键基数(唯一值)对比就很容易看出。

        解决问题

      公式的正确表述是非常简单的,但是,在看它之前,值得看看前面提出的解决方案。事实上,这就是解决办法:

DEFINE
MEASURE Sales[Average Sales Amount] =
AVERAGEX ( Sales, 'Sales'[Quantity] * 'Sales'[Net Price] )
EVALUATE
ADDCOLUMNS  ( VALUES ( Product[Color] ),
"Sales",
CALCULATE (
CALCULATE ( [Average Sales Amount],  // 嵌套一个CALCULATE()
FILTER ( Sales, Sales[Quantity] > 3 )))) 

      通过在内部计算数据之外添加CALCULATE,获得了在仅包含Red--红色产品的当前筛选器中计算FILTER函数的效果。因为外部CALCULATE已将ADDCOLUMNS的行筛选合并到当前筛选中了。 该解决方案是可行的,但它还是稍显复杂,因为正确的代码表述要容易得多:

DEFINE

MEASURE Sales[Average Sales Amount] =
AVERAGEX ( Sales,'Sales'[Quantity] * 'Sales'[Net Price] )
EVALUATE
ADDCOLUMNS (
VALUES ( Product[Color] ),
"Sales",
CALCULATE (  [Average Sales Amount],
Sales[Quantity] > 3  ))

      该公式的最后一个版本不受前一个问题的影响,因为现在,CALCULATE的筛选器参数仅使用 "Sales[Quantity]列。因此,它不会将其影响扩展到整个Product表中作为Sales表的筛选器。

    结论

        在 DAX 世界中很少有黄金规则,但有一点是肯定的:

      当你可以筛选一列时,永远不要筛选表。

        实际上,当你将筛选表用作CALCULATE中的筛选器参数时,需要你了解扩展表及其行行为。

你可能感兴趣的:(CALCULATE 庖丁解牛系列- 扩展表 (3))