本篇文章将介绍VALUES与DISTINCT这两个函数的使用与注意事项,它们都属于DAX的核心函数,因此这两个函数的每一处细节最好都要掌握。


  先来看一下本篇文章要用到的数据与数据模型,如下图所示:


  VALUES函数的用法

  VALUES可以说是使用频率最高的一个表函数了,因为它能以返回表的方式来引用列。当你想将列作为表来使用时,只需要在所使用的列的外面套上一个VALUES,问题就可以迎刃而解。VALUES的语法结构如下:

  • 语法:
VALUES ( <TableName> | <ColumnName> )
  • 作用:

  VALUES将其参数在数据模型中的可见值以表的形式返回,它的参数可以是列,也可以是表,具体如下:

  1、若参数为列时,VALUES返回该列的所有可见值去重后形成的单列表;

  2、若参数为表时,VALUES返回该表的所有可见值,并且保留重复行。其中,作为参数的表只能是基础表,不能是返回表的表达式。

  VALUES函数比较简单,我们只需要记住,当它的参数为列时会去重,而参数为表的时候不会去重就可以了。由于其参数为表时不会去重,那么就跟直接引用基础表时的效果一致,因此一般我们都只使用VALUES函数来引用列。此外,无论VALUES函数的参数是列还是表,它都是返回的可见值,即需要考虑外部筛选上下文的影响。

  下面来看一些帮助理解的例子,由于VALUES函数是表函数,为了方便起见就以查询来演示。具体如下图:

  VALUES是一个表函数,它返回的是一个表,但在DAX中,单行单列的表可以被当作标量值来使用。下面就以VALUES函数来演示一下这种特性,具体如下图:


  DISTINCT函数的用法

  DISTINCT的功能与VALUES基本一致,仅在某些情况下具有不同的表现。DISTINCT的语法结构如下:

  • 语法:
DISTINCT ( <TableName> | <ColumnName> )
  • 作用:

  DISTINCT将其参数在数据模型中的可见值以表的形式返回,它的参数可以是列,也可以是表,具体如下:

  1、若参数为列时,DISTINCT返回该列的所有可见值去重后形成的单列表;

  2、若参数为表时,DISTINCT返回该表的所有可见值,并以行为单位去重。其中,作为参数的表可以是基础表或者是返回表的表达式。

  可以参照下图来理解DISTINCT的用法:


  VALUES与DISTINCT的区别

  VALUES与DISTINCT的区别主要有三个,具体如下:

  1、VALUES的参数若为表时,只能是基础表。而DISTINCT的参数若为表时,可以是基础表或者是返回表的表函数;

  2、VALUES的参数若为表时,保留重复行。而DISTINCT的参数若为表时,则会对重复行去重;

  3、当遇到由参照完整性不匹配而产生的空行时,VALUES会处理空行而DISTINCT则不会处理空行,也就是说VALUES会返回由参照完整性不匹配而产生的空行,而DISTINCT则不会返回由参照完整性不匹配而产生的空行。

  下面对第三点的区别做一个详细说明,使用到的度量值表达式如下:

商品表行数 = COUNTROWS('商品表')

values 商品编码 = COUNTROWS(VALUES('商品表'[商品编码]))

distinct 商品编码 = COUNTROWS(DISTINCT('商品表'[商品编码]))

values 商品售价 = COUNTROWS(VALUES('商品表'[售价]))

distinct 商品售价 = COUNTROWS(DISTINCT('商品表'[售价]))

  利用上面的度量值来求解商品表的行数,通过对比它们行数之间的差异,就可以观察到VALUES与DISTINCT在处理由参照完整性不匹配而产生的空行时所采取的不同行为。

  在看结果之前,再来看看我们的数据:

  可以看到,在销售表里有一个被标红了的商品编码(A05),这个商品编码在商品表中是不存在的,因此在这里就存在着参照完整性不匹配的情况,所以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都是使用频率非常高的表函数,它们的作用与接受的参数都可以说是基本一致,但却不是完全一样。两个函数都有各自的特点,又因为使用频率高,若不彻底理解的话很有可能就会出错或混用。建议看完这篇文章后亲自去测试一下,以更好的理解它们之间的差异。


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

  加入Q群