在 Power BI 的数据分析旅程中,掌握 DAX(数据分析表达式)语言是我们通往高级分析师的必经之路。而在众多的 DAX 函数中,VALUES 函数可以说是最基础但又最容易被忽视的“瑞士军刀”。你是否曾经在处理数据模型时,遇到过莫名其妙的空白行?或者在需要计算特定上下文中的唯一值时感到困惑?在这篇文章中,我们将结合 2026 年最新的数据工程理念,深入探讨 VALUES 函数的方方面面。不仅涵盖其基本语法和在不同数据模型关系下的行为差异,还将通过实战案例展示它在处理参照完整性问题和复杂度量值时的强大威力,并分享我们如何在 AI 辅助开发模式下高效使用这一工具。让我们一起揭开这个函数的神秘面纱,看看它是如何帮助我们构建更健壮、更智能的数据模型的。
VALUES 函数的核心概念与现代视角
首先,我们需要从根本上理解 VALUES 函数的定义。从表面上看,它的语法非常简单:只需传入一个表名或列名。然而,它在不同输入下的行为却有着微妙的差别,这正是我们需要重点掌握的地方。在 2026 年的“数据编织”架构下,数据的来源更加多样(从 SQL 数据库到 Fabric OneLake),理解 VALUES 在不同语义模型上下文中的行为变得尤为重要。
#### 语法解析
> VALUES(TableName or ColumnName)
当我们向 VALUES 函数传递一个列名时,它返回一个包含该列所有不重复值的单列表。这与 DISTINCT 函数非常相似,但在处理数据模型中的空白行时,VALUES 表现出了其独特的特性。
反之,当我们向 VALUES 函数传递一个表名作为输入时,它返回包含所有列的完整表。请注意,在这种情况下,它不会删除重复的行,而是保留原始表中的所有记录,包括重复行和可能存在的空白行。这就像是对表进行了一次“当前上下文的快照”,这对于调试至关重要。
2026 最佳实践:AI 辅助开发与 VALUES 函数
在我们目前的高级开发工作流中,编写 DAX 已经不再是单打独斗。我们利用 Vibe Coding(氛围编程) 的理念,让 AI(如 GitHub Copilot 或专门针对 DAX 的 Fabric AI 模型)成为我们的结对编程伙伴。VALUES 函数由于其上下文敏感性,往往是 AI 生成代码时容易产生歧义的地方。AI 有时会混淆 VALUES 和 DISTINCT,或者忽略“空白行”对业务逻辑的影响。
#### 示例:如何向 AI 提问以生成正确的 VALUES 代码
假设你正在使用 Cursor 或 Windsurf 这样的现代 IDE 编写 DAX。模糊的指令会导致错误的代码。我们需要像对待初级分析师一样,精确地指导 AI。
❌ 错误的提示词:
“帮我计算每个产品的销售额。”
✅ 2026 风格的精确提示词:
“在 INLINECODE26b3e3d0 和 INLINECODE12f8188a 存在一对多关系的情况下,编写一个度量值。使用 INLINECODE91837767 函数来迭代 INLINECODE555750b4,同时确保保留由于参照完整性缺失而产生的空白行,以便我们可以监控孤儿数据。请使用 CALCULATE 处理上下文转换。”
这种提示方式明确了我们使用 VALUES 的意图:不仅是计算,更是为了数据可观测性。通过这种方式,我们生成的代码不仅能运行,还能作为数据质量监控的一部分。
场景一:无直接关系的情况(原始数据探索)
首先,假设我们在 Power BI 中加载了这两张表,但没有在它们之间建立任何关系连线。在这种情况下,VALUES 函数的表现非常直观,完全作用于输入对象本身,不受外部数据上下文的影响。这在数据清洗的初步阶段非常有用。
#### 示例 1:提取唯一值列表(用于构建切片器)
假设我们只想知道 product_table 中到底有哪些不重复的产品名称,以便构建一个动态的筛选器。我们可以创建一个计算表或度量值,将列名传递给 VALUES 函数。
// 提取产品表中的唯一产品名称
// 在 DAX Studio 中运行此查询以验证数据质量
EVALUATE
VALUES(product_table[product_name])
// 注意:如果数据中包含 NULL,VALUES 会将其包含在内
如果你在 DAX Studio 或 Power BI 的表格视图中查看输出,你会看到一个干净利落的单列列表,其中列出了所有出现过的产品名称,重复项已被自动移除。这对于构建下拉菜单切片器或筛选器非常有用。
#### 示例 2:保留完整表结构(调试视图)
现在,让我们尝试将整个表作为参数传入。这在我们要验证数据加载是否完整时非常关键。
// 返回产品表的完整副本,包含重复行
// 这在排查 ETL 过程中的数据丢失问题时非常有用
EVALUATE
VALUES(product_table)
// 即使 product_table 本身没有重复行,VALUES(Tab) 也是一种
// 确保获取“当前上下文”下所有行的安全写法
场景二:存在关系的情况与“空白行”之谜(深度剖析)
接下来,我们进入最有趣的部分。让我们在 salestable 和 producttable 之间建立一对多的关系(通常是将 salestable 中的外键关联到 producttable 的主键)。在 Power BI 数据模型中,这种关系通常设置为单向筛选,即 producttable 筛选 salestable。
但是,现实世界的数据往往是不完美的。在 2026 年,虽然自动化 ETL 管道已经非常成熟,但实时数据流中的延迟或格式错误依然会导致参照完整性违规。这就是 VALUES 函数大显身手的时候。
#### 实际案例:数据不匹配导致空白行
让我们看一个具体的问题:假设 salestable 中有一笔销售记录的产品 ID 是“Coffeebeenpowder”,但在 producttable 中,由于数据录入错误或更新滞后,根本没有这个产品 ID 的记录。
为什么 VALUES 函数很重要?
如果你使用 DISTINCT 函数或普通的筛选逻辑,Power BI 往往会尝试“修复”这种不匹配,但这可能会掩盖数据质量问题。而 VALUES 函数不同,它严格遵守数据模型的关系。如果相关表中存在不匹配的数据,VALUES 函数会显式地返回一个空白行来代表那些“找不到父级”的孤儿记录。
#### 示例 3:检测空行(代码实战)
让我们再次运行查询 product_name 的代码,但这次是在建立了关系之后:
// 在有关系的模型中查询产品名称
// 运行此代码,你将看到底部的神秘空白行
EVALUATE
VALUES(product_table[product_name])
你会惊讶地发现: 结果列表不仅包含所有有效的产品名称,底部还会多出一行空值。在 Power BI 的视觉对象中,这通常显示为“(Blank)”。这就是 VALUES 函数在告诉我们:“嘿,salestable 里有一些记录,我在 producttable 里找不到对应的产品。”
高阶应用:虚拟表与迭代计算
VALUES 函数不仅仅是一个数据查看工具,它在编写复杂的度量值时更是不可或缺的。它允许我们将特定的列值转换为一个临时的上下文环境,从而在迭代计算中发挥作用。这在处理“动态 Top N”或“同比/环比”分析时至关重要。
#### 示例 4:计算加权平均或动态排名
让我们创建一个更有意义的度量值:“每个产品的销售贡献率”。如果我们直接使用简单的聚合,很难在复杂的筛选上下文中(比如按地区筛选后)准确计算。VALUES 提供了一个基于当前上下文的精确粒度。
// 计算每个产品在当前筛选上下文中的销售贡献率
// 这个度量值展示了 VALUES 如何提供计算粒度
Contribution Ratio =
VAR CurrentProductSales =
SUM(sales_table[sales])
VAR TotalContextSales =
CALCULATE(
SUM(sales_table[sales]),
ALLSELECTED() // 清除具体行的筛选,保留外部筛选
)
VAR Result =
DIVIDE(
CurrentProductSales,
TotalContextSales
)
RETURN
IF(
ISBLANK(Result),
0, // 处理除零或空值情况
Result
)
#### 示例 5:VALUES 与 CALCULATETABLE 的组合(生产级代码)
在我们的实际项目中,经常需要动态生成一个“已售产品列表”。VALUES 是获取这个列表最高效的方式之一。
// 获取当前上下文中实际产生销售的产品列表
// 这在构建“智能数据警报”时非常有用
Sold Products List =
CALCULATETABLE(
VALUES(product_table[product_name]),
sales_table[sales] > 0, // 仅筛选有销售额的产品
KEEPFILTERS() // 保留现有的外部筛选条件
)
代码解析:
- VALUES(producttable[productname]):这部分代码在当前筛选上下文中,提取了所有可见的不重复产品名称。
- CALCULATETABLE(…):我们将这个单列表作为输入,应用了额外的筛选逻辑(Sales > 0)。这比直接使用 FILTER 函数过滤整个表性能更好,因为 VALUES 先进行了压缩。
- KEEPFILTERS():这是企业级开发中的最佳实践,确保我们的计算不会意外覆盖用户在报表页面上设置的切片器筛选。
现代数据架构中的 VALUES 与 DirectQuery
在 2026 年,随着 Microsoft Fabric 的普及,越来越多的企业采用了 DirectQuery 或 DirectLake 模式来连接超大型的数据仓库。在这种架构下,VALUES 函数的角色发生了微妙但重要的变化。我们不再仅仅是在内存中的 VertiPaq 引擎上操作,VALUES 的逻辑转换可能会直接下推到源数据库(如 Snowflake 或 Azure Synapse)。
#### 2026 性能优化建议:下推与物化
在 DirectLake 模式下,使用 VALUES 函数时,我们需要特别关注 Fusion 引擎 的行为。当你在计算列中使用 VALUES 时,Power BI 可能会强制触发缓存物化,这在亿级数据表上是致命的性能杀手。
实战建议: 尽可能在度量值中使用 VALUES,而不是计算列。度量值的计算通常是延迟执行的,能够更好地利用 Fabric 的聚合下推能力。
#### 代码示例:安全的虚拟列计算
假设我们需要计算每个客户的“首次购买日期”,在 DirectQuery 环境下,我们不能简单地创建一个计算列。我们需要编写一个高效的度量值:
// 计算“当前筛选上下文”下的最早销售日期
// 这种写法避免了在源表上创建昂贵的计算列
Min Sales Date =
CALCULATE(
MIN(sales_table[date]),
KEEPFILTERS(VALUES(sales_table[customer_id]))
)
// 使用 VALUES 确保只计算当前有效的客户列表
// 即使某些客户在 Sales 表中没有记录,也能保持上下文准确
进阶工程化:VALUES 与 ALLEXCEPT 的性能博弈
在构建复杂的动态报表时,我们经常需要清除部分筛选上下文。很多开发者习惯使用 INLINECODE022e05ad,但在 2026 年的大规模语义模型中,我们更推荐使用 INLINECODE769eab6a 配合 INLINECODEf458e4cd 的组合模式。为什么呢?因为 INLINECODEf5565a1f 会保留表中所有的列,除了指定的列,这在拥有 50+ 列的宽表中会造成不必要的存储引擎开销。
#### 示例 6:高效计算百分比总和
我们需要计算某个特定类别的销售额占该类别的百分比,同时忽略其他维度的筛选。
// 性能优化的百分比计算
// 相比 ALLEXCEPT,这种方式明确指定了需要保留的上下文列
Category Sales % =
VAR CurrentCategory =
SUM(sales_table[sales])
VAR TotalCategory =
CALCULATE(
SUM(sales_table[sales]),
// 使用 VALUES 创建一个只包含特定列的虚拟表上下文
// 比 ALLEXCEPT 更轻量,更易于 Fabric 引擎优化
ALLEXCEPT(sales_table),
VALUES(product_table[category])
)
RETURN
DIVIDE(CurrentCategory, TotalCategory)
在这个例子中,我们利用 VALUES 明确定义了计算的范围。这种“显式上下文”的写法在处理拥有数百个维度的 Fabric 模型时,能显著减少查询计划中的复杂度。
赋能 AI:VALUES 函数在 Copilot 语义层中的角色
在 2026 年的先进开发理念中,我们不仅自己写代码,还在构建“语义模型”。Copilot 等 AI 工具会读取我们的 DAX 代码来理解数据结构。如果我们在代码中滥用 DISTINCT 而忽略了 VALUES,AI 可能会误判数据模型中不存在参照完整性问题,从而导致 AI 生成的报表或问答出现偏差。
#### 语义建模最佳实践
为了让 AI 更好地理解我们的模型,建议在处理“可能存在空值的维度”时,统一使用 VALUES。这相当于在给 AI 的提示词中隐式地告诉它:“注意,这里可能会出现孤儿数据。”
深入性能优化与工程化考量
随着数据量的爆炸式增长,我们不能再仅仅关注“代码能否运行”,必须关注“代码运行时的资源消耗”。VALUES 函数虽然看似简单,但在大模型中的滥用往往是导致报表卡顿的元凶。
#### 性能对比:VALUES vs DISTINCT vs ALL
处理空白行 (BLANK)
主要用途
:—
:—
保留 (用于检测孤儿数据)
上下文转换、容错计算
移除 (返回纯净值)
仅仅需要去重列表时
移除 (忽略所有筛选)
计算比率、忽略切片器#### 最佳实践与常见陷阱
在我们的探索过程中,有一些经验教训值得分享:
- DISTINCT vs VALUES:这是 DAX 学习者最常见的困惑。简单来说,DISTINCT 总是会删除空行,而 VALUES 会保留由于数据模型关系产生的空行。当你需要严格的数据质量检查时,请使用 VALUES;当你只需要纯净的唯一值列表用于 UI 展示时,DISTINCT 可能更合适,因为它在某些情况下可以减少 Edge Case 的处理逻辑。
- 上下文转换:在度量值中使用 VALUES 时,务必注意当前的筛选上下文。VALUES 生成的表是基于当前上下文的。如果当前上下文中没有数据,VALUES 返回的也就是空表。在调试复杂 DAX 时,我们建议使用 DAX Studio 的 Server Timings 面板,检查 VALUES 是否触发了意外的 Storage Engine 查询。
- 避免在行级上下文中过度使用:虽然 VALUES 函数本身非常高效,但在处理超大模型(数亿行数据)时,如果在 SUMX 或 FILTER 的行上下文中频繁调用 VALUES,可能会导致 Materialization(物化)开销激增。建议尽量使用变量 (VAR) 先将 VALUES 的结果缓存起来,再进行迭代计算。
2026 前沿视角:VALUES 在多模态与边缘计算中的演进
展望未来,随着边缘计算在 Power BI Embedded 和 Fabric 租户中的引入,VALUES 函数的职责正在扩展。在多模态数据分析场景中(例如结合文本、图像标签和结构化销售数据),我们经常需要 VALUES 来对齐不同模态的“键值”。
场景: 假设我们正在分析客户的情绪数据(非结构化文本)与销售数据的关联。我们需要使用 VALUES 来获取唯一的客户 ID 列表,以确保情绪评分模型能够正确地聚合到结构化的销售数据上。
在这种混合负载下,VALUES 的确定性(总是保留 BLANK)成为了我们构建可信 AI 数据管道的基石。如果使用 DISTINCT,我们可能会在数据同步过程中丢失那些尚未被情绪分析模型标记的客户,导致报表数据与实际业务脱节。
总结:迈向智能数据分析
在这篇文章中,我们一起深入探讨了 Power BI 中 VALUES 函数的多重面貌。从基本的单列去重,到处理复杂的表关系,再到作为高级度量值的构建基石,VALUES 函数展现出了其在 DAX 语言中的核心地位。
我们特别强调了它在检测参照完整性违规(即那个神秘的空白行)方面的独特能力。这不仅仅是一个语法特性,更是我们作为数据分析师保证数据准确性的有力武器。在 2026 年,随着 AI 逐渐接管底层代码的编写,深刻理解像 VALUES 这样的基础函数行为逻辑,将使你能够更有效地指导 AI,构建出既高效又健壮的数据模型。
下一步,建议你尝试在自己的 Power BI 项目中应用这些技巧,结合 AI 辅助工具(如 Copilot)编写一些基于 VALUES 的复杂度量值,并检查一下你的数据模型中是否也隐藏着那些“空白行”。通过不断的实践,你会发现 DAX 不仅仅是公式,更是解决数据问题的艺术。