理解 ALL, ALLEXCEPT, and ALLNOBLANKROW
ALL 都是一个有用的函数,它返回一个表的所有行或一个列的所有值,这取决于您所使用的参数。例如,下面的DAX查询返回产品表中的所有行
EVALUATE
ALL ( Product )
你不能在 ALL 参数中指定一个表表达式。您必须指定表名或列名列表。如果您使用单个列,那么结果就是一张原表去重后形成的唯一值列表,如图3-8所示。
EVALUATE
ALL ( Product[Class] )
图3-8 对列的所有值的查询返回一个所有惟一值的列表
你可以在 ALL函数的参数中指定来自同一个表的更多列。如果您使用许多列,那么结果将是一张包含了这些列中已有的值组合的列表(可以理解为对多个列做笛卡尔积,然后筛选其中原表中已经存在的列组合)。例如,下面的表达式产生如图3-9所示的结果:
EVALUATE
ALL ( Product[Class], Product[Color] )
ORDER BY Product[Color]
图3-9 关于更多列的all函数查询返回一个现有的值组合的列表
在所有的变体中,ALL函数忽略了任何现有的过滤器来产生它的结果。您可以将ALL函数用作迭代函数的参数,例如SUMX和FILTER,或者作为CALCULATE函数中的筛选参数(稍后您将看到)
如果您想要在一个ALL函数调用中包含表格的大部分列,您可以使用ALLEXCEPT函数来代替。ALLEXCEPT函数的语法需要一个表,后面是您想要从结果中排除的列。因此,在表格的其他列中,ALLEXCEPT返回一张表,其中包含了其他列现有的值组合的唯一列表。
在实际运用中,这是一种编写DAX表达式的方法,它将自动包含非参数列的所有结果,以及在之后的表模型中可能出现的任何额外的列。例如,如果您有一个包含5个列(ProductKey, Product Name, Brand, Class, Color)的产品表,那么下面的语法就会产生相同的结果
ALL ( Product[Product Name], Product[Brand], Product[Class] )
ALLEXCEPT ( Product, Product[ProductKey], Product[Color] )
但是,如果您稍后添加两列 Product[Unit Cost]和Product[Unit Price],那么ALL函数的结果会忽略它们,而ALLEXCEPT函数则将返回等效的:
ALL (
Product[Product Name],
Product[Brand],
Product[Class],
Product[Unit Cost],
Product[Unit Price]
)
下列查询返回一个表,该表除了产品表之外的Product Code和Color以外的所有列。图3-10的结果与原始表的行数相同,因为结果包括ProductKey列,它每一行具有惟一的值。结果中的其他列组合可能会返回较少的行数,因为ALLEXCEPT消除了返回列中值的重复组合。
EVALUATE
ALLEXCEPT ( Product, Product[ProductKey], Product[Color] )
图3-10 ALLEXCEPT返回现有的所有非参数指定列的值的组合
在前面的例子中,您已经在一个EVALUATE语句中看到了ALL函数,它执行DAX表达式,而没有任何现有的筛选条件。出于这个原因,最好是看一个在透视表中使用ALL函数计算表的行数的例子,这些例子中每个单元格使用不同的筛选条件来计算度量值。考虑以下度量值:
[Products] := COUNTROWS ( Product )
[All Products] := COUNTROWS ( ALL ( Product ) )
[All Brands] := COUNTROWS ( ALL ( Product[Brand] ) )
您可以在图3-11中看到每个度量值的不同结果的示例
图3-11所有产品和所有品牌都忽略了行上的类别并且总是显示相同的数字对于每一个产品类别,在All Products和All Colors列中,总是有相同的结果。在ALL函数的计算中忽略了透视表中单元格的筛选条件。
当你调用ALL函数在一个关系的父表时,如果子表包含一个或多个行在父表中不匹配任何值的行,就会返回一个额外的空白行。您可以通过使用 ALLNOBLANKROW 函数 代替ALL函数来忽略这个特殊的行。
考虑下面的度量值:
[All Products] := COUNTROWS ( ALL ( Product ) )
[All NoBlank Products] := COUNTROWS ( ALLNOBLANKROW ( Product ) )
[All Brands] := COUNTROWS ( ALL ( Product[Brand] ) )
[All NoBlank Brands] := COUNTROWS ( ALLNOBLANKROW ( Product[Brand] ) )
[All Sizes] := COUNTROWS ( ALL ( Product[Size] ) )
[All NoBlank Sizes] := COUNTROWS ( ALLNOBLANKROW ( Product[Size] ) )
在图3-12中,您可以看到ALL和ALLNOBLANKROW度量值之间的区别。对于Product表 和 the Products[Model]列,ALL版本的度量值比ALLNOBLANKROW版本多返回一行。原因是销售表中有一些行,在产品表中没有与之匹配的行,因此额外的一行实际上被添加到产品表中,您可以在图3-12中看到(空白)行中的结果。
图3-12 如果目标表包含一个因为不匹配而产生额外的空白行,那么ALL和 ALLNoBlank度量值都是不同的您应该注意到, All Sizes和All NoBlank Sizes计算结果总是相同。这些度量查询Products[Size]列的数量。在这种情况下,ALL和ALLNOBLANKROW函数返回相同的值,因为 Products[Size]列已经包含了一个产品的空白值。在图3-13的例子中,有569个空白大小的产品,加上一个额外的无法引用销售表产品空白产品,总共有570个。对于Products[Size]列,所有这些行都被分组在同一个的(空白)值中。
图3-13 透视表的行是每个产品名称的SIZE。第一个(空白)值的大小包括空白SIZE的产品和没有在销售表中匹配到的额外空白产品只有当你写了一个DAX公式需要它忽略了关系中不匹配的值时,你才应该使用ALLNOBLANKROW。然而,相较于ALL函数,ALL的使用则显得更为通用,而ALLNOBLANKROW用的则很少。
理解 VALUES和 DISTINCT
在上一节中,您已经看到,ALL主要用于返回一个列中所有惟一值的表。DAX提供了另外两个类似的函数,它们返回一个列的惟一值列表:VALUES和DISTINCT
如果在没有任何其他筛选条件情况下,在EVALUATE语句中 VALUES和 DISTINCT似乎是相同的。然而,当你把这些函数放在DAX度量值中时,你可以观察到一个不同的行为,因为计算发生在一个透视表的每个单元格的不同上下文中。考虑以下在产品表中Brand列和 Size 列计算不同唯一值数量的度量值。
[Products] := COUNTROWS ( Product )
[Values Brands] := COUNTROWS ( VALUES ( Product[Brand] ) )
[Distinct Brands] := COUNTROWS ( DISTINCT ( Product[Brand] ) )
[Values Sizes] := COUNTROWS ( VALUES ( Product[Size] ) )
[Distinct Sizes] := COUNTROWS ( DISTINCT ( Product[Size] ) )
VALUES 返回当前可见单元中惟一值列表,包括没有匹配的空白行。 DISTINCT同样,但是不返回没有匹配的空白行。但是,如果一个空白值作为列的有效值出现,那么这两个函数都将包含一个空行。唯一的区别是添加了空白行来处理关系中缺失值
一个例子可能会帮助你区分这种不同。正如表3-14,每个产品等级筛选出不同数量的产品。比如Deluxe有360种产品,有11个不同的品牌和204个不同的尺码。 VALUES 和 DISTINCT 返回相同的结果,只有一个例外:透视表中行的(空白)产品类。结果增加了一个虚拟行,以显示在Sales Amount中没有匹配到的产品。
图3-14 VALUES 和 DISTINCT 的区别,只有当一个空白产品被添加到报告的(空白)行中是可见的模型中以包含不匹配的行另一个在图3-14中可见的区别在 Grand Total中。 VALUES 应用于 Product[Brand],返回的值比应用在同样列上DISTINCT的值多一个。然而,这并不会发生在 VALUES 应用于Products[Size]的值上,后者返回的值与 DISTINCT应用在同样的列上相同。这个原因是 Distinct Sizes 列包含至少一个产品的空白值,因此添加的空白产品不会为 Distinct Sizes 列添加一个新的惟一值。
当没有筛选条件时, DISTINCT行为与ALLNOBLANKROW的行为相同,与此对比,VALUES行为与ALL行为相同。
VALUES也接受一张表作为参数。在这种情况下,它返回在当前可见的整个表,同时也包括没有匹配的空白行。例如,在数据模型中考虑以下度量值,其中Sales表与 Product 表有关系,并包含与产品主键不匹配的交易。
[Products] := COUNTROWS ( Product )
[Values Products] := COUNTROWS ( VALUES ( Product ) )
[All NoBlank Products] := COUNTROWS ( ALLNOBLANKROW ( Product ) )
[All Products] := COUNTROWS ( ALL ( Product ) )
VALUES作为标量值使用
即使 VALUES 是一个表函数,您也经常使用它来计算标量值,因为在DAX中有一个特殊的特性,您将在本节中学习。例如,您可以在表达式中找到VALUES,比如下面的表达式,它会显示颜色名称,以确保某一特定选择的产品都具有相同的颜色:
[Color Name] :=
IF ( COUNTROWS ( VALUES ( Product[Color] ) ) = 1, VALUES ( Product[Color] ) )
您可以在图3-15中看到结果。当Color Name列包含空白时,这意味着有两种或更多不同的颜色
图3-15当VALUES返回一行时,您可以将其用作标量值,例如Color Name 度量值这里的有趣之处在于,我们将VALUES的结果作为标量值,即使它返回一个表。这不是一种VALUES特殊的行为而是DAX语言一种更普遍的行为。
如果一个表表达式返回一个带有一行和一列的表,那么如果需要的话,可以自动转换成为标量值。
在实践中,如果结果恰好有一行和一列,您可能会使用任何表表达式作为标量值。当表格返回更多的行时,您会在执行时得到这个错误:“在期望单个值的地方提供了多个值的表。“出于这个原因,您应该始终编写一个返回不同结果的条件来确保能够转换为标量值,以防表表达式返回更多的行(您应该已经知道,当您编写DAX表达式时,表表达式是否只返回一行)。
前面例子的 Color Name度量使用COUNTROWS来检查产品表的 Color 列是否只有一个值。一种更简单的方法是使用HASONEVALUE,它执行相同的检查,如果列只有一个值返回的值,反之则返回 FALSE 。以下两个语法是等价的
COUNTROWS ( VALUES ( <column> ) ) = 1
HASONEVALUE ( <column> )
您应该使用HASONEVALUE而不是COUNTROWS,原因有两个:它更易于阅读,而且可能会稍微快一些。下面是基于HASONEVALUE的 Color Name 度量值的更好的实现:
[Color Name] :=
IF ( HASONEVALUE ( Product[Color] ), VALUES ( Product[Color] ) )
您经常使用 VALUES 作为标量表达式的原因是它返回单个列,并且可能返回单行,这取决于执行上下文。在许多DAX模式中, VALUES 作为标量表达式是很常见的,并且在本书中反复出现。
网友评论