《DAX权威指南》简体版 (第三章)02
了解ALL,ALLEXCEPT和ALLNOBLANKROW
ALL系列函数包括ALL、ALLEXCEPT以及 ALLNOBLANKROW。
所有这些都是一个有用的系列函数:
它返回一个表的所有行或者一个列的所有值,这取决于使用的参数。
例如,下面的DAX查询返回产品表中的所有行:
EVALUATE
ALL ( Product )
不能在ALL( )参数中指定一个表表达式,而必须指定一个表名的列表或列名的列表。如果使用单个列,结果是一个包含唯一值的列表,如图3-8所示。
EVALUATE
ALL ( Product[Class] ) --ALL
参数为一个表的列表
译者:Class列表中可能存在一个或多个列值为Econmy、Regular、Deluxe的行,ALL(Class)的结果是该列的所有唯一值的列表,即只有Econmy、Regular、Deluxe三个值的列表。
可以在ALL函数的参数中指定来自同一个表的更多个列。如果使用多个列作为参数,结果将是一个具有相同行数量的列表,其中包含这些列中现有值的组合列表。例如,下面的表达式生成图3-9所示的结果。
EVALUATE
ALL ( Product[Class],Product[Color] )
ORDER BY Product[Color]
译者补充:如上图,[Class]列与[Color]列虽然有重复的非唯一值,例如[Class]列的行值Economy,[Color]列的行值Silver Grey、White。从单列的结果来看,似乎是不对的。
但是,ALL ( Product[Class],Product[Color] )是针对[Class]和[Color]两列取唯一值,其中包含这两列中现有值的组合列表中的唯一值。为方便观察,我们换个角度看看:
如图,[Class]和[Color]列的对应行值的两两组合的值,例如:Economy+Silver是一个唯一值,依次类推,Economy+Silver Grey也是唯一值……。由于在实际的运用中,ALL()的列表结果很重要,所以加了这点补充。
有的人将ALL(多个列)的结果理解为:先对多个列做笛卡尔积,然后筛选其中原表中已经存在的列值组合。这种说法对不对?并没有答案,关键是DAX内部引擎的工作原理是不能用猜想或一两个正确的公式就可以证明的。
在ALL的变体函数中,都忽略了任何现有的筛选器来产生结果。
我们可以将这些ALL系列函数作为迭代函数的参数(如SUMX和FILTER),或者作为一个CALCULATE函数中的筛选器参数(稍后将会看到)。
如果想利用ALL函数调用某个表的大部分列,则可以使用ALLEXCEPT代替。要想从结果中排除哪些列,则需要一个表,后面再是该表中无须排除的列。
因此, ALLEXCEPT返回一个表,即该函数指定列表之外的其他列,其中包括这些其他列表的唯一值组合的列表。
实际上,ALLEXCEPT是一种编写DAX表达式的方法,它将自动包含在ALL非参数列的所有结果,以及之后数据模型中可能新增的新的任何额外的列。
例如,如果有一个带有五个列的产品表(ProductKey, Product Name, Brand, Class, Color--产品代码、产品名称、品牌、类别、颜色),下面的语法产生相同的结果:
ALL ( Product[Product Name],Product[Brand],Product[Class] ) // 除去的三列(ALL包含的三个列)
ALLEXCEPT ( Product, Product[ProductKey], Product[Color] ) // 保留的两列(相当于ALL包含的除去这两列之外的任何其他列)
然而,如果以后再再该表中添加两列 :[Unit Cost]和 [Unit Price]--产品单位成本和产品(单价),那么,ALL()的结果将忽略它们,与前面ALLEXCEPT的返回结果等效。
下面的查询返回一个表,该表除了[Code]和[Color]--产品代码和产品颜色列之外的所有列。
ALL (Product[Product Name],Product[Brand],Product[Class],
Product[Unit Cost],Product[Unit Price]) –-不包含[Code]和[Color]两列,其结果等效于下面的公式:
EVALUATE
ALLEXCEPT ( Product,
Product[ProductKey],Product[Color] )
后者的好处是,不受表中新增列表的影响。
图3-10是上述公式的结果。该结果与原表的行数相同,因为结果包含了ProductKey列,该列的每一行都是一个唯一值。结果为ALLEXCEPT参数指定列之外的其他列的组合,但可能会返回更少的行,因为返回的列值中删除了重复值。
前面的例子,是一个ALL函数的EVALUATE输出语句,它执行一个DAX表达式,而没有任何现有的筛选条件。也就是说:
ALL的计算语句,它在没有任何现有筛选器的情况下执行DAX表达式。
出于这个原因,我们最好是看一个在透视表中使用ALL函数计算表的行数的例子,其中使用不同的筛选器对每个度量的单元格值进行计算。
考虑以下度量:
[Products] : = COUNTROWS ( Product )
[All Products] := COUNTROWS ( ALL ( Product ) )
[All Brands] : = COUNTROWS ( ALL ( Product[Brand] ) )
可以在图3-11中看到每个度量的不同结果的示例。
对于每个产品类别(第一列中的每行),在[All Products]和[All Blands]列中都产生相同的数字,是因为度量公式中ALL()语句的计算忽略了透视表的每个单元格定义的筛选器。
译者:(1)[All Products] := COUNTROWS ( ALL ( Product ) )度量,仅计算All (Product)的结果:即Product表的全部唯一值行的计数(结果为2517)。
(2)[All Brands] := COUNTROWS ( ALL ( Product[Brand] ) )度量,仅计算ALL ( Product[Brand] )的结果:即Product表的[Brand]列的唯一值行的计数(结果为12,[Brand]列表中唯一值为12个)。
当调用关系的父表时,如果子表包含一个或多个与父表中的值不匹配的行,则会检索额外的空行。可以使用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度量之间的区别。这些度量的值中, ALL版本度量结果与对应的ALLNOBLANKROW版本度量结果都多一个。原因是Sales表中有一些行在Product表中并没有匹配的行,因此,实际上添加了一个额外的行,你可以在图3-12中看到(空白)行中的结果。
你应该注意到, [All Sizes ]和 [All NoBlank Sizes]度量总是返回相同的值(622),该度量查询Products[Size]列的数量。因为Products[Size]列已经包含了一个空白值的产品,所以这种情况下ALL 和 ALLNOBLANKROW函数返回相同的值。
在图3-13的示例中,有569个(blank)--空白计数的产品,加上额外的,其中包含对销售表中不匹配的产品的引用的一个空白产品,总共570个。所有这些行被分组在Products[Size]列的 (空白)值中用于计算。
当你需要编写一个遍历表,然后忽略关系中不匹配的值时,应该使用ALLNOBLANKROW。然而,通用都是使用ALL,而ALLNOBLANKROW却很少被使用。
了解VALUES 和 DISTINCT
在前一节中,已经看到, ALL(一个列表)结果返回该列所有唯一值行值的表。DAX函数提供了另外两个类似的函数:VALUES 和 DISTINCT,也返回一个列表的唯一值的列。
在没有任何其他筛选操作的情况下,如果在EVALUATE语句中使用VALUES 和 DISTINCT看起来是相同的。
但是,当将这两个函数分别放置在DAX度量中时,就可以观察到它们不同的行为。因为在一个数据透视表的每个单元格中,计算发生在当前不同的筛选子集中。
考虑下面的度量,计算Product[Brand]和Product[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个独特的brands--品牌、204个独特的sizes--尺寸。这时,VALUES 和 DISTINCT返回这些相同的结果。只有一个例外:在数据透视表之中的产品类别行的(blank)—(空白),其结果实际上包括增加的一行,以显示不匹配产品的销售金额的值。
在图3-14中,另一个差异是可见的。VALUES() 应用于Product[Brand]列返回的值比DISTINCT()应用于同一列的值要多。
但是,这不会发生在DISTINCT()应用到Product[Brand]列的值上,它返回的值与对应列上的值相同。因为DISTINCT()的Products[Size]列包含至少一个产品的空白值,因此添加的空白产品不会为[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 ) )
可以在图3-15中看到,在这种情况下,当没有筛选器时,VALUES的结果对应于ALL的行为,包括添加的空白行,以显示未匹配产品的销售额。在这种情况下,不能在一个表上使用DISTINCT。
如果需要删除一列中重复的行, DAX函数中没有单独这样的函数来删除重复的行(这时必须使用SUMMARIZE(),稍后将在第9章看到)。
然而, 当没有筛选器时,[Products]度量计算表中的行数,并忽略一个可能的空白行,该行为与ALLNOBLANKROW()相同。
图3-15的值,并考虑在产品表中添加空白行以获得不匹配的销售值。
译者:在学习完列表关系后,ALL()行为与VALUES()行为是有区别的:ALL()行为在于创建一个去掉关系的唯一值列表(不能用于筛选等),VALUES()的唯一值结果列表间保持原列表关系。
使用VALUES创建标量值
即使VALUES是一个表函数,也会经常使用它来计算标量值。在这一节中将学习到DAX的一个特殊特性。例如,可以在表达式中设置VALUES(),如下面的一个DAX--[ColorName]度量,用于显示颜色名称,以确保选择的为相同颜色的所有产品
[Color Name] := IF (COUNTROWS (
VALUES ( Product[Color] ) ) = 1,VALUES ( Product[Color] ))
译者:这是使用IF()测试一列中布尔值(真/假条件)的第一种方法。实际业务场景中经常用到,后面将有更优化的方法介绍。
可以在图3-16中看到结果。当[ColorName]度量结果包含空白时,意味着有两个或更多不同的颜色。
有趣的一点是,我们使用VALUES()作为标量值的结果,即使它返回一个表。这不是VALUES的特殊行为,但它代表DAX语言更一般的行为:
如果需要一个表表达式返回一行或一列,并需要它自动完成,则可以使用任何表表达式来将列表转换为标量值(即将列表转换为值列表)。
实践中,如果结果正好为一行或一列,可以使用任何表表达式作为一个标量值。而当表返回多行时,在执行时会出现这样一个错误:“一个多值的表是由其他方式提供的。”
因此,你应该保持该标量值的转换条件,使表表达式返回一个不同的多行结果 (你应该已经知道,当你写DAX公式时,该表表达式只返回一行)。前面的[Color Name]度量示例,使用COUNTROWS ( VALUES ( Product[Color] ) ) = 1:即COUNTROWS()检查Color--颜色列在Products--产品表里的选择是一个值。
一个更简单的完全相同的控制方法是使用HASONEVALUE,它执行同样的检查,如果列只有一个值,返回TRUE,否则返回FALSE。以下两个语法是等价的:
COUNTROWS ( VALUES ( ) ) = 1,HASONEVALUE ( )
你应该用HASONEVALUE代替COUNTROWS,这有两个原因:可读性更强,以及可以稍快一些。下面是一个更好的基于HASONEVALUE设置的度量:
[Color Name]。
[Color Name] := IF (HASONEVALUE ( Product[Color] ), VALUES ( Product[Color] ))
经常使用值作为标量表达式的原因是:在不同的筛选条件下,它返回单个列,或者返回单个行。在许多DAX模式中,使用VALUES()作为标量表达式是很常见的,并且在本书中反复出现。