第3章 使用基本表函数
3.1 表函数简介
3.2 EVALUATE语法简介
3.3 理解FILTER
3.4 ALL和ALLEXCEPT
3.5 了解VALUES,DISTINCT和空行
3.6 将表用作标量值
3.7 ALLSELECTED介绍
了解VALUES,DISTINCT和空行
在上一节中,您看到与一列一起使用的ALL返回具有所有唯一值的表。DAX提供了两个其他类似的函数,它们为列返回唯一值的列表:VALUES 和 DISTINCT。这两个函数看起来几乎完全相同,唯一的区别在于它们如何处理表中可能存在的空行。您将在本节稍后了解可选的空行。现在,让我们集中讨论这两个函数的作用。
ALL始终返回列的所有不同值。另一方面,VALUES仅返回不同的可见值。通过查看以下两个度量值,就可以了解两种行为之间的区别:
NumOfAllColors:= COUNTROWS(ALL('Product'[Color]))
NumOfColors:= COUNTROWS(VALUES('Product'[Color]))
NumOfAllColors 会计算 Product 表的所有颜色,而 NumOfColors 只会计算可见(通过报表中筛选显示)的颜色。图3-8中显示了按类别划分的这两个度量值的结果。
该报告为每个类别显示 NumOfColors 和 NumOfAllColors。
由于报告按类别划分,因此每个给定的类别都包含某些但不是全部颜色的产品。VALUES 返回当前筛选评估的列的不同值。如果我们在计算列或计算表中使用 VALUES 或 DISTINCT,因为没有活动的筛选,它们的行为与 ALL 相同。另一方面,在度量中使用时,这两个函数在考虑现有筛选的情况下计算结果,而ALL忽略任何筛选。
如前所述,这两个函数几乎相同。重要的是要理解为什么VALUES 和 DISTINCT 是同一行为的两个变体,区别在于他们对待表中存在空行的方式。首先,我们需要了解如果不显式创建空行,空行是如何出现在表中的。
事实是,如果关系无效,引擎会自动在关系一侧的任何表中创建空行。为了演示该行为,我们从“产品”表中删除了所有银色产品。由于最初有16种不同的颜色,而我们删除了一种颜色,因此可以预期颜色总数为15。相反,图3-9中的报告显示了一些意外情况:NumOfAllColors 仍为16,报告第一行显示了一个无类别名的新行。
由于产品位于与销售关系的“一”侧,因此对于 Sales 表中的每一行,在 Product 表中都有一个相关行。但是,由于我们故意删除了一种颜色的所有产品,因此 Sales 中有许多行不再与 Product 表具有有效的关系。请注意,我们没有从 Sales表中删除任何行;我们删除了一种旨在破坏关系的颜色。
为了确保在所有计算中都考虑到这些行,引擎会自动向 Product 表添加一行(该行所有列中都为空值)。Sales 中的所有孤立行都链接到此新引入的空行。
重要提示
尽管在Sales表中引用的多个不同产品在 Product 表中不再具有对应的 ProductKey,但是在 Product 表中仅添加了一个空行。
确实,在图3-9中,您可以看到第一行显示类别为空,并算作一种颜色。该数字来自表中类别为空、颜色为空,所有列中都为空的行。如果检查表,将不会看到该行,因为它是在加载数据模型期间创建的自动行。如果在某个时候该关系再次变得有效(如果要重新添加银色产品),则表中的空白行将消失。
DAX中的某些函数将空白行视为其结果的一部分,而其他函数则不这样做。具体来说,VALUES 将空白行视为有效行,然后将其返回。另一方面,DISTINCT 不返回它。您可以通过查看以下新指标来了解差异,该指标计算 DISTINCT 颜色而不是 VALUES:
NumOfDistinctColors := COUNTROWS ( DISTINCT ( 'Product'[Color] ) )
结果在图3-10中可见。
设计良好的模型不应显示任何无效关系。因此,如果您的模型是完美的,则这两个函数将始终返回相同的值。但是,在处理无效关系时,您需要了解此行为,因为否则可能会导致编写不正确的计算。例如,假设我们要计算每种产品的平均销售额。一种可能的解决方案是使用以下代码来计算总销售额并将其除以产品数量:
AvgSalesPerProduct :=
DIVIDE (
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
),
COUNTROWS (
VALUES ( 'Product'[Product Code] )
)
)
结果如图3-11所示。这显然是错误的,因为第一行是一个巨大的毫无意义的数字。
第一行显示的数字(类别为空)对应于所有银色产品的销售量,该产品不再存在于“产品”表中。此空行将所有银色的产品与不再在“产品”表中的产品相关联。DIVIDE的分子考虑了所有银色产品的销售。分母计数由VALUES返回的一个空行。因此,单个不存在的产品(空行)正在累计 Sales 中引用但在 Product 表中不可用的许多其他产品的销售,从而导致数量巨大。在这里,问题在于无效关系,而不是公式本身。实际上,无论我们创建什么公式,Sales 表中都有许多产品销售,而数据库没有这些信息。不过,查看同一计算的不同公式如何返回不同的结果将很有用。考虑另外两个变体:
AvgSalesPerDistinctProduct :=
DIVIDE (
SUMX ( Sales, Sales[Quantity] * Sales[Net Price] ),
COUNTROWS ( DISTINCT ( 'Product'[Product Code] ) )
)
AvgSalesPerDistinctKey :=
DIVIDE (
SUMX ( Sales, Sales[Quantity] * Sales[Net Price] ),
COUNTROWS ( VALUES ( Sales[ProductKey] ) )
)
在第一个变体中,我们使用 DISTINCT 代替 ALUES 。结果,COUNTROWS 返回一个空值,结果将为空。在第二个变体中,我们仍然使用 VALUES,但是这次我们计算的是 Sales[ProductKey] 的数量。请记住,有许多不同的 Sales [ProductKey] 值,它们都与同一空行相关。结果在图3-12中可见。
有趣的是,AvgSalesPerDistinctKey 是唯一正确的计算。由于我们按类别进行了细分,因此每个类别都有不同数量的无效产品密钥,所有无效产品密钥都折叠到单个空行。
但是,正确的方法应该是固定关系,以使产品的销售不会被孤立。黄金法则是在模型中没有任何无效关系。如果出于任何原因您的关系无效,那么您在处理空行以及对空行的存在如何影响计算时都必须格外谨慎。
最后,请注意,ALL函数始终返回空行(如果存在)。如果您需要从结果中删除空行,那么 ALLNOBLANKROW 是您要使用的函数。
多列 的VALUES
函数 VALUES 和 DISTINCT 仅接受单个列作为参数。没有对应的版本用于两列或更多列,不像 ALL 和 ALLNOBLANKROW。如果需要从不同的列中获得不同的、可见的值组合,则VALUE毫无帮助。在第12章的后面,您将学到:
VALUES ( 'Product'[Category], 'Product'[Subcategory] )
可以通过以下方式获得:
SUMMARIZE ( 'Product', 'Product'[Category], 'Product'[Subcategory] )
稍后,您将看到 VALUES 和 DISTINCT 通常用作迭代函数的参数。只要关系有效,它们的结果就没有差异。在这种情况下,当您遍历一列的值时,您需要将空白行视为有效行,以确保您遍历所有可能的值。根据经验,VALUES 应该是您的默认选择,只有在您要明确排除可能的空值的情况下才使用DISTINCT。在本书的后面,您还将学习如何利用 DISTINCT 而不是 VALUES 来避免循环依赖。我们将在第15章“高级关系处理”中进行介绍。
VALUES 和* DISTINCT* 也接受表作为参数。在这种情况下,它们表现出不同的行为:
- DISTINCT返回表的不同值,而不考虑空白行。因此,从结果中删除了重复的行。
- VALUES返回表中的所有行,而不删除重复项以及附加的空白行(如果存在)。在这种情况下,重复的行保持不变。