PowerBI之DAX神功:第1卷第20回 循环依赖什么鬼?
一、循环依赖之时间旅行者
循环依赖是鬼,它活着的时候叫循环引用。
《大话西游大圣娶妻》紫霞仙子说了几句牛魔王听不懂的话:),牛魔王对青霞说:“你妹妹疯了,说了一晚上鬼话!”
大家要知道,循环依赖和循环引用,是一回事,只是活着和死了叫法不一样

【新建列】儿子 = 'Sheet2'[父]+'Sheet2'[母]
【新建列】孙子 = 'Sheet2'[儿子]+'Sheet2'[儿媳]
我再修改【儿子】列时,就提示检测到循环依赖
儿子 = 'Sheet2'[父]+'Sheet2'[孙子]

这样造成的循环依赖的原因,大家都清楚:
儿子的出生与孙子无关,没有儿子哪来的孙子?
孙子列是依赖儿子列生成的,你再又让儿子列依赖孙子列生成,是不行的。
二、第二次使用Calculate新建列造成的循环引用

【新建列】销售金额 = sumx('Sheet3','Sheet3'[销量]*'Sheet3'[售价])

新建列(销售金额)是行上下文没有筛选功能,这个表中任何列都不能筛选它

【新建列】销售金额 = calculate(sumx('Sheet3','Sheet3'[销量]*'Sheet3'[售价]))

当套上Calculate之后,所有字段都可以筛选了

当我写第2个新建列时
销售成本 = sumx('Sheet3','Sheet3'[销量]*'Sheet3'[进价])

这样写,哪个列都不能筛选,没问题。但是,我给它也套上一个Calculate时,出问题了:
销售成本 = CALCULATE(sumx('Sheet3','Sheet3'[销量]*'Sheet3'[进价]))

《The Definitive Guide to DAX》中的解释非常专业,也许你能看懂,但是你不可能每次都将他展开找原因。
DAX神功解释:得语文者得天下,得阅读者得语文

这句话已经说得很清楚了,销售成本这列,不能被销售金额这个列筛选
你写成下面这样,取消销售金额这个列的筛选就可以了。
销售成本 = CALCULATE(sumx('Sheet3','Sheet3'[销量]*'Sheet3'[进价]),all(Sheet3[销售金额]))

sumx本来是行上下文,你通过calculate强行转化为筛选上下文
被强行转化的这两列分别是:销售金额和销售成本
销售金额通过前4列筛选:商品、销量、售价、进价

销售成本 = CALCULATE(sumx('Sheet3','Sheet3'[销量]*'Sheet3'[进价]))
这样写出来的销售成本,原则上按前5列筛选:商品、销量、售价、进价、销售金额
销售金额依赖前4列筛选,它就不能再去筛选销售成本,所以要取消销售金额的筛选

首先,筛选器不能重复
其次,筛选器不能隔山打牛
所以,这样写也是错的,因为隔山打牛
销售成本 = CALCULATE(sumx('Sheet3','Sheet3'[销量]*'Sheet3'[进价]),ALL('Sheet3'[商品],Sheet3[销量],Sheet3[售价],Sheet3[进价]))

上面的公式,相当于取消了前4列的筛选,使用销售金额筛选,这样隔山打牛是不行的。
三、多表循环依赖的处理方法


现在这两张表无法建立关系,没有主键和外键
【度量值】总分1 = SUM('成绩表'[分数])
【新建列】等级1 = SWITCH(TRUE(),[总分1]>90,"S",[总分1]>60,"A","B")

【新建列】新列 = [等级1]&"-MG"

怎么解决?因为现在只有一张表,按照单张表处理方法,修改度量值:
【度量值】总分1 = CALCULATE(SUM('成绩表'[分数]),ALL('成绩表'[新列]))

现在我们将【总分1】度量值恢复,并且删除【新列】
【度量值】总分1 = SUM('成绩表'[分数])

两表连线会出现循环引用:

DAX神功完整解释:如果使用成绩表[等级1]与评价表[等级]连线,由于【总分1】这个度量值的上下文作用,成绩表[等级1]就会与评价表[等级]产生依赖关系,而评价表[等级]又通过成绩表和评价表直接的关联关系与成绩表[等级1]产生依赖,这就形成了一个循环依赖关系,所以这两个表不能使用等级字段创建关系。
我担心你看不懂这段话,所以我画个图:

当一端表和多端表建立连线关系后,其实表格是这样的:

就相当于是,1端表中的所有列都Vlookup到了多端表中去了。
现在修改度量值,你只写all(等级1)是不行的,因为连线后,评价表字段也牵连进来了,
【度量值】总分1 = CALCULATE(SUM('成绩表'[分数]),ALL('成绩表'[等级1]))
可是两个表没建立关系之前,你的度量值里无法引用其它表中的列,这时ALLexcept就有了它无法替代的作用

总分1 = CALCULATE(SUM('成绩表'[分数]),ALLEXCEPT('成绩表','成绩表'[学号],'成绩表'[科目],'成绩表'[分数]))
只允许学号,科目,分数列筛选,取消其它所有列的筛选。
有些人会说,不对呀,写成这样也可以
总分1 = CALCULATE(SUM('成绩表'[分数]),ALLEXCEPT('成绩表','成绩表'[学号]))
写成什么样是根据你的业务需求决定的,但是,循环依赖这个鬼,你必须保证 成绩表中的等级1和评价表里所有列在【总分1】度量值中取消筛选
关于其它列是否筛选,由你自己决定。
四、九叔重现,避开鬼打墙,多表循环依赖的处理方法:


这次看每个人数学和语文的总分数+花名册中的加分后得到的每个人总分
大于等于180是S, 大于等于120是A,否则是B

我们在《DAX神功》第1卷第14回 讲过,写法不止下面一种
[新建列]
总分 =
var x = '成绩表'[学号]
return
sumx(FILTER('成绩表','成绩表'[学号]=x),'成绩表'[分数]+RELATED('花名册'[加分]))

新建列浪费内存,我们一般会使用度量值方式。
[度量值]
总分 =
var x = SELECTEDVALUE('成绩表'[学号])
return
sumx(FILTER(all('成绩表'),'成绩表'[学号]=x),'成绩表'[分数]+RELATED('花名册'[加分]))

[新建列]
等级 = SWITCH(TRUE(),[总分]>=180,"S",[总分]>=120,"A","B")

现在评价表与成绩表通过等级连线就不会出现循环依赖的问题了,当前行概念就是九叔,避开鬼打墙。
五、度量值出现循环引用的概率低


表格白底黑字是网友留言中叙述的原始表,绿底黑字是我在Excel中计算的结果
将白底黑字表格放到PowerBI中,写如下度量值:
本月平均单价 = DIVIDE(sumx('Sheet4','Sheet4'[上月末金额]+'Sheet4'[本月入库金额]),sumx('Sheet4','Sheet4'[月初数量]+'Sheet4'[本月入库数量]))
本月出库金额 = sum(Sheet4[出库数量])*[本月平均单价]
下月初金额 = SUM(Sheet4[上月末金额])+sum(Sheet4[本月入库金额])-[本月出库金额]

完全正常没有问题。度量值是不易出现循环引用的,也不是绝对,比如讲时间智能日期函数时就会遇到。
建议:
度量值1和5完全没有必要写,心里明白就可以了
打个比方,公司集体会议,无论什么职务,开会点名都答“到”!
我叫孙兴华,是公司的副总裁,同时也是总经理。
你喊我孙兴华,我答“到”!就可以了!叫我一次就可以了,不能因为我有两个职务,你就喊我两次。
如果你喊副总裁,很有可能是总裁答“到”!总裁有可能姓付。
不做无意义的重复劳动。
关于度量值中的循环引用,我们后期课程中还会涉及,敬请期待。
