回忆是一座桥
却是通往寂寞的牢

19、扩展表原理

  本篇文章将介绍DAX中最重要的一个原理,那就是扩展表原理。如果说数据模型是DAX的灵魂,那么扩展表原理就是数据模型的基石。扩展表原理非常重要,但这并不是体现在它的功能性作用上,它的真正意义是能够让我们彻底理解筛选器在表间关系上的传递过程,从而真正意义上地彻底掌握数据模型和筛选上下文。


  先给出我对扩展表原理的理解:某个基础表的扩展表,包含了基础表自身的列,以及该基础表的上级表的所有列,但其扩展表上不属于基础表自身列的其他列并不可见,也不能直接引用。当某个基础表被用作筛选器参数或者该基础表的行上下文被转换成筛选上下文后,其扩展表上不属于基础表自身列的其他列也会设置筛选器。此外,来自某列的筛选器只筛选其自身列,若有表的扩展表包含该列,那么这个扩展表也会被交叉筛选。故,某列上的筛选器可以筛选包含此列的所有扩展表。


  下面我来详细阐述一下上面这段话的意思。

  首先,我们要搞清楚直接筛选与交叉筛选的含义与区别。所谓的直接筛选是针对某个列或某个表而言的,当针对列时,如果这个列被筛选了,且出现筛选的原因是其自身列上的筛选器导致的,那么这就是直接筛选。如果这个列被筛选了,但出现筛选的原因是由于相同表或相关表上的其它列被筛选所导致的,那么这就是交叉筛选;当针对表时,如果这个表被筛选了,且出现筛选的原因是该表上任意一个列上的筛选器导致的,那么这就是直接筛选。如果这个表被筛选了,但出现筛选的原因是由于相关表上的其他列被筛选所导致的,那么这就是交叉筛选。

  其次,我们要清楚一个事实,那就是表是由列组成的,当列被筛选的时候表也会跟着被筛选。如下图所示,当产品类别列被筛选了,整个产品表都会跟着被筛选。

  如上图所示,产品表中的产品类别列上被设置了一个筛选器,那么这个筛选器就只会筛选产品表中的产品类别列上的值,它并不会去筛选其它非相同列的值,但由于列是表不可分割的一部分,一个表中的某个列被筛选,那么该表的其它列也会跟着被交叉筛选。这是很显而易见的事情,但只有明确地知道这一点,才能真正理解扩展表原理。

  最后,我们还要清楚地知道某张基础表的扩展表包含了哪些列。这一点我在上文中也已经明确指出了:某个基础表的扩展表,包含了基础表自身的列,以及该基础表的上级表的所有列,但其扩展表上不属于基础表自身列的其他列并不可见,也不能直接引用。因此,我们只需要确定某张表的上级表有哪些,然后就可以确定其扩展表上拥有的列了,如果对上级表的概念不太清晰,建议去看一下我写的第七篇关于数据模型的文章。

  下面举个例子,对于以下数据模型而言,订单表的上级表只有产品表,因此订单表的扩展表既包含了订单表的自身列,还包含了产品表的所有列,如下图所示:

  上图中,黑色列名的列即为订单表的自身列,蓝色列名的列即为订单表的上级表的列,上面所有列加起来形成的表就是订单表的扩展表。其中,扩展表上不属于自身列的其它列是不可见的,也不能直接引用。

  相关的铺垫已经做完了,那么现在就来解释一下为什么筛选器能够顺着关系箭头的指向去筛选下级表而不能逆向筛选的原因。例如,对于上面给出的这个数据模型而言,当我们在产品表中的产品类别列上设置了一个筛选器,那么这个筛选器就只会筛选产品类别列上的值,但由于列是表不可分割的一部分,从而使得包含产品类别列的表都会跟着被筛选。又因为订单表的扩展表上刚好包含了产品表的产品类别列,所以订单表的扩展表也跟着被筛选了,从而使得订单表被筛选,这就是筛选器能够顺着关系箭头的指向去筛选下级表的原因。那么反过来,当我们在订单表中的产品名称列上设置了一个筛选器,那么这个筛选器就只会筛选订单表的产品名称列,由于订单表必定包含订单表中的产品名称列,所以订单表会跟着被筛选。但是,对于产品表而言,它不存在任何上级表,所以产品表的扩展表上的列就只有产品表的自身列,并不包含订单表中的任何列,所以对于订单表中任意列上的筛选器来说,产品表的扩展表都不会跟着被筛选,这就是为什么筛选关系不能逆向传播的原因。

  当我们掌握了扩展表原理后,之前学习的那种筛选器在表间关系上的传递过程的理解就基本上可以抛弃了,但是扩展表原理只在强关系上才能使用,因为对于多对多关系等弱关系来说是不存在扩展表的,从而使得扩展表原理不适用,那么这时就要用到之前学习的那种理解方式了。


  下面来看一下基于扩展表原理的应用,主要就是我上文中提到的这句话:“当某个基础表被用作筛选器参数或者该基础表的行上下文被转换成筛选上下文后,其扩展表上不属于基础表自身列的其他列也会设置筛选器”。由于某个基础表的扩展表上不仅包含了基础表的自身列,还包含了该基础表的上级表的所有列,所以基于扩展表的一个应用就是能够突破表间关系不能逆向筛选的限制,从而在某些情况下能够化腐朽为神奇。

  下面通过一个例子来举例说明,使用到的度量值表达式如下:

存在销售记录的产品数量 = CALCULATE(COUNTROWS('产品表'),'订单表','订单表'[产品名称]="耳机")

  把上述度量值放入卡片图中,结果如下:

  这个度量值计算的内容就如它的名称一样,就是计算有多少个产品具有销售记录。为了验证结果,我还特地用矩阵计算出了所有产品具有的订单数量。从矩阵中的结果可以看到,的确是有8个产品具有订单,从而说明了上面这个度量值的计算结果是准确的。那下面就来分析一下上述度量值的计算过程:

  1. 该度量值应用在卡片图中,因此不存在任何初始的筛选器,即外部计值环境中不存在任何筛选器
  2. 先在外部计值环境下计算CALCULATE的各个内部筛选器参数,得到两个内部筛选器如下:

    '订单表'                    // 注意,订单表为基础表,因此其扩展表上不属于基础表自身列的其他列也会设置筛选器
    
    '订单表'[产品名称]="耳机"
  3. 由于不存在行上下文,所以环境1等于外部筛选上下文,所以环境1也不存在任何筛选器
  4. 由于CALCULATE函数不包含任何筛选调节器,因此环境2等于环境1,所以环境2也不存在任何筛选器
  5. 将CALCULATE函数的各个内部筛选器相交,即把第二步中的两个筛选器相交,得到环境3,环境3生成的筛选器组合如下图所示:
  6. 环境3与环境2交互,由于环境2不存在任何筛选器,因此最终计值环境等于环境3,即上图所示的筛选器组合
  7. CALCULATE的计算器参数在最终计值环境里计值,将统计产品表的可见数据的行数
  8. 由于产品表的扩展表不包含下级表(订单表)的任意列,因此订单表任意列上的筛选器均无法筛选产品表,只有产品表的扩展表上包含的列才能筛选产品表,即上述筛选器组合中蓝色列名的列上的筛选器才能筛选产品表。因此,对于产品表而言,最终计值环境等价于以下筛选器组合:
  9. 经上述筛选器组合筛选后的产品表刚好只有8行,因此计算器参数最终返回8。由于该蓝色列名的筛选器组合最初是来自于订单表的扩展表,所以从语义上来说,该度量值计算的就是存在销售记录的产品数量。

  从上面这个例子中可以看到,我们只有清楚地知道每个筛选器能筛选的内容并严格按照各个函数的计值流程去梳理,才不会出错。否则,我们很容易就会被订单表里的各个筛选器干扰,从而得到错误的结果。

  此外,虽然利用扩展表原理能够突破不能逆向筛选的限制,但还是少用为好,因为这样使用的性能是比较慢的。如果我们真的想要达到逆向筛选的效果,那么不妨使用双向筛选箭头的表间关系,这样性能反而更快,也更好理解。


  那么扩展表原理相关的内容就介绍到这里了,我的表达能力不太好,可能表述得有点凌乱,因此看完后仍不理解的话可以再看多两遍。此外,在读懂本文后,一定要去多做一点练习与测试,并从扩展表原理的角度去思考筛选器的传递过程,争取把扩展表原理形成条件反射。


  DAX系列文章中涉及到的案例文件,均已上传到QQ群:344353627,若有需要,可自行加群获取。

未经允许不得转载:夕枫 » 19、扩展表原理
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论