大数跨境
0
0

DAX 中 DISTINCT 与 VALUES 函数的避坑指南

DAX 中 DISTINCT 与 VALUES 函数的避坑指南 PowerPivot工坊
2025-09-05
0

图片


本文翻译自Marco Russo & Alberto Ferrari的文章—《Choosing between DISTINCT and VALUES in DAX 来源:SQLBI   何时应在 DAX 中选择 DISTINCT 而非 VALUES?本文将介绍如何编写健壮的度量值,使其在数据质量不佳和模型变更时仍能保持正确性。


初学 DAX 建模时,DISTINCT 和 VALUES 函数常看似可互换:两者都会返回当前筛选上下文中列的唯一值列表。在整洁的开发模型中,它们的行为完全一致,因此很容易随意选择其一——更糟糕的是,不经思考地混用两者。

然而这两个函数并非完全相同。细微的差异在可能包含无效关系或脏数据的生产模型中至关重要。本文将详解:

  • 每个函数的技术行为

  • 为何一个额外的"空白行"会造成结果差异

  • 明确的使用场景选择原则

  • 特殊情况:表迭代、多列操作与统计度量

通过本文您将理解:为何 VALUES 应作为默认选择,以及何时需改用 DISTINCT。文中将包含非优化度量值示例,以展示使用 VALUES 或 DISTINCT 导致结果差异的简单场景。需要注意的是,这些差异同样可能出现在优化代码中,但实际用例往往过于复杂难以在简单文章中演示。因此,请将这些示例仅视为教学案例,而非编写特定计算的最佳实践。


DISTINCT 与 VALUES 的核心差异解析

DISTINCT 与 VALUES 是两个具有相似签名的函数。这两个函数都只接受单个参数(通常为列引用):
DISTINCT(<ColumnNameOrTableExpr>)VALUES(<TableNameOrColumnName>)

从函数签名可见,VALUES 还可接受表引用,而 DISTINCT 能接收更通用的表表达式。在本文起始部分,我们重点讨论以列引用作为参数时这两个函数的行为。

二者的行为看似相同,因为它们都返回当前筛选上下文中可见的列唯一值列表。唯一区别在于:当同时满足以下所有条件时,VALUES 可能会多返回一个空白行:

  1. 该列属于常规关系中的"一"端表

  2. 该常规关系处于无效状态

  3. 筛选上下文未过滤掉无效关系产生的附加空白行

  4. 列中的空白值未被筛选上下文过滤排除

需要特别说明的是:如果从一对多关系中"一"端的列获取值,则该列不允许出现空白值——这是用于标识无效关系中特殊空白行的保留值。

常规关系指一对多(或一对一)关联,其中"一"端列是主键。当"多"端值在"一"端不存在时,关系将失效,DAX 引擎会自动向"一"端表添加空白行。所有"多"端未匹配行都会关联到该空白行。而在受限关系(多对多、双向关系等)中永远不会添加该行,因此 DISTINCT 和 VALUES 的行为完全一致。


使用 VALUES 函数进行列迭代

当使用迭代器(如 SUMX、AVERAGEX、FILTER 等)对列进行迭代时,通常需要包含模型中所有参与总计的数据点。若使用 DISTINCT,系统会跳过空白行,导致关系另一侧所有未匹配的行被忽略;而使用 VALUES 时,由于迭代过程会同时处理空白行,因此仍能涵盖这些未匹配的行。
销售表中的度量值
Sales Adjusted (incorrect)=SUMX(    DISTINCT(Customer[Continent]),    [Sales Amount]*IF(Customer[Continent]=="Europe",.99,1))

在数据模型中,若Sales表中存在CustomerKey值在Customer表中不存在的情况(包括Sales[CustomerKey]为空白值的情形),报表将并排显示错误的"调整后销售额(错误)"度量值(报告中命名为"S.A.(错误)")与正确值的对比结果。

错误金额在按客户[国家]字段切片显示的矩阵中很容易解释,因为两个总计之间的差异可直接归因于初始的空白国家字段。然而,当使用客户表之外的其他表列(如按产品[品牌]字段切片矩阵)进行度量值切片时,理解这种差异会变得异常复杂。在后一种情况下,由于每行都包含未在报告中直接"显现"的未知客户,两套度量值的所有行都会呈现不同金额。

以下是正确计算销售额调整值的定义:

销售表中的度量值
Sales Adjusted =SUMX(    VALUES(Customer[Continent]),    [Sales Amount]*IF(Customer[Continent]=="Europe",.99,1))

通过使用 VALUES 而非 DISTINCT,当客户表存在无效关系时,系统会自动包含附加的空白行。

当 DISTINCT 成为正确选择时


在某些计算中,空白行会导致误导性结果。典型案例如 MINX、MAXX、AVERAGEX 等统计迭代器及百分位数计算——若将所有未知客户归入同一个庞大的"未知"客户类别,会造成统计偏差。此时完全跳过这些值可能是更佳选择。这类情况通常属于例外而非通则,因此我们建议默认使用 VALUES 函数,除非能明确解释公式中使用 DISTINCT 的合理性。

例如,以下是对客户[城市]平均销售额的错误计算:

销售表中的度量值
City Average (incorrect)=AVERAGEX(    VALUES(Customer[City]),    [Sales Amount])

同样地,当通过客户属性(如客户[国家])对数据进行切片时,矩阵会显示出一个对应所有"未知"客户聚合值的空白行。这会导致矩阵总计值出现偏大的错误金额,而其他报表(如按产品[品牌]切片度量值的矩阵)则会在每个单元格中都出现错误金额。

城市平均值的正确公式应使用 DISTINCT 而非 VALUES 进行城市迭代:
销售表中的度量值
City Average =AVERAGEX(    DISTINCT(Customer[City]),    [Sales Amount])

结论

VALUES 与 DISTINCT 的差异仅在于一个空白行,但该空白行可能导致模型遭遇真实数据时出现收入流失、客户丢失及隐性计算错误。将 VALUES 作为默认迭代驱动函数(仅在确有合理理由时切换为 DISTINCT),能为度量值构建健壮性,并为后续节省大量调试时间。


END

图片

长按下方二维码关注“Power Pivot工坊”获取更多微软Power BI、PowerPivot相关文章、资讯,欢迎小伙伴儿们转发分享~ 

【声明】内容源于网络
0
0
PowerPivot工坊
提供Power Pivot,Power Query等Power BI技术相关文章,培训咨询等服务。
内容 648
粉丝 0
PowerPivot工坊 提供Power Pivot,Power Query等Power BI技术相关文章,培训咨询等服务。
总阅读182
粉丝0
内容648