DAX的常用表函数之VALUES与DISTINCT

  • 格式:docx
  • 大小:871.24 KB
  • 文档页数:8

一、前言
在学习DAX的过程中,初学者一般将DAX的表达式分为计算列、度量值以及查询三个类别,他们一般都是将表函数单独用于查询,在计算列与度量值中很少使用返回表的表函数。

但实际上,表函数的作用不仅仅是返回表的数据,它在计算列和度量值上起到的作用可以说是非常重要的,因此不能刻意的只将表函数用作查询。

在前面的文章中我也说过,计算列、度量值与查询其实是一样的,都是DAX表达式,只是它们所处的计值环境不同而已,对DAX的学习归根到底其实就是对计值环境的理解。

因此,不能将表函数的作用局限于查询!
DAX的表函数有很多,例如:VALUES、DISTINCT、ALL、ADDCOLUMNS、TOPN 等等,本文只介绍VALUES与DISTINCT的用法,其它的表函数在以后的文章中讲解。

之所以列出其它的相关函数,主要是想帮助你们形成一个系统且全面的DAX 知识体系。

这样,哪怕我写文章的速度慢,你们也可以去查找资料来学习。

二、VALUES的用法
2.1 将VALUES用作表函数
VALUES可以说是使用频率最高的一个表函数了,因为它能以返回表的方式来引用列。

当你想将列作为表来使用时,只需要在所使用的列的外面套上一个VALUES,问题就可以迎刃而解。

VALUES的语法结构如下:
•语法:
VALUES ( <TableName> | <ColumnName> )
• 1
•作用:
VALUES将其参数在数据模型中的可见值以表的形式返回,它的参数可以是列,也可以是表,具体如下:
1、若参数为列时,VALUES返回该列的所有可见值,忽略重复值;
2、若参数为表时,VALUES返回该表的所有可见值,并且保留重复行。

其中,作为参数的表只能是基础表,不能是返回表的表达式。

可以参照下图来理解VALUES的作用:
VALUES的参数可以是表也可以是列,若参数为列时会忽略重复值,而参数为表时却不会忽略重复行,这一点需要谨记。

此外,无论其参数是否为表或列,都会受到外部计值环境的影响。

为了加深对VALUES的理解,下面举一个小例子:求解销量大于300的门店有哪些,使用到的DAX度量值如下:
销量 := SUM('销售表'[销售数量])
销量大于300的门店_1 := CALCULATE (
[销量],
FILTER ( '门店',[销量] > 300 )
)
销量大于300的门店_2 := CALCULATE (
[销量],
FILTER ( VALUES('销售表'[店号]),[销量] > 300 )
)
• 1
• 2
• 3
• 4
• 5
• 6
•7
•8
•9
•10
•11
结果如下图所示:
上图中的行标签代表的是店号,可以看到,两个求解销量大于300的门店的度量值得到的结果是一样的。

区别只在于,[销量大于300的门店_1]这个度量值在FILTER里的第一参数里使用了完整的基础表,而[销量大于300的门店_2]使用了VALUES所返回的虚拟表。

当你不存在门店表时,又想要筛选门店,那么你就可以用VALUES将门店列以表的形式返回,就如上面的例子一样。

2.2 将VALUES用作标量值
首先,VALUES是只能返回表的表函数,它并不能用于聚合得到标量值。

但是,当VALUES里的参数所指代的表或列在外部计值上下文的作用下只剩下一个值,也就是一行一列的表时,就可以将VALUES返回的那个一行一列的表当作标量值来使用,如下图所示:
上图中所用到的度量值的表达式如下:
只有一个值的VALUES能用作标量值:=CALCULATE(VALUES('商品表'[售价]),'商品表'[商品编码]="A01")
将多个值的VALUES用作标量值会报错:=CALCULATE(VALUES('商品表'[售价]))
• 1
• 2
• 3
三、DISTINCT的用法
DISTINCT的功能与VALUES基本一致,仅在某些情况下具有不同的表现。

DISTINCT的语法结构如下:
•语法:
DISTINCT ( <TableName> | <ColumnName> )
• 1
•作用:
DISTINCT将其参数在数据模型中的可见值以表的形式返回,它的参数可以是列,也可以是表,具体如下:
1、若参数为列时,DISTINCT返回该列的所有可见值,忽略重复值;
2、若参数为表时,DISTINCT返回该表的所有可见值,并且删除重复行。

其中,作为参数的表可以是基础表或者是返回表的表达式。

可以参照下图来理解DISTINCT的作用:
四、VALUES与DISTINCT的区别
VALUES与DISTINCT的区别主要有三个,具体如下:
1、VALUES的参数若为表时,只能是基础表。

而DISTINCT的参数若为表时,可以是基础表或者是返回表的表函数;
2、VALUES的参数若为表时,保留重复行。

而DISTINCT的参数若为表时,则会删除重复行;
3、当遇到由参照完整性不匹配而产生的空行时,VALUES会处理空行而DISTINCT则不会处理空行,也就是说VALUES会返回由参照完整性不匹配而产生的空行,而DISTINCT则不会返回由参照完整性不匹配而产生的空行。

下面对第三点的区别做一个详细说明,使用到的DAX度量值公式如下:
商品表行数:=COUNTROWS('商品表')
values 商品编码:=COUNTROWS(VALUES('商品表'[商品编码]))
distinct 商品编码:=COUNTROWS(DISTINCT('商品表'[商品编码]))
values 商品售价:=COUNTROWS(VALUES('商品表'[售价]))
distinct 商品售价:=COUNTROWS(DISTINCT('商品表'[售价]))
• 1
• 2
• 3
• 4
• 5
• 6
•7
•8
•9
利用上面的度量值来求解商品表的行数,通过对比它们行数之间的差异,就可以观察到VALUES与DISTINCT在处理由参照完整性不匹配而产生的空行时所采取的不同行为。

在看结果之前,先来看看我们的数据,如下图所示:
可以看到,被红框框住的销售记录所售的产品在商品表里找不到对应的产品,因此在这里就存在着参照完整性不匹配的情况,所以DAX引擎会在上级表,也就是在商品表里增加一个空白行来对应那些不匹配的销售记录。

但与此同时,商品表里的黄色高亮行的售价为空,这并不代表着没有意义,而代表着它的值就是空。

可能有点拗口,但是请记住商品表里有个售价为空的情况,这个空值与空行的空值是不一样的。

下面来看结果,如下图所示:
可以看到,有一个被命名为:(空白) 的行标签,这个行标签对应着的就是那些在下级表里的不匹配行。

观察VALUES与DISTINCT在商品编码这一列上返回的行数就可以看到它们之间行为的不同,这个例子充分说明了VALUES会返回由参照完整性不匹配而产生的空行,而DISTINCT则不会。

然后,再来观察VALUES与DISTINCT在商品售价这一列上返回的行数。

可以看到,VALUES在(空白)这一个行标签下的值还是为1,也就是说VALUES返回了由参照完整性不匹配而产生的空行,但是VALUES在总计行的值却是4而不是5。

造成这个现象的原因就是因为商品售价那一列上有空值,而VALUES也返回了由参照完整性不匹配而产生的空行,也就是说VALUES返回了两个空值,但因为VALUES的参数为列时是会忽略重复值的,所以就相当于将两个空值合并为了一个,也可以说是删除了一个空值,因此总计行的结果为4而不是5。

看到这里,可能有人会简单的以为VALUES会返回空值而DISTINCT则不会返回空值,这个理解是错误的。

VALUES与DISTINCT是否返回空值,取决于这个空值属于谁,如果这个空值是属于参照完整性不匹配而产生的空行,那么VALUES 会返回这个空值,而DISTINCT则不会。

但如果说空值是属于已存在的数据的某列上,那么这个空值无论是VALUES还是DISTINCT都会返回。

想要证明也很简单,只需要把下级表的不匹配数据删掉,也就是把上面所给出的销售表的不匹配记录删掉,再刷新就可以看到结果:
在商品售价这一列上是有一个空值的,但无论是VALUES还是DISTINCT都返回了相同且正确的数值,这足以说明数据里的空白与参照完整性不匹配而产生的空白是不一样的。

五、总结
VALUES与DISTINCT都是使用频率非常高的表函数,它们的作用与接受的参数都可以说是基本一致,但却不是完全一样。

两个函数都有各自的特点,又因为使用频率高,若不彻底理解的话很有可能就会出错或混用。

建议看完这篇文章后亲自去测试一下,以更好的理解它们之间的差异。