DAX:关系概述(高阶)

PowerBI的数据模型是表格模型,只能基于单个列创建关系,不支持基于多列的关系。但是,可以通过把相关的多个列组合成一个计算列,以它作为主键来创建关系。在数据模型中创建的关系,称作物理关系,物理关系会存储到数据模型中,虚拟关系只存在于DAX代码中,相比虚拟关系,利用物理关系的查询性能会更高。

一,使用计算列来创建关系

计算列使用COMBINEVALUES函数,用分隔符和一组表达式作为参数,并把这些表达式的结果以字符串的形式串联起来,用分隔符分割。使用COMBINEVALUES函数的优势是:如果模型通过DirectQuery方式连接到数据源,那么通过COMBINEVALUES函数创建的关系性能得到优化。举个例子:

Sales[DiscountKey] = COMBINEVALUES("-",Sales[OrderDate], Sales[ProductKey)]

注意:在基于计算列创建关系时,经常发生循环依赖。循环依赖是指同一依赖既可以是A依赖于B,也可以是B依赖于A,无法分辨,DAX不允许出现这种依赖关系。通常情况下,DAX中的依赖类型是:空行依赖和引用依赖,这两个依赖都不能循环。

1,空行依赖

如果计算列位于关系的“一”端,并且多端存在多余的记录,那么DAX引擎会在关系的“一”端创建空行。

举个例子,PriceRanges表用于表示Sales[Price]的价格区间,注意:使用VALUES()的原因是:虽然VALUES()返回的是一个表,不是一个标量值;但是当一个表包含单行和单列时,如果表达式需要,这个表会被自动转换为标量值。

Sales[PriceRangeKey]=
VAR FilterPriceRanges = FILTER(PriceRanges, AND(PriceRanges[MinPrice] <= Sales[Price], PriceRanges[MaxPrice] > Sales[Price])) 

RETURN CALCULATE(VALUES(PriceRanges[PriceRangeKey]),FilterPriceRanges )                           

这个计算列计算除了正确的值,但是,当试图在表PriceRanges[PriceRangeKey]和Sales[priceRangeKey]之间创建关系时,将由于循环依赖关系而导致错误。原因是:PriceRanges表可能会产生一个空行,也可能没有空行,这取决于Sales[PriceRangeKey]的值。换句话说,当Sales[PriceRangeKey]的值发生变化时,PriceRanges表的内容可能也会发生变化。反过来,当PriceRanges表的内容发生了变化,那么Sales[PriceRangeKey]的值也可能需要更新。

空行循环依赖的情况是这样的:

  • Sales[PriceRangeKey]依赖于PriceRanges表,是因为引用了PriceRanges表(引用依赖),并且VALUES()函数可能会返回额外的空行(空行依赖);
  • PriceRanges表依赖于Sales[PriceRangeKey],是因为Sales[PriceRangeKey]可能会导致PriceRanges产生空行(空行依赖)。

2,打破空行循环依赖

为了终止循环依赖的关系链,只需要打破Sales[PriceRangeKey]对PriceRanges表中的空行依赖。就是说,确保公式中使用的所有函数产生的结果不包含空行。

当多端的一个值不存在于“一”端中时,VALUES()返回的结果会把空行包含进来。而如果使用DISTINCT函数,无论额外的空行是否存在,DISTINCT始终产生相同的结果。

如果使用DISTINCT函数代替VALUES(),那么Sales[PriceRangeKey]不再依赖空行,最终的结果是:PriceRanges表依赖于Sales[PriceRangeKey]的空行,而Sales[PriceRangeKey]依赖于PriceRanges的引用。由于两个依赖关系没有形成闭环,所以循环依赖消失了。

通常情况下,打破空行循环依赖的方法是:

  • 使用DISTINCT 代替VALUES
  • 使用ALLNOBLANKROW代替ALL
  • CALCULATE不要直接使用布尔表达式作为筛选器参数

举个例子,非常隐蔽的ALL函数,下面两个DAX公式是等价的:

= CALCULATE( MAX( Customer[YearlyIncome]), Customer[Education]="High School")
= CALCULATE( MAX( Customer[YearlyIncome]), FILTER(ALL(Customer[Education]), Customer[Education]="High School"))

ALL函数会创建对空行的依赖,使用ALLNOBLANKROW不会返回空格。

=CALCULATE( MAX( Customer[YearlyIncome]), FILTER(ALLNOBLANKROW(Customer[Education]), Customer[Education]="High School"))

二,使用虚拟关系

虚拟关系是指在数据模型中不是物理上创建的关系,虚拟关系只存在于DAX中,需要编写DAX代码把筛选器从一个表转移到另一个表,也就是说,通过DAX代码实现数据的筛选。

1,使用CONTAINS函数转移筛选器

次优的做法是依靠迭代,通过逐行检查是否包含相应的数据。可以使用CONTAINS函数,如果包含<value>,那么函数返回True。

CONTAINS(<table>, <columnName>, <value>[, <columnName>, <value>]…) 

举个例子,表Sales和Advertised之间没有关系,但是和Product表之间存在关系。FILTER函数迭代Sales表,并未每一行调用CONTAINS函数,CONTAINS函数返回做过广告的Brand,如果产品的Brand包含在表Advertised[Brand]中,那么CONTAINS返回True。FILTER函数获取到筛选之后的数据集,逐行计算销售的总价。

Advertised Brands Sales1 = 
SUMX( FILTER( Sales, CONTAINS(Advertised, Advertised[Brand], RELATED('Product'[Brand]))
        ,Sales[Quantity] * Sales[Price])

2,使用TREATAS函数转移筛选器

TREATS通过数据沿袭实现筛选上下文的转移,这是应用虚拟关系首选也是最好的做法,请阅读:DAX :【翻译】数据沿袭(Data Lineage )了解详细用法。

Advertised Brands Sales2 =
VAR AdvertisedBrands = SUMMARIZE(Advertised, Advertised[Brand])
VAR FilterAdvertisedBrands = TREATS(AdvertisedBrands, Product[Brand])
RETURN CALCULATE( SUM(Sales[Quantity] * Sales[Price]), KEEPFILTERS(FilterAdvertisedBrands))

 

 

 

 

 

 

posted @ 2018-10-31 18:33  悦光阴  阅读(14612)  评论(0编辑  收藏  举报