PowerBI之DAX神功:第1卷第18回 【颠覆权威】Calculate复杂的筛选条件
一、筛选单列和多列
通过前两节课的学习,我们理解了下面两个度量值是等价公式
本期销量1 = CALCULATE([总销量],'销售表'[日期]="本期")
本期销量2 = CALCULATE([总销量],filter(all('销售表'[日期]),'销售表'[日期]="本期"))
回忆《DAX神功》第1卷第7回 基础表函数之Filter函数
我们学习了filter单列和多列筛选,如果你是按顺序听课,现在举一反三没有问题

总分 = Sum(Sheet1[分数])
一班男生分数 = CALCULATE([总分],'Sheet1'[性别]="男",'Sheet1'[班级]="一班")
一班男生分数 = CALCULATE([总分],'Sheet1'[性别]="男" && 'Sheet1'[班级]="一班")
一班男生分数 = CALCULATE([总分],filter(all('Sheet1'[性别],'Sheet1'[班级]),'Sheet1'[性别]="男" &&'Sheet1'[班级]="一班"))
注:使用逗号或&&代表and(且)的关系,|| 代表or(或)的关系
现在性别和班级都无法筛选,因为all(列)

我们可以通过姓名进行筛选

以上举例每列上只有一个条件,如果是单列上有多个条件呢?
一班男女生分数 = CALCULATE([总分],'Sheet1'[性别] in {"男","女"} , 'Sheet1'[班级]="一班")
一班男女生分数 = CALCULATE([总分],'Sheet1'[性别] in {"男","女"} && 'Sheet1'[班级]="一班")

二、权威书籍中对复杂筛选的建议
如果你的表就如同《The Definitive Guide to DAX》案例所讲述的,所有列都来自一张表

需求:销售金额大于1的商品卖了多少钱?
常识:销售金额=数量*售价
PowerBI 2021年6月版亲测可用,早期版本及其它支持DAX的软件未经测试
金额总和=sumx('Sheet2','Sheet2'[数量]*'Sheet2'[售价])
度量值 = CALCULATE([金额总和],'Sheet2'[数量] * 'Sheet2'[售价] >1)

如果你使用的是低版本的PowerBI,可以选择升级到最新版。
如果你使用的是Excel中的PowerPivot,可以使用下面的等价公式:
等价度量值 = CALCULATE([金额总和],filter(all(Sheet2[数量],Sheet2[售价]),'Sheet2'[数量] * 'Sheet2'[售价] >1))

恢复筛选度量值 = CALCULATE([金额总和],keepfilters(filter(all(Sheet2[数量],Sheet2[售价]),'Sheet2'[数量] * 'Sheet2'[售价] >1)))
恢复筛选等价度量值= CALCULATE([金额总和],filter('Sheet2','Sheet2'[数量] * 'Sheet2'[售价] >1)))
理论上,直接使用表会比keepfilters慢,速度是个虚无缥缈的事情,具体情况具体分析,非战斗人员你的重心应该是如何写对公式算出来。
我并不建议大家用这样的方法处理,我没有指责书中这么讲不对,而是国人看完书籍之后跑出来抬扛是错的。

三、孙兴华对复杂筛选的建议
方法千千万!我在《火力全开》笔记05课,教给大家的方法:
恢复筛选等价度量值= CALCULATE([金额总和],filter('Sheet2','Sheet2'[数量] * 'Sheet2'[售价] >1)))
等价度量值2 = CALCULATE([金额总和],FILTER('Sheet2',[金额总和] >1))
还记得《火力全开》中笔记原文吗?
PS:《火力全开》源于《乾坤大挪移》正面迎战六大门派,各门各派惧怕《火力全开》的流行,有雇佣水军的、有开着小号来捣乱的、有托私人关系打压的、还有学完《火力全开》想讲出来卖钱又觉得《火力全开》影响它生意了故意来黑的,还有吓鸟的...等等
切记:从国外文献或书籍上看到的东西,需要动脑子,否则没有灵魂。

我为什么不建议你们使用书中的方法,因为你在实际工作中,不可能是一张表
虽然,我建议你使用SQL从服务器上拿一张干净的表(需要哪些字段就拿哪些字段)
但是,你有各种理由不这样去做。
你的情况比我举的例子还要复杂,你用书中的方法就不易实现了


需求:销售金额大于1的商品卖了多少钱?
总金额 = SUMX('销售表','销售表'[数量]*RELATED('商品表'[售价]))
度量值1 = CALCULATE([总金额],'销售表'[数量]*'商品表'[售价]>1)
错误: 该表达式包含来自己多表的列,只有单个表中的列筛选器布尔表达式中
度量值2= CALCULATE([总金额],'销售表'[数量]*RELATED('商品表'[售价])>1)
错误:关系函数是用在行上下文中的,Calculate创建的是筛选上下文
度量值3 = CALCULATE([总金额],FILTER('商品表','商品表'[售价]*RELATEDTABLE('销售表'[数量])>1)
错误:filter第1参数使用1端表时,关系函数无法通过一端找多端的列,RELATEDTABLE参数只能是表
度量值4 = CALCULATE([总金额],FILTER('销售表','销售表'[数量]*RELATED('商品表'[售价])>1))
正确:因为filter不仅是表函数还是迭代函数,上行上下文
从度量值3和度量值4我们可以推导出来:
度量值5 = CALCULATE([总金额],FILTER('商品表',[总金额]>1)
错误:虽然没有报错,但是返回结果不是我们需要的,因为度量值3,关系没有建立,所以筛选失效,返回的结果是没有筛选的。
度量值6 = CALCULATE([总金额],FILTER('销售表',[总金额]>1)
正确:同理于度量值4
我上述讲的两个正确的度量值4、度量值6 都没有限制筛选
如果想限制某个列或某几个列不能筛选,filter第1参数使用all(列)
如果想限制所有列不能筛选,filter第1参数使用all(表)

四、现在打脸开始

很多人看《火力全开》时问我,你在第5节讲Filter时举的这个例子我看不懂。
你先回答我一个问题:filter是表函数,是表函数的我们都可以放到新建表中做测试
这句话我在《火力全开》中是不是经常说?你有测试吗?如果你测试了,你就秒懂!


总数量 = sum('销售表'[数量])
度量值7 = CALCULATE([总数量],FILTER('商品表',[总数量]>2))
现在我们将FILTER('商品表',[总数量]>2)拿出来放到新建表中

度量值8= CALCULATE([总数量],FILTER('销售表',[总数量]>2)) //错误
为什么说度量值8是错误的?其实不是错误,而是它得到的数据不是你的需求

五、嵌套筛选

加拿大安大略省城市:伦敦
它位于加拿大安大略省的西南部,距离多伦多不到200km。
伦敦人数 =
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),'Sheet1'[地区]="伦敦"),
'Sheet1'[国家]="英国"
)
PS:筛选时要注意筛选条件是不是限制了某个列的筛选?
假设你内层限制了某个列的筛选,外层又筛选这个列,那是无效的。
六、Calculate公式计算顺序
你有没有发现上一个案例我是不是写反了,没有!
伦敦人数=
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),'Sheet1'[国家]="英国"),
'Sheet1'[地区]="伦敦"
)
你会发现这样写答案也是对的,你真聪明:
我先筛选英国,再筛选伦敦;或先筛选伦敦再筛选英国;本来就是一回事
像这种筛选不同列的问题,谁在里面谁在外面,不会影响最终结果
但是,你必须要知道他的正确顺序。

DAX函数中,只有Calculate和CalculateTable是从外向内计算,其它函数都是从内向外计算。
Calculate和CalculateTable都是先计算第2参数,再计算第1参数。
美国人数 =
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),'Sheet1'[国家]="美国"),
'Sheet1'[国家]="英国"
)
英国人数 =
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),'Sheet1'[国家]="英国"),
'Sheet1'[国家]="美国"
)
权威书籍和收费讲师,又是使用相同的图来讲这个问题:
上边写上一个公式,左边画一条龙,右边画一道彩虹,中间一个大箭头,指向下面一个不知道是龙还是彩虹。
DAX圣经想法
顺序是外层筛选器先生效,由外向内,当遇到内层筛选器时,内层筛选器覆盖外层筛选器。

我孙兴华一介文盲,让我给你讲讲,你以后,就记着,所有DAX函数都是从内向外的计算顺序。我这是颠覆式的说法:你先别着急喷我,且听我慢慢道来
【1】没学过DAX,但是正常人的想法
例如:【美国人数】的度量值,我先从所人数总和中,筛选出美国的人数,我在美国的人数中筛选英国,能找到吗?找不到,所以筛选失败,它最终结果还是美国人数。
同理:英国人数度量值也是相同道理。
【2】DAX神功想法
以英国人数度量值为例,内层的Calculate是不是取消了对国家列的筛选,那外层还筛个毛线呀?

各位讲师,你们肯定不服气,我们继续:
DAX圣经讲下面这个度量值是这样讲的,它说:使用Keepfilters后,calculate不会覆盖之前的筛选上下文。下面这个度量值取两个筛选器的交集。
美国人数 =
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),KEEPFILTERS('Sheet1'[国家] in {"美国","英国"})),
'Sheet1'[国家] in {"美国","加拿大"}
)
DAX神功解释:DAX函数全部都是由内向外,现在证明给你看
度量值【美国人数】内层Calculate筛选器使用了keepfilters后,恢复了筛选功能。


我们在内层Calculate筛选后的表上再筛选外层的 "美国"和"加拿大",你说是不是只能筛选出美国人数?因为使用了keepfilters后这个允许我继续筛选!是不是由内向外!我英语不如各位收费讲师,口语不如小学生,但是我看书不止看文字,我要动脑子,你们可能说我炫理解,就跟编程一样,没有谁一上来就写出正确的代码,都是在解决BUG中不断成长。
我们再拓展一下:为什么下面的代码就只能看到美英人数
美英人数 =
CALCULATE(
CALCULATE(SUM(Sheet1[人数]),'Sheet1'[国家] in {"美国","英国"}),
KEEPFILTERS('Sheet1'[国家] in {"美国","加拿大"})
)


内层筛选使用的是布尔值,就相当于国家列禁筛选,你外层只要筛选国家列,统统不可以,最终的答案是不是 “英国” 和 “美国” 的人数?

《DAX神功》文字版合集:
https://www.bilibili.com/read/readlist/rl442274
《DAX神功》视频版合集:
https://www.bilibili.com/video/BV1YE411E7p3
PowerBI(DAX函数)、PowerQuery(M函数)、Python办公自动化、Python爬虫、Python数据分析、ExcelVBA、WordVBA、AccessVBA、MySQL等等
https://www.bilibili.com/read/cv10222110