DAX:【翻译】ALLSELECTED 教程

原文是:The definitive guide to ALLSELECTED

 

ALLSELECTED 可以返回一个表,也可以删除过滤器并恢复以前的过滤器上下文。 在这两种情况下,它都是通过使用迭代器在过滤器上下文堆栈上留下的最后一个影子过滤器上下文来实现的。

一,ALLSELECTED函数的用法

ALLSELECTED 函数的用法:作为一个表值函数或者CALCULATE函数的调节器。

在继续之前,我们需要回答一个重要的问题:ALLSELECTED 是一个表函数,还是像 KEEPFILTERS 和 USERELATIONSHIP 那样充当 CALCULATE 修饰符? 答案是它显示了这两种行为,具体取决于参数的数量和使用它的上下文。 实际上,ALLSELECTED 可以与三个不同的参数一起使用:表、列或根本没有参数,如以下几个示例所示:

AllSelectedColumn :=
CALCULATE (
    [SalesAmount],
    ALLSELECTED ( Customer[Occupation] )
)
 
AllSelectedTable :=
CALCULATE (
    [SalesAmount],
    ALLSELECTED ( Customer )
)
 
AllSelectedAll :=
CALCULATE (
    [SalesAmount],
    ALLSELECTED ()
)

下图显示了这三个度量在包含产品类别、客户性别和客户职业的矩阵中使用时的结果:

重要说明:Power BI 中的矩阵和 Excel 中的数据透视表使用等效于 DAX 迭代器来评估每个单元格的度量,该迭代器评估用户界面行和列中使用的每个属性组合的度量。 这对于本文的其余部分很重要。

这些示例将 ALLSELECTED 显示为 CALCULATE 修饰符,尽管如此,ALLSELECTED 也可以用作表函数,如以下代码所示:

AllSelectedCustomerSales :=
SUMX (
    ALLSELECTED ( Customer ),
    [SalesAmount]
)

当用作表函数时,ALLSELECTED 按照下面解释的规则返回表的子集或列的值的子集。 另一方面,当不带参数使用时,如以下代码所示,ALLSELECTED 只能用作 CALCULATE 修饰符:

AllSelectedSales :=
CALCULATE (
    [SalesAmount],
    ALLSELECTED ()
)

目前,重点是与表或列一起使用时的 ALLSELECTED - 稍后我们将介绍不带参数的 ALLSELECTED,这种情况仅用作 CALCULATE 修饰符。 在对 ALLSELECTED 进行更深入的分析之前,我们需要介绍影子过滤上下文,因为它们在 ALLSELECTED 的描述中至关重要

二,介绍影子筛选上下文

影子过滤上下文是由迭代器创建的一种特殊类型的过滤上下文,在初始状态下,影子过滤上下文是不活动的。不活动的过滤器上下文只是保持休眠状态,并且不会以任何方式影响代码行为。 尽管如此,它仍然存在,而且对于本文而言它很重要,因为 ALLSELECTED 在其执行过程中激活影子过滤器上下文。
我们考虑以下DAX代码:

SalesAmount :=
SUMX (
    Sales,
    Sales[Quantity] * Sales[Net Price]
)

作为一个迭代器,SUMX 生成一个包含 Sales 表的影子过滤器上下文。它不包含整个 Sales 表,像往常一样,它只包含在当前过滤器上下文中可见的行。 作为影子过滤器上下文,它是不活动的。 因此,过滤器上下文不会影响计算,这就是影子过滤器上下文很少被讨论的原因。 为了证明这一点,我们看下面的代码,它的行为符合预期。 它的结果是总销售量乘以颜色数。 缺少调用 SUM 的上下文转换是有意的:

WrongSalesAmount :=
SUMX (
    VALUES ( Product[Color] ),
    SUM ( Sales[Quantity] )
)

在迭代期间,Color 上的行上下文不会转换为过滤器上下文,因为没有 CALCULATE 执行上下文转换。正如这里所解释的,确实存在一个过滤器上下文:“影子”过滤器上下文。影子过滤器上下文包括迭代器启动时在当前过滤器上下文中处于活动状态的颜色列表。实际上,阴影过滤器上下文在迭代期间是不活动的,除非它被一个函数打开 - 即 ALLSELECTED。换句话说,当不使用 ALLSELECTED 时,忽略影子过滤上下文的存在是安全的。另一方面,当用户决定利用 ALLSELECTED 时,影子过滤器上下文至关重要。

细心的读者可能会注意到,如果影子过滤器上下文处于活动状态,它就不会改变结果。事实上,阴影过滤器上下文包含所有颜色,结果与原始过滤器上下文完全相同。但是,使用如下更精细的表达式,复杂性开始增加:

AnotherSalesAmount :=
SUMX (
    CALCULATETABLE (
        VALUES ( Product[Color] ),
        Product[Color] = "Red"
    ),
    SUM ( Sales[Quantity] )
)

在这种情况下,影子过滤器上下文包含一个颜色——即只有红色。 但是在迭代中,SUM (Sales[Quantity]) 仍然计算所有销售额的总和。 如果影子过滤器上下文处于活动状态,那么引擎只会对红色销售额求和。

在本文中,我们将普通过滤器(即非影子过滤器)称为显式过滤器,其唯一目的是在需要时区分显式过滤器和影子过滤器。
现在我们了解了影子过滤器上下文,我们可以开始分析 ALLSELECTED 对样本数据的行为。

三,举个例子

我们使用一个只有九行三列的表格:产品、品牌和颜色:

在这个表上,我们使用一个外部的 CALCULATETABLE 来过滤一些颜色和品牌。 我们还应用了一个不寻常的度量 ListProductNames。 ListProductNames 使用 CONCATATENATEX 返回当前筛选上下文中可见的产品名称列表,该度量的目的是让我们分析 ALLSELECTED 的结果(当用作表函数时)和表的结果(当 ALLSELECTED 用作 CALCULATE 修饰符时)。

DEFINE
    MEASURE Product[ListProductNames] =
        CONCATENATEX (
            VALUES ( 'Product'[Product] ),
            Product[Product],
            ", "
        )
EVALUATE
CALCULATETABLE (
    ROW ( "Products", [ListProductNames] ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

此查询的结果是:头盔(Helmet)、鞋子(Shoes)、键盘(Keyboard)、钢琴(Piano)。 下图显示了为颜色和品牌生成的两个过滤器,以及生成的过滤器上下文:

到目前为止,没有什么新东西。 在接下来的部分中,我们将使用与 ALLSELECTED 相同的代码片段来分析其行为。

一,具有一个列参数的ALLSELECTED

ALLSELECTED ( Table[Column] ) 返回列的值,这些列值是在最后一个影子过滤上下文中被实际过滤之后,仍然可见的值。 在下面的代码中,我们使用 Product[Product] 上的 ALLSELECTED 来检索在最后一个影子过滤器上下文中可见的所有产品名称的列表。 在进一步阅读之前,可以尝试猜测结果会是什么。

EVALUATE
CALCULATETABLE (
    ROW (
        "Products", CONCATENATEX (
            ALLSELECTED ( 'Product'[Product] ),
            Product[Product],
            ", "
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

第一个猜测是按照上一张图片中的过滤器进行操作。答案将是结果与之前相同:只有蓝色、绿色以及 Contoso 和 Fabrikam 的四种产品 - 对应于Helmet、Shoes、Keyboard、Piano,如前面的示例所示。 实际上,结果非常不同——它返回所有产品:Bike, Helmet, Shoes, Robot, Shirt, Rollerblades, Motorbike, Keyboard, Piano

实际上,ALLSELECTED 返回由最后一个影子过滤器上下文过滤的列的值。 读者可能已经注意到,当我们调用 ALLSELECTED 时没有活动的迭代器。 因此,没有要激活的影子过滤器上下文。 结果,所有产品名称都被返回,因为 Color 和 Brand 都没有过滤 Product[Product] 列——尽管它是交叉过滤的。

这是这里要学习的第一课:ALLSELECTED 不考虑过滤器上下文,它的主要目的是检索先前设置的影子过滤器上下文。 ALLSELECTED 与 VALUES或 DISTINCT非常不同。 VALUES 和 DISTINCT 始终考虑过滤器上下文,而 ALLSELECTED 不考虑。 ALLSELECTED 作用于列并检查该列是否被影子过滤器上下文过滤,忽略任何交叉过滤器。

对于习惯于以这种方式编写先前代码的读者来说,事情可能会更加混乱:

EVALUATE
CALCULATETABLE (
    ROW (
        "Products", CALCULATE (
            CONCATENATEX (
                VALUES ( 'Product'[Product] ),
                Product[Product],
                ", "
            ),
            ALLSELECTED ( Product[Product] )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

这一次的结果会如预期的那样,是:Helmet、Shoes、Keyboard、Piano。当我们阅读它时,我们倾向于认为这是因为 ALLSELECTED ( Product[Product] ) 在 Product[Product] 列上应用了过滤器。但这不是正在发生的事情。在这种情况下,ALLSELECTED 不会对 Product[Product] 列应用任何类型的过滤器,因为没有影子过滤器上下文。当调用DAX时,VALUES 通过观察颜色和品牌上的过滤器来分析当前上下文中可见的产品。 ALLSELECTED 对过滤没有任何贡献。换句话说,过滤 Product[Product] 的不是 ALLSELECTED,而是由 VALUES 明确表示的 Color 和 Brand 交叉过滤的效果。

到目前为止,我们已经看到 ALLSELECTED 没有以任何方式过滤列。现在是时候生成影子过滤器上下文并要求 ALLSELECTED 激活它了。为此,我们需要一个迭代器——我们用 ADDCOLUMNS 替换 ROW,在 Brand 上迭代,如下代码所示:

EVALUATE
CALCULATETABLE (
    ADDCOLUMNS (
        VALUES ( 'Product'[Brand] ),
        "Brands",
        CONCATENATEX (
            ALLSELECTED ( 'Product'[Brand] ),
            Product[Brand],
            ", "
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

正如预期的那样,此查询的结果是为每个品牌返回 Contoso、Fabrikam 的两行。

乍一看,似乎 ALLSELECTED 已经恢复了来自外部 CALCULATETABLE 的过滤器。 然而,情况并非如此,尽管它返回相同的结果。 发生的情况是 ADDCOLUMNS(它是一个迭代器)生成一个包含迭代表的影子过滤器上下文。 迭代表是 VALUES(Product[Brand]) 的结果 - 在仅包含 Contoso 和 Fabrikam 的筛选器上下文中进行评估 - 返回这两个品牌。

ALLSELECTED 返回在最后一个影子过滤器上下文中可见的品牌,产生预期的结果。 如何确定这确实是 ALLSELECTED 的行为? 可以尝试将 VALUES 替换为 ALL,这样 ADDCOLUMNS 的迭代就不再发生在两个品牌上,而是发生在所有品牌上:

EVALUATE
CALCULATETABLE (
    ADDCOLUMNS (
        ALL ( 'Product'[Brand] ),
        "Brands", CONCATENATEX (
            ALLSELECTED ( 'Product'[Brand] ),
            Product[Brand],
            ", "
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

这一次,结果将是一个包含三行的表格,每行显示所有品牌:

原因是 ADDCOLUMNS 现在迭代 ALL(Product[Brand])。 因此,影子过滤器上下文包含 Product[Brand] 的所有值。 激活后,影子过滤器上下文会显示所有值,尽管外部 CALCULATETABLE 仅过滤三个品牌中的两个。

我们现在通过在同一列上使用两个迭代器使事情变得更复杂。 出于语法目的,我们需要使用 SELECTCOLUMNS 重命名列以避免名称冲突。 这是一个更详细的示例:

EVALUATE
CALCULATETABLE (
    GENERATE (
        SELECTCOLUMNS (
            ALL ( 'Product'[Brand] ),
            "Outer Brand", Product[Brand]
        ),
        GENERATE (
            SELECTCOLUMNS (
                VALUES ( 'Product'[Brand] ),
                "Inner Brand", Product[Brand]
            ),
            ROW (
                "Brands", CONCATENATEX (
                    ALLSELECTED ( 'Product'[Brand] ),
                    Product[Brand],
                    ", "
                )
            )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

在同一个 Product[Brand] 列上有两个嵌套迭代,在最里面的公式中使用 ALLSELECTED 作为表函数。 结果是在最近的影子过滤器上下文中迭代的品牌列表。 由于最近的影子过滤器上下文是由扫描 VALUES ( Product[Brand] ) 的迭代器生成的,并且由于 VALUES 受到最外层 CALCULATETABLE 创建的过滤器上下文的过滤,因此Inner Brand的结果仅包括两个品牌 :

在Inner Brand和Outer Brand中交换 VALUES 和 ALL 会导致不同的结果。 实际上,内部迭代将扫描 ALL 并且 ALLSELECTED 将返回所有值:

EVALUATE
CALCULATETABLE (
    GENERATE (
        SELECTCOLUMNS (
            VALUES ( 'Product'[Brand] ),
            "Outer Brand", Product[Brand]
        ),
        GENERATE (
            SELECTCOLUMNS (
                ALL ( 'Product'[Brand] ),
                "Inner Brand", Product[Brand]
            ),
            ROW (
                "Brands", CONCATENATEX (
                    ALLSELECTED ( 'Product'[Brand] ),
                    Product[Brand],
                    ", "
                )
            )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

关于迭代Brand列的讨论到此结束,继续讨论产品名称——Product[Product],如果我们在 Brand 的两次迭代中查询 ALLSELECTED ( Product[Product] ),我们应该期待什么结果? 使用适当的规则很容易解决这个问题:ALLSELECTED 返回在最后一个影子过滤器上下文中可见的所有产品名称。 这是一个在查看代码时猜测答案的机会:

EVALUATE
CALCULATETABLE (
    GENERATE (
        SELECTCOLUMNS (
            VALUES ( 'Product'[Brand] ),
            "Outer Brand", Product[Brand]
        ),
        GENERATE (
            SELECTCOLUMNS (
                ALL ( 'Product'[Brand] ),
                "Inner Brand", Product[Brand]
            ),
            ROW (
                "Products", CONCATENATEX (
                    ALLSELECTED ( 'Product'[Product] ),
                    Product[Product],
                    ", "
                )
            )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

解决方案相当简单:因为没有过滤Product[Product]的影子过滤器上下文,行上下文无法转换为筛选上下文,且行上下文无法筛选数据,所以 ALLSELECTED 只返回所有产品名称:

此时要思考的一个好问题是,是否可以返回倒数第二个影子过滤器上下文,就像 EARLIER 与行上下文一样。 如果只依赖 ALLSELECTED,答案是否定的。 话虽如此,DAX 不仅提供 ALLSELECTED,它还提供了一种更强大的机制来处理外部上下文,无论是显式上下文还是影子上下文:变量。 如果在内部迭代中,想要访问 Brand 的外部上下文,则只需在最里面的影子过滤器上下文启动之前将 Product[Brand] 的 ALLSELECTED 值保存在一个变量中,如下例所示:

EVALUATE
CALCULATETABLE (
    GENERATE (
        SELECTCOLUMNS (
            VALUES ( 'Product'[Brand] ),
            "Outer Brand", Product[Brand]
        ),
        VAR OuterProducts =
            ALLSELECTED ( 'Product'[Brand] )
        RETURN
            GENERATE (
                SELECTCOLUMNS (
                    ALL ( 'Product'[Brand] ),
                    "Inner Brand", Product[Brand]
                ),
                ROW (
                    "Brands", CONCATENATEX (
                        OuterProducts,
                        'Product'[Brand],
                        ", "
                    )
                )
            )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

我们建议读者自己测试的结果仅显示 Contoso 和 Fabrikam,它们是外部过滤器上下文中的品牌。

提醒一下,使用变量可以让代码在以后更容易编写和调试:必须习惯使用它们,因为它们在复杂的 DAX 代码中起着非常重要的作用。
至此,我们已经深入分析了 ALLSELECTED 用作单列表函数时的语义。 下一步是添加一些复杂性并将其与完整表一起用作参数。

二,参数是表的ALLSELECTED函数

当使用 ALLSELECTED ( Product )时,事情开始变得更加复杂。 实际上,一个表包含多个列。 在每一列上,可能有一个显式过滤器(使用 CALCULATE 设置)或一个由迭代器设置的影子过滤器。 此外,多个过滤器——显式过滤器或影子过滤器——可以嵌套在每一列上,使事情变得更加复杂。 让我们从一张完整的表开始研究 ALLSELECTED。

将 ALLSELECTED 用于表和将其用于列之间的第一个明显区别来自我们最简单的测试查询:

EVALUATE
CALCULATETABLE (
    ROW (
        "Products", CONCATENATEX (
            ALLSELECTED ( 'Product' ),
            Product[Product],
            ", "
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

在本文的开头,我们已经声明 ALLSELECTED 忽略列上的显式过滤器,它只使用影子过滤器上下文。 如果与列参数一起使用,则为 true,但如果与 table 参数一起使用,则为 false。 其实前面查询的结果是:Helmet, Shoes, Keyboard, Piano.

换言之,带有表的 ALLSELECTED 使用了由 CALCULATETABLE 创建的显式列筛选器,并且仅返回蓝色或绿色以及 Contoso 或 Fabrikam 的产品。 读者会注意到,在第一个查询中,没有影子过滤上下文,只有明确的显式过滤上下文。

如果我们在其中一列上引入迭代会发生什么? 此查询显示预期的行为:

EVALUATE
CALCULATETABLE (
    ADDCOLUMNS (
        ALL ( 'Product'[Color] ),
        "Products", CONCATENATEX (
            ALLSELECTED ( 'Product' ),
            Product[Product],
            ", "
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

由于 ADDCOLUMNS 迭代所有产品颜色,因此现在有一个包含所有产品颜色的影子过滤器上下文。 此影子过滤器上下文由 ALLSELECTED 激活,它替换了先前的蓝色和绿色过滤器。 另一方面,未由影子筛选器上下文筛选的 Brand 列仍然维护 Contoso 和 Fabrikam 上的显式筛选器。 结果如下,以任何颜色显示所有属于 Contoso 或 Fabrikam 的产品。 换句话说,颜色上的过滤器被替换了,品牌上的过滤器没有被替换。 我们建议读者查看原始数据,以充分理解这个复杂的结果。

如果两列都有一个影子过滤器上下文——由于下面代码中的双重迭代——那么这两个影子过滤器完全替换了由最外面的 CALCULATETABLE 产生的显式过滤器,如下面的代码所示:

EVALUATE
CALCULATETABLE (
    GENERATE (
        ALL ( Product[Brand] ),
        ADDCOLUMNS (
            ALL ( 'Product'[Color] ),
            "Products", CONCATENATEX (
                ALLSELECTED ( 'Product' ),
                Product[Product],
                ", "
            )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

最后一个查询的 Products 列中的结果是所有产品的列表,因为 Color 和 Brand 显式过滤器都已被相应的阴影过滤器替换。 因此,ALLSELECTED 恢复所有具有影子筛选上下文的列上的最后一个影子筛选上下文,并在其他列上维护显式筛选器(如果有)。

下一个查询是一个艰难的查询:有一个包含所有品牌的外部影子过滤器上下文和一个仅包含两个品牌和两种颜色的内部影子过滤器上下文。 因为 ALLSELECTED 将激活每列上的最后一个影子过滤器上下文,所以它将在最里面的过滤器上下文停止。 事实上,它已经过滤了品牌和颜色。 因此,在这种情况下,外部影子过滤器上下文未激活:

EVALUATE
CALCULATETABLE (
    GENERATE (
        SELECTCOLUMNS (
            ALL ( Product[Brand] ),
            "Outer Brand", Product[Brand]
        ),
        ADDCOLUMNS (
            CROSSJOIN (
                VALUES ( Product[Color] ),
                SELECTCOLUMNS (
                    VALUES ( Product[Brand] ),
                    "Inner Brand", Product[Brand]
                )
            ),
            "Products", CONCATENATEX (
                ALLSELECTED ( 'Product' ),
                Product[Product],
                ", "
            )
        )
    ),
    Product[Color] IN { "Blue", "Green" },
    Product[Brand] IN { "Contoso", "Fabrikam" }
)

Products 列中生成的内容对于结果的所有行都是相同的:Helmet, Shoes, Keyboard and Piano

Brand 上的外部 ALL 不会激活,被最里面的阴影过滤器上下文覆盖。 完整的结果如下:

三,第一次总结

让我们总结一下迄今为止的发现:

  • 迭代器创建影子过滤上下文。
  • CALCULATE 创建显式过滤上下文。
  • ALLSELECTED 是一个表函数,当与表或列一起使用时会返回不同的结果。
  • 对于列参数,ALLSELECTED 返回列的值,仅考虑最后一个影子过滤器(如果有)。 如果不存在影子筛选上下文,则 ALLSELECTED 返回所有列值。
  • 对于表参数,ALLSELECTED 返回一个表,其中包含在对具有影子过滤器的任何列应用最后一个影子过滤器后剩余的所有行; 或者,如果没有可用的影子过滤上下文,则 ALLSELECTED 返回最后一个显式过滤上下文。

在这一点上,我们提醒读者注意 ALLSELECTED 经常用作 CALCULATE 修饰符而不是表函数。 我们已经看到,下面这两个度量返回了不同的结果:

AllSelectedWithCALCULATE :=
CALCULATE (
    CONCATENATEX (
        VALUES ( 'Product'[Product] ),
        Product[Product],
        ", "
    ),
    ALLSELECTED ( Product[Product] )
)
 
AllSelectedAsTableFunction :=
CALCULATE (
    CONCATENATEX (
        ALLSELECTED ( 'Product'[Product] ),
        Product[Product],
        ", "
    )
)

EVALUATE  
    SUMMARIZECOLUMNS(
      'Product'[Color],
      'Product'[Brand],
      'Product'[Product],
      "AllSelectedWithCALCULATE", [AllSelectedWithCALCULATE],
      "AllSelectedAsTableFunction", [AllSelectedAsTableFunction]
    )

事实上,在上面显示的措施中,外部过滤器上下文是由 VALUES 注入的,而不是由 ALLSELECTED 注入的,如前所示。 此外,ALLSELECTED 的操作方式与 ALL 类似,当用作 CALCULATE 修饰符时,它会从过滤器上下文中删除相应的过滤器。
然而,以下情况仍有待分析:

  • 不带参数的 ALLSELECTED 的用法,当 ALLSELECTED 不再是表函数,而只是 CALCULATE 修饰符时。
  • ALLSELECTED 和上下文转换之间的交互。

四,没有参数的ALLSELECTED函数

在完全没有参数的情况下,ALLSELECTED 显然不是一个表函数。 相反,ALLSELECTED 只能用作 CALCULATE 的过滤器参数。 直观地说,它对某个影子过滤上下文引用的所有列执行 ALLSELECTED。 在实践中,恢复每列的最后一个影子过滤器上下文。

既然对ALLSELECTED函数已经有了深入的介绍了,就不再需要一步一步来了。我们将举一个例子,让读者得出结论,我们定义了两个度量:

AllSel :=
CALCULATE (
    SUM ( Sales[Quantity] ),
    ALLSELECTED ()
)
 
AllSelSumX :=
SUMX (
    VALUES ( 'Product'[Brand] ),
    SUMX (
        VALUES ( 'Product'[Color] ),
        CALCULATE (
            SUM ( Sales[Quantity] ),
            ALLSELECTED ()
        )
    )
)

然后,我们将它们投影到 Power BI 报告中:

在报告中,只选择了 Contoso 和 Fabrikam,总共有 6 个产品。 AllSel 的值看起来是正确的,因为它为每一行都是矩阵中的 Quantity 总数。令人困惑的结果在 AllSelSumX 中:唯一看起来正确的数字是总计 (300×6=1800),而所有其他数字看起来都是错误的。

注意:对于 那些只能简单理解ALLSELECTED 的用户来说,这些数字看起来是错误的。事实上,一旦清楚 ALLSELECTED 是如何工作的,它们就是完全正确的。事实上,如果我们关注 30 (Contoso/Blue) 的值,解释就很简单了。我们从只包含一种产品、一种品牌、一种颜色的过滤器上下文开始。两个嵌套迭代器(品牌上的 SUMX 和颜色上的 SUMX)都只迭代一行,这意味着两个 SUMX 函数都会生成一个仅包含一个值的阴影过滤器上下文。 ALLSELECTED 恢复这些影子过滤器上下文,以便在恰好包含一行的过滤器上下文中执行 SUM。因此,为每一行计算的值是给定行的过滤器上下文中唯一可见的产品的值,因为 ALLSELECTED 通过激活两个影子过滤器上下文来恢复它。遵循相同的路径,AllSelSumX 的所有值开始变得更有意义,因为现在很清楚 ALLSELECTED 的工作原理,恢复影子过滤器上下文。我们强烈建议读者在阅读以下提示之前至少花一点时间来理解 Contoso 行:

180 等于 60 乘以 3;对品牌进行一次迭代;对颜色进行三次迭代;并且,颜色的最内层阴影过滤器上下文包含三种颜色。

这并不是说显示的 AllSelSumX 值有任何意义。最终用户和读者很可能认为这个数字是错误的。在这里,目标不是找到正确计算数字的方法。相反,我们专注于准确了解 ALLSELECTED 的工作原理,以便我们依赖它并仅在它产生我们需要的东西时使用它。

五,ALLSELECTED函数和上下文转换

ALLSELECTED 是否以任何方式与上下文转换交互?我们知道 CALCULATE 生成一个过滤器上下文,该过滤器上下文等效于任何现有的行上下文,作为其创建的过滤器上下文的一部分。我们还知道,通过上下文转换生成的过滤器上下文的优先级低于任何显式过滤器上下文。使用 ALLSELECTED 作为 CALCULATE 过滤器参数意味着将影子过滤器上下文转换为显式过滤器上下文。这样,通过上下文转换生成的过滤器,获得跟显式筛选上下文一样的优先级。

因此,看起来 ALLSELECTED 删除了由上下文转换生成的最后一个过滤器上下文,实际上不会发生这样的删除。删除由上下文转换生成的最后一个过滤器上下文的效果只是将影子过滤器上下文转换为显式过滤器上下文的副作用。

六,总结

如此处所述,当人们更熟悉影子过滤器上下文时,ALLSELECTED 的行为更容易理解。话虽如此,ALLSELECTED 是一个非常复杂的函数,因为存在影子过滤上下文,并且因为它们与显式过滤上下文的交互使得很难详细说明结果。

我们通常在培训期间教给用户的是使用 ALLSELECTED 来检索查询上下文——即执行数据透视表或报表的上下文——当且仅当没有迭代发生时。如果有任何数量的迭代,建议用户避免使用 ALLSELECTED,因为结果几乎是不可预测的。实际上,结果非常难以理解。本文介绍了了解 ALLSELECTED 行为所需的所有工具。也就是说,我们不希望每次需要调试度量时都必须执行所有这些复杂的推理步骤。因此,强烈建议:ALLSELECTED 不应该在迭代中使用,除非用户已经非常清楚地了解他们正在做什么并且非常需要它。在大多数情况下,变量可以避免在迭代中使用 ALLSELECTED。为此强烈建议使用变量。

 

 

参考文档:

The definitive guide to ALLSELECTED

posted @ 2020-02-25 10:28  悦光阴  阅读(361)  评论(0编辑  收藏  举报