人生没有彩排
每一天都是现场直播

PowerBI中度量值所处的计值环境研究

前言

在前面介绍自动匹配原理的文章中,有提到Auto-Exists机制本身就存在Bug,那么本篇文章就来探讨一下这个问题,而这个问题其实就是对度量值所处的计值环境的研究。

这个Bug其实PowerBI官方也是清楚的,但由于海量的报表已经运行在这个Bug的基础之上,影响范围过大,因此也不好直接修复这个问题。所以在2024年10月,PowerBI更新了一个名为“值筛选行为”的功能,就是为了解决这个Bug,并通过提供手工切换的方式,来让用户自行决定是否要继续沿用这个Bug特性,还是采用另一种修复后的计值流程。

正所谓,本来没有路,走的人多了也就成了路。由于影响范围过大且Bug行为相对稳定可重复,这个Bug已经不再是Bug了,它演变成了一个特殊的特性。所以,还是很有必要对这个Bug特性进行研究和掌握的。但这毕竟是Bug,因此即使是在大量案例的基础下归纳总结出的计值流程也不一定正确,只能说是符合绝大多数场景,在某些场景下可能不适用。

本篇文章只介绍该Bug特性,关于通过更改“值筛选行为”进行修复后的正确计值流程将在后续文章中介绍。另外,本篇文章介绍的内容仅代表作者的自身理解,建议读者在掌握了计值上下文和自动匹配原理后再进行阅读。

Bug的证实

一直以来我都害怕是自己学艺不精,即使在面对各种DAX原理无法解释清楚、甚至自相矛盾的案例时,我都仅仅只是怀疑,而不敢光明正大的对所有人说,这就是一个盖棺定论的Bug。

直到后来,该Bug由DAX之父Jeffrey Wang亲自承认,才终于解决了我多年以来的困惑。以下是Jeffrey Wang的原话:

Jeffrey Wang SAYS : You’re sharp! You’ve uncovered one of the key reasons we introduced independent mode. There’s been a longstanding “bug”, a performance optimization, that effectively triggers independent mode under certain conditions.

As it turns out, this optimization happens in several common scenarios (including your repro), which likely explains why few users reported issues with coalesced behavior: they were often unknowingly experiencing independent mode.

We discovered this “bug” a long time ago but chose not to fix it, as doing so would have forced many reports into the less intuitive coalesced behavior, potentially breaking them.

以上出自Jeffrey Wang的博客评论,因网络问题我无法亲自留言,故由另一位小伙伴“一棵松 (VAC) ”代为留言提问。链接:Understanding the Impact of Value Filter Behavior

一切正常时的计值流程

由于Bug的影响,Auto-Exists机制在不同的场景中会有不同的行为,导致度量值所处的计值环境也随之不同,既有没触发Bug的正常行为,也有触发了Bug所带来的几种特殊行为。

下面先来看一下没触发Bug时的Auto-Exists机制,也就是PowerBI产品组最开始所希望达到的效果,如下图所示:

首先,报表环境的筛选器的交互方式都是相交的,无论是相同列或非相同列,一律是相交行为。另外在上图这个案例中,由于两个切片器以及矩阵的两个行标签都是来自同一个表,因此还会触发Auto-Exists机制。下面以矩阵第一行为例来简单介绍其计值流程,具体如下图所示:

简单来说就是因为Auto-Exists机制,使得产品代码筛选器也被过滤到只剩一个值,而这个值刚好是矩阵行标签的产品所对应的产品代码,所以Sales-ALL Product度量值虽然移除了产品名称筛选器,但由于产品代码筛选器还在,所以计算的其实还是同一个产品的销售额。

Bug的出现

上面的案例介绍的就是PowerBI产品组最开始所希望达到的效果,看起来好像没什么问题,计值流程也完美符合Auto-Exists的理论。但实际上,如果再移除多一个产品类别筛选器,就可以看到问题了。如下图所示,添加一个新的度量值去同时移除产品类别和产品名称的筛选器。

可以发现,新添加的度量值的结果竟然返回了切片器所选的四个产品代码的销售额之和。而这与DAX原理其实是不符合的,因为根据原理,其计值流程应该如下图所示的才对,即最终新添加的度量值也应该返回矩阵当前行的产品的销售额。

所以这里其实是出现了Bug,也就是Jeffrey Wang所说的:在很多常见场景中,Auto-Exists优化机制并没有按预期设计的行为去执行,反而触发了“值筛选行为”为“独立”模式下的行为。

There’s been a longstanding “bug”, a performance optimization, that effectively triggers independent mode under certain conditions.

As it turns out, this optimization happens in several common scenarios (including your repro), which likely explains why few users reported issues with coalesced behavior: they were often unknowingly experiencing independent mode.

也就是说,Bug的出现才是常态,而完全正常的、不含Bug的Auto-Exists机制的出现场景反而是少数,不得不说这很幽默。

对Bug特性的研究

下面就来介绍一下我对该Bug特性的研究,给大家做一个参考吧。根据大量案例下的研究,该Bug特性的计值流程可以分成三部分,其中一个就是上面介绍的完全正常的Auto-Exists机制的行为,另外两个就是根据报表环境中是否出现了固化筛选器来划分。

而这三种场景下的计值流程都是不一致的,所以也没法总结归纳出一个恰当的”理论“来诠释它,毕竟这是个Bug,行为有点迷是可以理解的。所以只能是分类处理,死记硬背这三个场景下各自的计值流程了。


1、没触发Bug,完全正常的Auto-Exists机制的场景

该场景下的计值流程就如上面案例介绍的一样,所有报表环境中存在的初始筛选器都先进行相交,然后如果存在同一个表上的两个或以上的筛选器,那么再触发Auto-Exists机制,将那些无效的筛选器组合给过滤掉,过滤后剩下的筛选器组合就是提供给度量值的计值环境了。

而该正常场景反而碰到的不多,它属于极少数情况,该场景的触发特点有以下几点:

  • 存在两个及以上的切片器,并且切片器的字段之间含有类别与子类别这种层级关系
  • 视觉对象存在两个或以上的轴字段,并且轴字段间也含有类别与子类别这种层级关系
  • 切片器的字段与视觉对象的轴字段之间有重叠,即出现相同字段,且重叠的必须是类别颗粒度层级的字段
  • 以上出现的所有字段都来自同一个表

当满足以上要求时,如果度量值中没有移除筛选器,或移除的筛选器只是子类别颗粒度层级的字段上的筛选器时,那么其计值流程就会遵循正常的行为,即不会出现Bug。但如果尝试在度量值里同时移除类别和子类别字段上的筛选器时,那么反而会触发Bug,此时触发Bug后的计值流程可以参考下面介绍的两个场景中的计值流程。

由于该完全正常的场景属于极少数情况,所以不妨倒转天罡,把正常的行为当作Bug,而把真正的Bug特性当作正常的行为,这样可能会更好理解,也更容易记住。


2、报表环境中只存在标准筛选器时的场景

为便于描述,将报表环境分为两部分:一部分是视觉对象的轴字段提供的筛选器,比如矩阵的行列标签提供的筛选器。另一部分则是除了视觉对象的轴字段提供的筛选器之外的其他初始筛选器,统一称之为切片器环境。

那么该场景中的计值流程如下:

  • 首先,切片器环境中的所有初始筛选器之间先进行相交
  • 然后,如果切片器环境中存在同一个表上的两个筛选器字段,那么会触发Auto-Exists,将过滤掉那些无效的筛选器组合
  • 最后,切片器环境最后得到的有效筛选器组合将与视觉对象的轴字段筛选器进行相交,相交后得到的筛选器组合即为度量值的计值环境。注意:在该步骤中,即使视觉对象的轴字段与切片器环境中的字段属于同一个表,那也不会触发Auto-Exists,因此那些无效的筛选器组合不会被过滤

为方便理解,下面来看一个例子。首先往产品表里添加一个计算列,复制一个产品类别字段,并命名为产品类别2,如下图所示:

然后用产品类别2字段去替换本篇文章开始时的那个案例中的产品类别字段,结果如下图所示:

可以发现,同样都是“产品类别”字段,但两个矩阵中同一个度量值得到的结果竟然不一致。这是因为更改为产品类别2字段后,其与切片器环境中的产品类别字段就不再重叠,所以正常触发了Bug特性,而不再遵循第一种完全正常的Auto-Exists机制下的场景的计值流程。

而此时报表环境中只存在标准筛选器,并不存在固化筛选器,所以更改为产品类别2字段后,它遵循的其实就是本小节所介绍的计值流程。同样的,以矩阵第一行为例来简单介绍其计值流程,具体如下图所示:

该场景下的Bug特性计值流程与完全正常的计值流程相比,差别主要在于Auto-Exists只发生在切片器环境,因此最后提供给度量值的计值环境中其实是含有那些无效组合的,所以当移除了一些筛选器后,原本筛选不到数据的无效组合可能就能够筛选得到数据了。

由于不会触发Bug的完全正常的场景是极少数,而且报表环境中一般也不会存在固化筛选器,所以这个其实就是最常见的场景了,将上面的计值流程搞清楚并记住,基本就掌握了80%的Bug特性了。


3、报表环境中存在固化筛选器时的场景

在报表环境中添加固化筛选器的方式有两种,其中最常见的一种就是通过层级切片器,另一种则比较罕见,那就是给存在两个或以上轴字段的视觉对象添加度量值视觉筛选器。

同样的,为便于描述,将报表环境分为两部分:一部分是视觉对象的轴字段提供的筛选器,比如矩阵的行列标签提供的筛选器。另一部分则是除了视觉对象的轴字段提供的筛选器之外的其他初始筛选器,统一称之为切片器环境。

那么该场景中的计值流程如下:

  • 首先,切片器环境中的所有初始筛选器之间先进行相交
  • 然后,如果切片器环境中存在同一个表上的两个筛选器字段,那么会触发Auto-Exists,将过滤掉那些无效的筛选器组合
  • 最后,切片器环境最后得到的有效筛选器组合将与视觉对象的轴字段筛选器进行交互,但此时分两种情况:
    • 切片器环境中的固化筛选器 若与 视觉对象的轴字段筛选器 存在相同列,则 切片器环境最后得到的有效筛选器组合 与 视觉对象的轴字段筛选器 相交并再次执行Auto-Exists,最后得到的就是度量值的计值环境
    • 切片器环境中的固化筛选器 若与 视觉对象的轴字段筛选器 不存在相同列,则 切片器环境最后得到的有效筛选器组合 与 视觉对象的轴字段筛选器 只相交但不执行Auto-Exists,最后得到的就是度量值的计值环境
    • 注意:在该步骤中,是否再次执行Auto-Exists只取决于切片器环境中的固化筛选器与视觉对象的轴字段筛选器之间是否存在相同列。如果两者不存在相同列,那么即使视觉对象的轴字段与切片器环境中的字段属于同一个表,那也不会触发Auto-Exists

为方便理解,下面来看一个例子,如下图所示:

在该案例中,首先通过添加层级切片器的方式来添加固化筛选器,然后注意观察两个矩阵的行标签,一个是产品类别和产品名称的组合,另一个则是产品类别2和产品名称,虽然都是“产品类别”,但是两个矩阵的度量值所得到的结果却是不一致的。

由于该案例中出现了固化筛选器,所以遵循本小节所介绍的计值流程。下面先来介绍标号为1的矩阵的计值流程,同样的以第一行为例,其计值流程如下图所示:

由于标号为1的矩阵的行标签与报表环境中的固化筛选器无相同列,所以不会再次执行Auto-Exists,因此最后提供给度量值的计值环境中其实是含有那些无效组合的,所以当移除了一些筛选器后,原本筛选不到数据的无效组合可能就能够筛选得到数据了。

下面再来看一下标号为2的矩阵的计值流程,同样的以第一行为例,其计值流程如下图所示:

由于标号为2的矩阵的行标签与报表环境中的固化筛选器存在相同列,所以会再次执行Auto-Exists,因此得到的结果也有所不同。

通过这个案例中两个矩阵的对比,可以看到,当报表环境中存在固化筛选器时的计值流程是比较复杂的,它会根据视觉对象的轴字段筛选器与固化筛选器中是否存在相同列来决定是否再次执行Auto-Exists,因此也会导致后续的计值流程出现变化。

总的来说,报表环境中出现固化筛选器的概率是很低的,并且常见于新手,因为他们经常会使用层级切片器,而不是把层级切片器拆分成多个标准筛选器。所以,只要能够遵守良好的开发实践规范,那么这种存在固化筛选器的场景其实也不容易遇上。

总结

一直以来,各种PowerBI或DAX的资料基本都没有介绍过度量值所处的计值环境,而本篇文章对其进行了研究,并给出了自身的理解心得,勉强算是补充了这块知识点。

无论对计值上下文和筛选器的交互掌握得有多么娴熟,如果最开始时度量值所处的计值环境就理解出错了,那么后续的筛选器交互再怎么正确,也不可能得到想要的结果,正所谓差之毫厘,谬之千里。

最后再次提醒,本篇文章介绍的内容仅代表作者的自身理解,有任何问题欢迎留言讨论!

未经允许不得转载:夕枫 » PowerBI中度量值所处的计值环境研究