PowerBI之DAX神功:答網(wǎng)友問04 懶惰計(jì)算與迭代函數(shù)轉(zhuǎn)換上下文
Ps:函數(shù)內(nèi)層、外層行數(shù)與轉(zhuǎn)換問題,我們上節(jié)《DAX神功》第2卷第2回:理解迭代函數(shù)的行數(shù)中做了詳細(xì)解釋。
提問網(wǎng)友的需求:獲取每個(gè)月份最大銷售金額及產(chǎn)生最大金額的銷售日期


需求結(jié)果:

前景回顧:
上節(jié)課我們介紹了兩種度量值寫法:
銷售峰值1 = maxx('日期表',calculate(sumx('銷售表',RELATED('商品表'[售價(jià)])*'銷售表'[銷量])))
銷售峰值2 = maxx('日期表',sumx(RELATEDTABLE('銷售表'),RELATED('商品表'[售價(jià)])*'銷售表'[銷量]))
度量值還可以怎么寫?
銷售金額 = sumx('銷售表',RELATED('商品表'[售價(jià)])*'銷售表'[銷量])
銷售峰值3 = maxx('日期表',[銷售金額])?
因?yàn)椤句N售金額】是度量值,等同于外面套了一個(gè)Calculate,度量值將行上下文轉(zhuǎn)成篩選上下文。
至此網(wǎng)友提問的第一問回答完畢:獲取每個(gè)月份最大銷售金額

正片開始:
網(wǎng)友提問:《The Definitive Guide to DAX》中關(guān)于求銷售峰值發(fā)生日期的公式不理解
也是我們今天問題的第2問:產(chǎn)生最大金額的銷售日期
發(fā)生日期 =
var x=[銷售峰值3]??
? ? ? ? var tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=x)
return
? ? ? ?IF(COUNTROWS(tb)=1,tb,BLANK())

第1步:理解度量值
銷售金額 = sumx('銷售表',RELATED('商品表'[售價(jià)])*'銷售表'[銷量])
// 銷售表每一行的銷量與商品表對應(yīng)的商品售價(jià)相乘的結(jié)果,這個(gè)【銷售金額】是度量值,它將sumx從行上下文轉(zhuǎn)換成了篩選上下文。

銷售峰值3 = maxx('日期表',[銷售金額])?
// 大多數(shù)人,是因?yàn)椴焕斫膺@個(gè)公式,但是他不知道問題出在這里,而是直接研究度量值【發(fā)生日期】,造成看不懂。
先寫出【銷售峰值3】的等價(jià)公式:
銷售峰值3等價(jià) =?maxx('日期表',calculate(sumx('銷售表',RELATED('商品表'[售價(jià)])*'銷售表'[銷量])))?
內(nèi)層:

內(nèi)層的:calculate(sumx('銷售表',RELATED('商品表'[售價(jià)])*'銷售表'[銷量]))
其實(shí)就是一個(gè)值12,但是這個(gè)值具有篩選功能。
外層:
銷售峰值3等價(jià)?=?maxx('日期表',calculate(12))

如果我們使用字段【月】,來篩選銷售峰值3等價(jià)

現(xiàn)在我們需要知道,每個(gè)月銷售峰值出現(xiàn)在哪一天?
第2步:理解篩選表
// 因?yàn)槲覀冃枰淖畲箐N售金額發(fā)生日期這個(gè)度量值是一個(gè)將日期表中最大銷售金額所在的日期返回成標(biāo)量值?!禗AX神功》第1卷第10回 將表用做標(biāo)量值,所以用到Values也可以使用DISTINCT。

其實(shí)就是找到3和5那兩個(gè)日期,就是每個(gè)月最大金額的產(chǎn)生日期
表1=FILTER(VALUES('日期表'[日期]),[銷售金額]=3)

表2=FILTER(VALUES('日期表'[日期]),[銷售金額]=5)

表3 = FILTER(VALUES('日期表'[日期]),[銷售金額])
// 有銷售金額的只有這4個(gè)日期,表1和表2就是從這4個(gè)日期中找出3或5的日期

表4 = FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額]))

// 因?yàn)楝F(xiàn)在我們只是新建表,并沒有參與篩選,所以它顯示的是maxx('日期表',[銷售金額])的值5所對應(yīng)的日期2020/2/1。為了證明:我們將表4放到度量值中
證明1 = CALCULATE([銷售金額],FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額])))
證明2 = Countrows(FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額])))

所以,我們【發(fā)生日期】度量值是這樣來寫的:
詳見《DAX神功》第1卷第10回 將表用做標(biāo)量值? ?對此做出了詳細(xì)講解
發(fā)生日期 =
IF(
COUNTROWS(FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額])))=1,
FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額])),
BLANK()
)
// 例如1月最大值是3,如果只有一行,我們就返回那個(gè)日期,否則返回空
// 有些人問?為什么會有否則?假設(shè)1月有5天都是3,且最大值為3,那你就不能返回哪天是最大值,這個(gè)日期也不只一行,所以返回空。
第一次簡化公式:
詳見《DAX神功》第2卷第1回 VAR變量
發(fā)生日期 =
? ? ? ? var tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額]))
return
? ? ? ? IF(COUNTROWS(tb)=1,tb,BLANK())
第二次簡化公式:
發(fā)生日期 =
var x=[銷售峰值3]
var tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=x)
return
IF(COUNTROWS(tb)=1,tb,BLANK())
// 為什么不能直接寫成??tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=[銷售峰值3])?
我們看看這種情況會發(fā)生什么?
表5 = FILTER(VALUES('日期表'[日期]),[銷售金額]=[銷售峰值3])

tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額]))
maxx('日期表',[銷售金額])是行上下文,
而[銷售峰值3]是篩選上下文,我們可以利用懶惰計(jì)算的特性:
<1> 沒有被使用過的變量永遠(yuǎn)不會被計(jì)算
<2> 當(dāng)變量完成了首次計(jì)算,則它不會在同一范圍內(nèi)被再次計(jì)算。
var x=[銷售峰值3]
var tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=x)
這時(shí)只計(jì)算,[銷售金額]=[銷售峰值3],而[銷售峰值3]是由maxx('日期表',[銷售金額])生成的
其實(shí)就是?[銷售金額]=maxx('日期表',[銷售金額])
如果你寫成:[銷售金額]=[銷售峰值3],那就相當(dāng)于:
[銷售金額]=Calculate(maxx('日期表',[銷售金額]))
如果你對惰性計(jì)算不熟悉,我建議這樣寫:
發(fā)生日期?=
var?x=maxx('日期表',[銷售金額])
var?tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=x)
return
IF(COUNTROWS(tb)=1,tb,BLANK())
或者
發(fā)生日期?=
var?tb=FILTER(VALUES('日期表'[日期]),[銷售金額]=maxx('日期表',[銷售金額]))
return
IF(COUNTROWS(tb)=1,tb,BLANK())

《孫興華講PowerBI火力全開》PowerBI必學(xué)課程
https://www.bilibili.com/video/BV1qa4y1H7wp
《DAX神功》文字版合集:
https://www.bilibili.com/read/readlist/rl442274
《DAX神功》視頻版合集:
https://www.bilibili.com/video/BV1YE411E7p3
PowerBI(DAX函數(shù))、PowerQuery(M函數(shù))、Python辦公自動(dòng)化、Python爬蟲、Python數(shù)據(jù)分析、ExcelVBA、WordVBA、AccessVBA、MySQL等等
https://www.bilibili.com/read/cv10222110