https://zhuanlan.zhihu.com/p/75462046
對於初學者,總是會把度量值和計算列搞混,我也經常碰到這樣的問題,有些星友用文章中的代碼總是報錯,發給我一看,才知道TA把本來是度量值的DAX用在了計算列上,或者相反。
即使是已經學習了很長時間的人,對於計算列和度量值的寫法也未必輕車熟路,還是有可能不明白其中到底有什么不同,這篇文章就試圖把度量值和計算列徹底講清楚。
什么是計算列、什么是度量值
計算列是直接在現有的表上添加一個新列,
新建的計算列,和表中之前已經存在的列一樣,可以作為表格的行、列,圖表的軸以及切片器的字段等等,也可以使用計算列與其他表建立關系。
而度量值,如果不放到上下文環境中,它就是一個孤立的公式,並沒有視覺上的計算結果,因此,很多人會覺得度量值比較難以理解,尤其是Excel用習慣的人,都會傾向於使用計算列。
字段標識不同
如下圖的字段面板
倒數第二個字段,前面有fx符號,代表的是計算列;而最下面一個,前面是個計算器的標識,表明[最后訂單日期]是一個度量值。
怎么寫計算列、怎么寫度量值?
這個才是大家更關注的問題,也是平時容易出錯的地方。下面以一個實例來介紹計算列與度量值的寫法有什么不同。
以這個簡化的訂單表為例,如何計算出每個客戶的最后下單時間?
計算列的寫法
不少人也許直接這樣寫,
結果都是訂單表中最后一個訂單日期,顯然與我們的期望不符,為什么會這樣呢?
計算列的上下文就是當前行,但是MAX函數(包括SUM、MIN等聚合函數)並不能將行上下文轉化為篩選上下文,也就是說當前行沒有起到篩選作用,它就會返回整個表的最大訂單日期。
關於上下文,不要理解的太復雜,完全可以按中文字面意思來理解,簡單來說就是當前的計算環境,一般分為行上下文和篩選上下文,行上下文可以簡單的理解為當前行,行上下文並不會必選產生篩選作用,只有篩選上下文才會產生篩選作用,篩選上下文有很多種形式,比如表格的當前行,矩陣的行和列、圖表的坐標軸、報表的切片器、篩選器等。
如何讓當前行發生篩選作用呢?在MAX函數外圍再套個CALCULATE函數就可以了,
這樣返回的下單日期都不同了,但是仔細看就會發現,返回的日期和當前行的下單日期是一樣的,並沒有按照客戶進行計算。
再強調一遍,計算列的上下文就是當前行,包括當前行的每一個值,在上表中,每一行的數據是唯一的,因此只能返回當前行的訂單日期。
那么到底如何按照客戶來計算,返回當前客戶的最后下單日期呢?
解決這個問題的DAX寫法有很多種,這里用個相對簡潔的代碼,
ALLEXCEPT函數的意思是,清除訂單表其他列的篩選作用,只保留客戶列的篩選,這樣當前行的上下文,其他列的篩選作用清除了,只有客戶發生篩選作用。
比如客戶甲,有兩筆訂單,使用MAX計算出最大的訂單日期,也就是甲的最后下單日期。
上面說了這么多計算列的寫法,那么度量值該怎么寫呢?
最后下單日期=MAX('訂單表'[訂單日期])
是不是簡單到不敢相信,那么如何看到計算結果呢,最簡便的方式就是直接用個表格或者矩陣展現結果,
再舉個例子,依然是這個訂單表,如何計算每個產品的最后賣出日期?
如果你理解了上面計算列的原理,這個問題是不是也很容易就出來了,
那么度量值又是如何寫才能解決這個問題的呢?
其實度量值的寫法可以完全不變,依然可以用上面的代碼(MAX('訂單表'[訂單日期]),只要把表格的維度改為產品名稱就可以了,
通過上面兩個問題,你看出來計算列和度量值有哪里不同了嗎?
在計算列中,其上下文是明確的,就是當前行,並且可以根據需要縮小為當前行的某一列;
而度量值,編寫時並沒有明確的上下文,度量值寫好之后,可以放在任意的上下文環境中,執行不同的計算;
計算列寫好代碼,數據自動計算出來,並且不受外部篩選器的影響;
而度量值寫好之后,並沒有立即計算,只有放到上下文中時,才進行計算並返回結果。
所以利用DAX解決問題時,首先應想到上下文是什么,新建列時如何利用當前行的上下文?
而度量值雖然暫時沒有上下文,但在建度量值之前,也首先應該想清楚,我需要構建怎樣的上下文環境才能得到期望的結果,然后利用心中所想的上下文環境,來對應的編寫度量值的DAX代碼。
在PowerBI中,除了計算列和度量值,還有個地方可以用到DAX,就是使用DAX新建表,
使用建表的方式同時可以解決上面的問題,
其中VALUES('訂單表'[客戶]),返回客戶的不重復列表,就是為了給后面的代碼提供上下文,有了這個上下文,后面的代碼與新建列的寫法是一樣的。
通過這三種使用DAX的環境,以及上面的幾個例子,應該能發現,無論是度量值、計算列還是建新表,其DAX代碼本質上並沒有什么不同,不同的只是下上文而已。
計算列只能利用當前行上下文;新建表可以使用DAX構建上下文;’而度量值最靈活,先不管上下文,寫好代碼之后,給什么上下文,就執行什么運算,或者說需要什么計算,就給度量值提供什么上下文。
你以后再遇到問題打算用DAX實現,一定要先想清楚,是通過計算列、新表還是度量值來解決,然后再開始寫對應的DAX。
計算列和度量值寫法的不同點
關於計算列和度量值在寫法上有一些不同,比如在計算列中使用列,可以直接使用,默認就是該列的當前行,比如返回當前訂單日期的前一天,可以直接寫:
而在度量值中使用列,必須在列上套一個聚合函數,否則不能自動轉換為篩選上下文,計算訂單日期前一天的度量值應該這樣寫:
訂單日期前一天 = MAX('訂單表'[訂單日期])-1
什么時候使用新建列,什么時候使用度量值
一般來說,為了性能考慮,能用度量值解決的問題就盡量不用計算列(參考:高效使用Power BI的15條建議)。只有當你需要利用新建的計算列生成切片器(度量值不能用於切片器),或者需要用這個字段與其他表建立關系時,才需要新建列。
另外當需要創建很復雜的字段時,盡量使用計算列,因為它需要時間來運算,提前運算完成,而不是在需要用的時候再進行計算(度量值是用的時候才計算),可以帶來更流暢的體驗。
總結
計算列和度量值在DAX代碼的寫法上並沒有本質不同,形式上的不同只是由於上下文環境的區別引起的。
二者的本質不同在於其運算特點,是否根據上下文動態運算、以及計算時間上的不同,才是根本的差異。
如果你看到這里還是有不明白的地方,那么從頭再仔細看一遍吧。