《DAX權(quán)威指南》簡體版 (第四章)01

第04章:基本列表篩選?

介紹

通過前面幾章的介紹,你已經(jīng)了解了DAX語言的基礎(chǔ)知識(shí)。知道如何創(chuàng)建計(jì)算列和度量,并且對(duì)在DAX中使用的常用函數(shù)有了較好的理解。

本章中,將轉(zhuǎn)到DAX更高一級(jí)水平知識(shí):即學(xué)習(xí)DAX綜合語言的一個(gè)堅(jiān)實(shí)的理論背景,之后,你就能成為一個(gè)真正的DAX高手了。

到目前為止,通過所獲得知識(shí)的了解,可以創(chuàng)建許多有用的業(yè)務(wù)報(bào)告。但是,還需要學(xué)習(xí)列表篩選(行篩選和列篩選),以便創(chuàng)建更復(fù)雜的報(bào)告。

列表篩選是DAX中所有高級(jí)特征的基礎(chǔ)。

這里有幾句提醒:列表篩選是一個(gè)簡單的概念,你很快就會(huì)學(xué)習(xí)并理解它。不過,還需要基于一些微妙的細(xì)節(jié)考慮,以便徹底了解它。否則,在你的DAX學(xué)習(xí)路徑中,你會(huì)隨時(shí)都會(huì)感到迷茫。

我們?cè)诠埠退饺苏n程中向許多用戶教授DAX。我們知道這是絕對(duì)正常的。在某種程度上,你會(huì)覺得公式像魔法一樣有效,因?yàn)樗鼈儗⑵鸬綉?yīng)有的作用。

但是,你可能并不明白這其中的內(nèi)部原因是什么。不要擔(dān)心:你將在一個(gè)好公司里信任你的工作,大多數(shù)DAX商學(xué)院的學(xué)生都達(dá)到了這一水平,許多其他學(xué)生將來也會(huì)達(dá)到這個(gè)目標(biāo)。

首先,這僅僅意味著列表篩選對(duì)他們來說還不夠清晰。在這一點(diǎn)上,解決方案很簡單:讀完本章后,再次回到本章,再讀一遍,你可能會(huì)發(fā)現(xiàn)一些在你第一次讀到時(shí)錯(cuò)過了的新東西。

譯者:這是事實(shí),許多人就停留在看第一篇。這個(gè)簡體譯本,我是2018年9月份就完成了的,期間也只是看了一遍,現(xiàn)在回過頭來,發(fā)現(xiàn)好多理解還是不對(duì)。

此外,一方面,函數(shù)CALCULATE在使用列表篩選時(shí)起著重要的作用,這可能是DAX里最強(qiáng)大、最難以學(xué)習(xí)的函數(shù)。我們將在第5章?“理解CALCULATE?和?CALCULATETABLE中介紹”,然后在本書的其余部分也將使用到它。所有這些,如果沒有扎實(shí)的列表篩選背景,理解CALCULATE會(huì)有難度。

譯者:參閱本人《CALCULATE庖丁解牛108式》系列(未完)。大多數(shù)內(nèi)容也是來源于《DAX圣經(jīng)》,但做了最通俗的、啰嗦式講解。這里已經(jīng)引出了DAX的兩個(gè)做核心的知識(shí):篩選+計(jì)算

另一方面,在不嘗試實(shí)驗(yàn)、建立在實(shí)際操作的情況下理解列表篩選的重要性,并使用CALCULATE幾乎是不可能的。

因此,這一章和隨后的一章,是我們?cè)谝郧暗臅幸苍?jīng)寫過的,你需要標(biāo)記出來,或者把書頁的角折起來作為記號(hào)。

DAX的計(jì)算環(huán)境介紹

首先,讓我們從理解什么是當(dāng)前列表篩選開始。

任何DAX表達(dá)式都在一個(gè)篩選條件下進(jìn)行計(jì)算。篩選是公式計(jì)算所在的“環(huán)境”。

例如,考慮一個(gè)很簡單的公式:例如?[Sales Amount]度量:

[Sales Amount]?:=?SUMX?( Sales,?Sales[Quantity] * Sales[UnitPrice] )

你已經(jīng)知道,這個(gè)公式在銷售表中(不涉及其他表)計(jì)算[Quantity]?*?Sales[UnitPrice]--數(shù)量乘以價(jià)格的值的總和。可以將該度量拖入透視表中,并查看結(jié)果,如圖4-1所示:

圖4-1在沒有篩選的情況下,[Sales Amount]度量顯示了銷售總額。

這個(gè)數(shù)字看起來沒有一點(diǎn)意義,對(duì)吧??但是,如果你能夠仔細(xì)想想,該公式它計(jì)算的到底是什么?它計(jì)算的應(yīng)該是:銷售額的總和。這是一個(gè)最大的數(shù)字(總數(shù)),這并沒有實(shí)際意義。

但是,當(dāng)我們使用一些列來分解這個(gè)總數(shù),并處理這個(gè)分解的結(jié)果時(shí),這個(gè)數(shù)據(jù)透視表就開始變得更有用了。例如,可以使用產(chǎn)品顏色,將其放在行中,而透視表(數(shù)值區(qū))會(huì)突然顯示出一些有意義的業(yè)務(wù)洞察,如圖4-2所示:

圖4-2按顏色劃分后,[Sales Amount]的值看起來更有實(shí)際意義了。

總數(shù)(最后一行總計(jì)—Grand Total)還在,但現(xiàn)在,每個(gè)行值顯示了更小的值([Sales Amount]列的每個(gè)行值)以及每個(gè)值的總和(Grand Total),這些值都有一個(gè)實(shí)際意義。

然而,如果你再仔細(xì)想想,應(yīng)該注意到,這個(gè)奇怪的事情的發(fā)生,同時(shí)也是我們心中的疑問:[Sales Amount]度量,現(xiàn)在已經(jīng)沒有按我們想象的方式在計(jì)算。

本來我們認(rèn)為,這個(gè)公式是計(jì)算“所有銷售額的總和--[Sales Amount]的總數(shù)”,但在數(shù)據(jù)透視表的每個(gè)值單元中,公式結(jié)果并不是計(jì)算所有銷售的總和,而是計(jì)算了具有特定COLOR--顏色行所對(duì)應(yīng)的每個(gè)products-產(chǎn)品的銷售總額。

然而,問題是,我們自始至終都沒有指定(定義)計(jì)算必須在數(shù)據(jù)模型的該子集上工作。換句話說,并沒有明確指定公式按這種方式來處理數(shù)據(jù)的子集。更通俗的講:你并沒有這種想法或按這個(gè)想法向DAX下達(dá)過任何一個(gè)該如何執(zhí)行的命令。

但是,為什么公式會(huì)在不同的行(透視表單元格)中卻計(jì)算出了不同的值呢?

答案很簡單,事實(shí)上:因?yàn)镈AX計(jì)算公式是在當(dāng)前計(jì)算的環(huán)境下(當(dāng)前篩選環(huán)境下)。你可以把一個(gè)公式的當(dāng)前列表篩選,看作是公式計(jì)算所在的單元區(qū)域(當(dāng)前列表環(huán)境)。

由于product color--產(chǎn)品顏色被放置在透視表的行上,因而透視表中的每一行上將出現(xiàn)所有的product color列值,相當(dāng)于從整個(gè)數(shù)據(jù)庫中,篩選出了該產(chǎn)品的子集:即特定的color--顏色(每一行是一種顏色,而不是整個(gè)顏色)。這才是公式所在的列表環(huán)境區(qū)域(當(dāng)前列表子集),計(jì)算就發(fā)生在這個(gè)列表子集里。

也就是說,這是在公式計(jì)算之前,就已經(jīng)應(yīng)用于數(shù)據(jù)庫的一組篩選器。

當(dāng)公式計(jì)算?[Sales Amount]時(shí),其實(shí)際還是計(jì)算其所有值,但這時(shí)候,因?yàn)椴]有查詢整個(gè)數(shù)據(jù)模型所有行的選項(xiàng),因而,它不會(huì)在整個(gè)數(shù)據(jù)庫中計(jì)算(計(jì)算整個(gè)值)。比如,當(dāng)DAX公式計(jì)算到行值為White--白色所對(duì)應(yīng)的行時(shí),只有White白色的產(chǎn)品是可見的,因此,計(jì)算只針對(duì)那些白色產(chǎn)品所對(duì)應(yīng)的銷售,計(jì)算透視表中的其他任何不是白色產(chǎn)品的行都被忽略(即其他顏色的行是被篩選掉了的),這時(shí)候,?[Sales Amount]?實(shí)際還是計(jì)算其“所有值”,只不過,現(xiàn)在這個(gè)“所有值”已經(jīng)成為只有白色產(chǎn)品的[Sales Amount]。

譯者:原文的該處,比較拗口。因?yàn)檫@段話比較重要,稍作了一點(diǎn)通俗、簡體式修改。希望更容易理解。

任何一個(gè)DAX公式都可以理解為:定義一個(gè)計(jì)算,但是,DAX在一個(gè)當(dāng)前篩選中對(duì)這個(gè)計(jì)算進(jìn)行了改變,從而定義并決定了DAX最終的計(jì)算值,這就是一個(gè)數(shù)據(jù)模型里DAX公式的奧秘和強(qiáng)大之處:

相同的DAX公式,可能會(huì)產(chǎn)生不同的結(jié)果值。因?yàn)镈AX針對(duì)的是不同的數(shù)據(jù)子集在計(jì)算。

唯一的情況下,如果公式的行為方式已經(jīng)被定義為Grand total—求全局最大的總數(shù)。在這個(gè)級(jí)別上,因?yàn)闆]有篩選,將針對(duì)整個(gè)數(shù)據(jù)模型計(jì)算。

請(qǐng)注意

為了簡單起見,在這些示例中,我們使用了一個(gè)透視表。顯然,也可以使用查詢定義一個(gè)當(dāng)前列表篩選(這將在后續(xù)的章節(jié)中進(jìn)一步介紹)。現(xiàn)在,最好保持該簡單方式,只考慮數(shù)據(jù)透視表,以便對(duì)概念有一個(gè)簡化、直觀的理解。

現(xiàn)在,讓我們把[YEAR]-年份放置到透視表的列上,使透視表更豐富一些。該報(bào)告如圖4-3:

圖4-3銷售金額現(xiàn)在已按[Color]-顏色和[year]-年份劃分。

首先,通過前面說明的DAX規(guī)則,應(yīng)該清楚的一點(diǎn): 即使任然還是同一個(gè)公式,現(xiàn)在每個(gè)值單元里已經(jīng)都顯示出不同的值。這是因?yàn)椋瑪?shù)據(jù)透視表被行和列兩個(gè)篩選條件所定義。顯然,2008年白色產(chǎn)品的銷售與2007年的白色產(chǎn)品的銷售不可能是兩個(gè)相同的值。

此外,因?yàn)榭梢酝瑫r(shí)在行和列中放置更多個(gè)字段,所以最好表述應(yīng)為:透視表行上的字段集和列上的字段集共同定義出篩選子集。

圖4-4說明了這一點(diǎn):

圖4-4?篩選是由行和列上的字段共同定義的子集。

表中每個(gè)單元格都有不同的值。因?yàn)樾猩嫌袃蓚€(gè)字段,[color[?和?[brand] name-顏色和品牌名稱列。行和列上的完整字段定義了篩選計(jì)算子集。

例如,圖4-4中突出顯示的單元格的行列篩選對(duì)應(yīng)于:

color=Black;
brand=Contoso
以及Calendar=Year 2007。

請(qǐng)注意

字段是否在行上或列上(或在切片器/頁面篩選器上,或在任何其他類型的篩選器中,甚至是由查詢創(chuàng)建)這都不重要。重要的是:所有這些篩選器都只是為了有助于定義出一個(gè)單一的當(dāng)前篩選,這是DAX公式用來計(jì)算的集。將字段放置在行或列上(任何篩選),只是一種布局方式的結(jié)果,DAX計(jì)算值的方式?jīng)]有任何變化。

? ? ? ? 譯者:初學(xué)者往往把DAX計(jì)算值的方式(總是按固定的一種方式計(jì)算,這是由內(nèi)部引擎的性質(zhì)決定的)與篩選方式混淆。正如前面所說:DAX計(jì)算值的方式永遠(yuǎn)沒有變化—總是計(jì)算全集的值—計(jì)算一整列的值和計(jì)算只有一行的列值并沒有什么區(qū)別。這在后續(xù)的高級(jí)列表篩選計(jì)算中將進(jìn)一步加深學(xué)習(xí)。也可以參閱本人的《CALCULATE的108式》系列。??

? ? ? ?現(xiàn)在,讓我們看看整個(gè)透視表。在圖4-5中,我們?cè)谝粋€(gè)切片器上添加了產(chǎn)品類別-Catagory,在篩選器上添加了monch-月份名稱,并選擇了12月份。

圖4-5在一個(gè)典型報(bào)告中,篩選由許多方面所定義,包括切片器和篩選器。

? ? ? ?顯然,在這一點(diǎn)上,計(jì)算每個(gè)單元格中的值具有由行、列、切片器和篩選器等共同定義的當(dāng)前列表篩選。所有這些篩選器都有助于在公式計(jì)算之前將DAX應(yīng)用于數(shù)據(jù)模型的當(dāng)前列表篩選定義。

此外,重要的是要了解,不是所有的單元格都具有相同的篩選器子集,不僅是在值方面,而且在字段方面也是如此。

例如,列上的Grand total只包含類別、月份以及年份的篩選器,但是它并不包含[color[?和?[brand]--顏色和品牌的篩選器。因?yàn)轭伾推放频淖侄卧谛猩希鼈儾]有篩選Grand total--總數(shù)。這同樣適用于數(shù)據(jù)透視表中的顏色小計(jì):這些單元格值沒有篩選制造商,唯一有效的篩選來自行的Color-顏色。

我們將此篩選稱為列表篩選,顧名思義,它篩選表或列表。

任何一個(gè)公式,由于你的業(yè)務(wù)維度需求不同都會(huì)有不同的值,這取決于DAX用來進(jìn)行計(jì)算的當(dāng)前篩選列表。這種行為雖然很直觀,但需要去很好地理解它。

現(xiàn)在,已經(jīng)了解了什么是一個(gè)當(dāng)前列表篩選,知道以下DAX公式表達(dá)式應(yīng)該解讀為:“當(dāng)前篩選條件下可見的銷售金額的總和”:

[Sales Amount]?:=?SUMX?( Sales,?Sales[Quantity] * Sales[UnitPrice] )

稍后將學(xué)習(xí)如何讀取、修改和清除列表篩選。到目前為止,已經(jīng)足夠了解這個(gè)事實(shí),即當(dāng)前列表篩選始終存在于數(shù)據(jù)透視表的任何單元格或報(bào)告/查詢中的任何值里。你總是需要考慮當(dāng)前列表篩選,以便理解DAX公式是如何計(jì)算的。

了解行篩選

列篩選是在DAX中存在的兩個(gè)篩選中的一個(gè),它的伙伴則是行篩選,在本節(jié)中,將了解它是什么?以及它是如何工作的。這次,我們使用一個(gè)不同的公式:

Sales[GrossMargin] =?Sales[SalesAmount] - Sales[TotalCost]

你可能會(huì)在計(jì)算列中,按順序?qū)懗鲞@樣的表達(dá)式:計(jì)算[GrossMargin]-毛利。只要在計(jì)算列中定義這個(gè)公式,就會(huì)得到結(jié)果列表,如圖4-6所示:

圖4-6 Sales[GrossMargin]的結(jié)果計(jì)算了表的所有行。

圖示結(jié)果顯示DAX公式計(jì)算了表中所有行的值,或者說,對(duì)于每一行,它計(jì)算的值與預(yù)期的不同。

為了理解這個(gè)行環(huán)境,在閱讀時(shí),我們需要先使用比較笨的計(jì)算公式:即要求兩列相減,但我們告訴DAX具體是列表中的哪一行的值相減了嗎??沒有!你可能會(huì)說,這時(shí)使用的行是隱式的,因?yàn)樗且粋€(gè)計(jì)算列,因此,DAX將逐行計(jì)算,對(duì)于每一行,它都將得出一個(gè)不同的結(jié)果值,這中理解是正確的。但是,從DAX表達(dá)式的角度來看,關(guān)于具體使用哪一行的信息仍然缺失(譯者注:并沒有定義或聲明)。

實(shí)際上,這里用于執(zhí)行計(jì)算的行并不存儲(chǔ)在公式內(nèi)部。它是由另一種篩選定義:行篩選。

當(dāng)定義某個(gè)計(jì)算列時(shí),DAX從表的第一行開始迭代:它創(chuàng)建了包含該行的行值篩選(譯者注:更好的稱呼應(yīng)該為:值列表篩選),并對(duì)表達(dá)式進(jìn)行了計(jì)算。然后它移到第二行,再次對(duì)表達(dá)式求值,依此循環(huán),直到計(jì)算完表中的所有行。如果有一百萬行,你可以認(rèn)為,DAX運(yùn)行了100萬次行篩選來計(jì)算公式100萬次。

顯然,為了優(yōu)化計(jì)算,這并不是實(shí)際發(fā)生的情況:否則,DAX將是一種非常緩慢的語言。

但是,不管怎樣,從邏輯的角度來看,這就是它的工作原理。

讓我們?cè)囍鼫?zhǔn)確些解釋它:行篩選是一個(gè)始終包含一行的篩選,而DAX在創(chuàng)建過程中會(huì)自動(dòng)定義它。比如計(jì)算列,可以使用其他技術(shù)創(chuàng)建一個(gè)行篩選,這將在本章后面討論,但是,解釋行篩選的最簡單方法是查看計(jì)算列,在這些列中,引擎總是自動(dòng)創(chuàng)建它。

行、列篩選關(guān)系共存

到目前為止,你已經(jīng)了解了行篩選和列表篩選是什么。它們是構(gòu)建DAX唯一的計(jì)算環(huán)境。因此,它們也是修改公式結(jié)果的唯一方法。

任何DAX計(jì)算公式都包含這兩個(gè)不同的篩選:行篩選和列篩選環(huán)境。

我們將這兩個(gè)篩選稱為“計(jì)算篩選”,因?yàn)樗鼈兪歉淖児降挠?jì)算方式的篩選,即為相同的公式提供不同的結(jié)果。

一個(gè)非常重要的問題是:你很難同時(shí)思考兩個(gè)篩選,但公式的結(jié)果卻又總是取決于這兩種篩選。在這一點(diǎn)上,你可能認(rèn)為這是顯而易見的,非常自然的,在你學(xué)習(xí)DAX的過程中,也許經(jīng)常會(huì)遇到,這種想法也許是對(duì)的。

但是,在后面章節(jié)內(nèi)容中,你會(huì)發(fā)現(xiàn),如果你不記得兩個(gè)篩選的共存關(guān)系,即其中的任一種篩選都可能會(huì)改變公式的結(jié)果,這對(duì)于你今后書寫DAX將是一大挑戰(zhàn)。

測(cè)試對(duì)行、列表篩選的理解

在我們繼續(xù)討論關(guān)于計(jì)算篩選的更復(fù)雜的討論之前,我們希望通過幾個(gè)例子來測(cè)試對(duì)篩選的理解。請(qǐng)不要立即看解釋:在問題結(jié)束后,試著回答它。然后閱讀解釋,這樣更會(huì)有意義。

計(jì)算列中使用SUM(聚合計(jì)算)

第一個(gè)測(cè)試非常簡單。如果你用這個(gè)代碼定義一個(gè)計(jì)算列,在銷售中,會(huì)發(fā)生什么?

Sales[SumOfSalesAmount] =?SUM?( Sales[SalesAmount] )

因?yàn)樗且粋€(gè)計(jì)算列,對(duì)于每一行,它將逐行計(jì)算,將得到一個(gè)個(gè)結(jié)果。你想看什么號(hào)碼??從這些選項(xiàng)中選擇一個(gè):

(1)這一行的SalesAmount的值,即每一行的不同值:
(2)所有行的總銷售額,即所有行的 一個(gè)相同值:
(3)一個(gè)錯(cuò)誤:不能在計(jì)算列中使用SUM。

請(qǐng)停止閱讀,我們等待你的猜測(cè)結(jié)果,然后繼續(xù)。

現(xiàn)在,讓我們?cè)敿?xì)說明當(dāng)這個(gè)DAX公式計(jì)算時(shí)發(fā)生了什么。

你已經(jīng)學(xué)會(huì)了這個(gè)公式的意義是什么:“計(jì)算當(dāng)前篩選條件下的銷售金額總和”。因?yàn)檫@是在一個(gè)計(jì)算列中,DAX函數(shù)公式逐行計(jì)算。因此,它為第一行創(chuàng)建一個(gè)行篩選,然后調(diào)用公式再次計(jì)算,一直到遍歷整個(gè)表的所有行。

? ? ? ? 既然公式計(jì)算了當(dāng)前篩選中的所有銷售額的總和,所以真正的問題是:這個(gè)“當(dāng)前的篩選是什么?”“答:完整的數(shù)據(jù)模型表。因?yàn)楫?dāng)前計(jì)算環(huán)境下,并沒有除DAX計(jì)算公式之外的任何數(shù)據(jù)透視表或任何其他類型的篩選。實(shí)際上,當(dāng)沒有活動(dòng)篩選存在時(shí),DAX將計(jì)算列(公式所在列)作為定義的一部分計(jì)算。

? ? ? ?即便有行篩選,SUM()也會(huì)忽略它。相反,它使用的是列表篩選,而當(dāng)前篩選現(xiàn)在是完整的數(shù)據(jù)模型表(當(dāng)前整個(gè)列表)。因此,第二個(gè)選擇是正確的:你會(huì)得到一個(gè)相同的銷售總額(所有行的總值)。對(duì)于所有的銷售行,如在圖4-7中所看到的結(jié)果(都為一個(gè)總值)。

圖4-7 SUM (Sales[SalesAmount]),在計(jì)算列中,針對(duì)完整的數(shù)據(jù)模型表計(jì)算。

? ? ? 這個(gè)例子說明了這兩種篩選是共存的。它們都作用于公式從而改變公式的結(jié)果,但使用了不同的方式。

計(jì)算列中使用的SUM、MIN和MAX等聚合函數(shù)只使用列表篩選,而忽略行篩選,而DAX只使用該篩選(這時(shí)只有列表篩選)來確定列值。如果你選擇了第一個(gè)答案,就像許多初學(xué)者通常做的那樣,這是完全正常的思考。關(guān)鍵在于,還沒有想到這兩種篩選正在一起工作,并以各自不同的方式在改變公式結(jié)果。

使用直覺邏輯判斷,第一個(gè)答案是最常見的,但它卻是錯(cuò)誤的,現(xiàn)在你應(yīng)該知道為什么會(huì)這樣了吧?。

度量中使用列表

我們想做的第二個(gè)測(cè)試略有不同。假設(shè)你想要在一個(gè)度量中而不是在計(jì)算列中定義一個(gè)毛利公式。其中一列為銷售額,另一列為產(chǎn)品成本,你可能會(huì)寫出以下表達(dá)式:

[GrossMargin]?:=?Sales[SalesAmount] - Sales[ProductCost]

如果嘗試編寫這樣一種度量,你希望得到什么結(jié)果?

(1)這個(gè)表達(dá)式工作正常,我們需要測(cè)試其結(jié)果:
(2)結(jié)果報(bào)告為一個(gè)錯(cuò)誤,你甚至不能寫出這個(gè)公式:
(3)可以定義這個(gè)公式,但是當(dāng)在透視表或查詢中使用它時(shí),它會(huì)給出一個(gè)錯(cuò)誤。

如前所述,停止閱讀,思考答案,然后閱讀下面的解釋。

在公式中,我們引用了Sales[SalesAmount],這是一個(gè)列名,也就是你想告訴DAX:需要引用Sales表中SalesAmount列的值來計(jì)算。這個(gè)定義缺少什么呢??你應(yīng)該還記得,從前面的公式參數(shù)中,這里缺少的信息是:從哪里獲取到當(dāng)前[SalesAmount]列的行值?

當(dāng)你在計(jì)算列中編寫此代碼時(shí),由于行篩選(譯者注:為隱式行篩選),DAX這時(shí)候知道在計(jì)算表達(dá)式時(shí)要使用的行。但是,度量的結(jié)果是什么呢??沒有迭代,沒有當(dāng)前行,也就是說,沒有行篩選。

因此,第二個(gè)答案才是正確的。你甚至不能寫出該公式,它在語法上是錯(cuò)誤的,當(dāng)試圖計(jì)算它時(shí),將會(huì)收到一個(gè)錯(cuò)誤提示。

請(qǐng)記?。?/b>一個(gè)列本身并不具有每個(gè)行值(數(shù)據(jù)列表不具有行的概念)。相反,列表的每一行都有一個(gè)不同的值。因此,如果想要定義單個(gè)列值,則需要指定要使用的當(dāng)前行。而指定要使用的當(dāng)前行的唯一方法就是行篩選。因?yàn)樵谶@個(gè)度量中沒有行篩選,因而公式是錯(cuò)誤的,DAX拒絕計(jì)算。

指出這個(gè)計(jì)算的正確方法,是在某些情況下使用具有行篩選行為的聚合函數(shù),如:

[GrossMargin]?:=?SUM?( Sales[SalesAmount] ) - SUM?( Sales[ProductCost] )

使用此公式,現(xiàn)在要求通過SUM進(jìn)行聚合。因此,后一個(gè)公式不依賴于行篩選,它只需要一個(gè)列表篩選,并且它提供了正確的結(jié)果。

? ? ? ? 譯者:其實(shí)不依賴行篩選的說法欠妥。因?yàn)樾泻Y選與列表篩選總是共存的,否則公式無法計(jì)算。使用聚合類函數(shù)不需要考慮行篩選,是因?yàn)檫@類函數(shù)內(nèi)部封裝有被引擎識(shí)別的自動(dòng)執(zhí)行的隱式行篩選行為。后面將要見到。

使用迭代器創(chuàng)建行篩選

你已了解到,在定義計(jì)算列時(shí),DAX自動(dòng)創(chuàng)建了行篩選。在這種情況下,引擎將逐行計(jì)算DAX表達(dá)式?,F(xiàn)在,是時(shí)候?qū)W習(xí)如何使用迭代器在DAX表達(dá)式中創(chuàng)建行篩選了。

你可能還記得第2章“介紹DAX”,即所有的X-后綴類函數(shù)都是迭代器,也就是說,它們遍歷表并計(jì)算每一行的表達(dá)式,最后使用不同的算法聚合結(jié)果。例如,看看下面的DAX表達(dá)式:

[IncreasedSales]?:=?SUMX?( Sales,Sales[SalesAmount] *?1.1?)

由于SUMX是一個(gè)迭代器,它遍歷Sales表(函數(shù)第一個(gè)參數(shù)為某個(gè)表),對(duì)于表的每一行,它計(jì)算銷售額增加了10%(*1.1)的值,最終返回所有這些值的聚合。為了計(jì)算每一行的表達(dá)式,SUMX在Sales表上創(chuàng)建行篩選,并在整個(gè)迭代期間使用它。在包含當(dāng)前迭代行的行篩選中,DAX將計(jì)算內(nèi)部表達(dá)式(SUMX的第二個(gè)參數(shù):表達(dá)式)。

需要注意的是,在整個(gè)計(jì)算流程中,SUMX的不同參數(shù)使用了不同的篩選。讓我們深入分析一下這個(gè)表達(dá)式:

=?SUMX?(Sales,?←外部環(huán)境
Sales[SalesAmount]?*?1.1?← 外部篩選+新的行篩選。內(nèi)部環(huán)境

第一個(gè)參數(shù)Sales是引用來自數(shù)據(jù)模型的列表篩選?(例如,它可能是一個(gè)透視表單元,另一個(gè)度量,或查詢結(jié)果列表的一部分),而第二個(gè)參數(shù)(表達(dá)式)是使用外部篩選加上新創(chuàng)建的行篩選來計(jì)算的。

所有迭代器都具有相同的方式:

l?為第一個(gè)參數(shù)的表的每一行創(chuàng)建一個(gè)新的行篩選;
l?第二個(gè)參數(shù)為新創(chuàng)建的行篩選(以及在迭代開始之前存在的任何其他篩選),以此計(jì)算表的每一行:
l?聚合步驟2中計(jì)算的值。

重要的是要記住,原始的篩選表達(dá)式仍然存在并有效,迭代器只是添加了一個(gè)新的行篩選:它們不以任何方式修改現(xiàn)有的篩選。

這個(gè)規(guī)則通常是有效的,但有一個(gè)重要的例外:如果前面的情況下已經(jīng)包含一個(gè)相同的表的行篩選,則新創(chuàng)建的行篩選將隱式先前存在的行篩選。我們將在下一節(jié)中更詳細(xì)地討論這個(gè)問題。

使用?EARLIER

在同一個(gè)表上嵌套多個(gè)行篩選的場(chǎng)景可能看起來很少見,但實(shí)際上,這種情況卻經(jīng)常發(fā)生。讓我們以一個(gè)例子來看看這個(gè)概念。

假設(shè)你希望計(jì)算每一種產(chǎn)品的價(jià)格與其他產(chǎn)品的價(jià)格比較(比如出現(xiàn)的次數(shù)),這將產(chǎn)生一種基于價(jià)格的產(chǎn)品排名。

為了解決這個(gè)問題,我們使用了前一章學(xué)過的FILTER函數(shù)??赡苓€記得,F(xiàn)ILTER是一個(gè)迭代器,它循環(huán)遍歷表的所有行,并返回一個(gè)包含滿足第二個(gè)參數(shù)條件定義下的新表。例如,如果需要檢索表中產(chǎn)品價(jià)格高于100美元的產(chǎn)品,可以使用:

=?FILTER?( Product,?Product[UnitPrice]?>?100?)

請(qǐng)注意

細(xì)心的讀者會(huì)注意到,F(xiàn)ILTER是一個(gè)迭代器,因?yàn)镻roduct[UnitPrice] > 100這個(gè)表達(dá)式中,假如有且僅當(dāng)一個(gè)有效的行篩選存在于產(chǎn)品表中,就可以被直接計(jì)算。否則,單位價(jià)格的有效值將是不確定的。FILTER是一個(gè)迭代器函數(shù),它在第一個(gè)參數(shù)中為表的每一行都將創(chuàng)建一個(gè)行篩選,因而也會(huì)包含第二個(gè)參數(shù)中的計(jì)算條件,從而使得在這個(gè)條件下的行計(jì)算成為可能。

現(xiàn)在,讓我們回到我們最初的例子:創(chuàng)建一個(gè)計(jì)算列。

Product[UnitPriceRank]?=
COUNTROWS?(
FILTER?(Product,
Product[UnitPrice]?>?PriceOfCurrentProduct))?–大于當(dāng)前產(chǎn)品的結(jié)果

我們希望計(jì)算出那些比當(dāng)前價(jià)格高的產(chǎn)品的數(shù)量。如果你想使用名稱PriceOfCurrentProduct(大于當(dāng)前產(chǎn)品的價(jià)格)作為計(jì)算的當(dāng)前產(chǎn)品的價(jià)格的篩選條件,那么,很容易看到這個(gè)pseudo-偽DAX公式將采取一切必要的行為來達(dá)到這一計(jì)算目的:

我們將公式正確表述為:希望FILTER只返回價(jià)格高于當(dāng)前值的產(chǎn)品,然后COUNTROWS計(jì)數(shù)這些產(chǎn)品。這時(shí),唯一剩下的問題是用有效的DAX語法來表示、替換這個(gè):PriceOfCurrentProduct--當(dāng)前產(chǎn)品的價(jià)格。

定義“current”-現(xiàn)在的、當(dāng)前的行篩選條件,我們的意思是想告訴DAX:計(jì)算到這一列的每一行時(shí)引用該列的當(dāng)前行值。這似乎比你想象的要難。

因?yàn)槭窃赑roduct—產(chǎn)品表中定義這個(gè)新的計(jì)算列,因此,DAX在一個(gè)行篩選中計(jì)算表達(dá)式(具有隱式行篩選)。但是,表達(dá)式使用了FILTER()函數(shù),它在同一個(gè)表(產(chǎn)品表)上創(chuàng)建了一個(gè)新的行篩選。

實(shí)際上,前一個(gè)表達(dá)式的第5行中使用的Product[UnitPrice]是篩選器--公式內(nèi)部所迭代的當(dāng)前行的單價(jià),因此,這個(gè)新的行篩選隱式了計(jì)算列引入的原始行篩選(Product[UnitPrice]列自帶的隱式行篩選)。

你發(fā)現(xiàn)問題了嗎?我們希望訪問當(dāng)前單元格(行)的價(jià)格來計(jì)算,但不希望使用由此引入的這個(gè)行篩選。相反,還是希望使用原有的計(jì)算列中的隱式行篩選。

DAX提供了一個(gè)函數(shù)來表示這種當(dāng)前行的行為:?EARLIER。

通過使用EARLIER表示?當(dāng)前行篩選?而不是最后定義的行篩選來檢索列值。因此,可以使用EARLIER(Product[UnitPrice])來表達(dá):PriceOfCurrentProduct--當(dāng)前產(chǎn)品的價(jià)格。

EARLIER是DAX中最奇怪的函數(shù)之一。許多用戶在使用EARLIER時(shí)候總感到疑惑,這也許是因?yàn)闆]有考慮到行篩選行為,而且也沒有考慮到通過在同一個(gè)表上創(chuàng)建多個(gè)迭代來嵌套行篩選的事實(shí)。

實(shí)際上,EARLIER是一個(gè)非常簡單的函數(shù),它將會(huì)在很多場(chǎng)景下有用。下面的代碼是最終解決場(chǎng)景:

Product[UnitPriceRank] =
COUNTROWS?(
FILTER?(Product,
Product[UnitPrice] >?EARLIER?( Product[UnitPrice] ))) +?1

?在圖4-8中,可以看到Product表中定義的計(jì)算列,該列已按降序?qū)蝺r(jià)進(jìn)行了排序:

圖4-8 [UnitPriceRank]是一個(gè)很有用的例子,說明了EARLIER嵌套的行篩選的運(yùn)用。

? ? ? ?因?yàn)橛?4個(gè)單價(jià)相同的產(chǎn)品,因此排名總是1,接下來才是第15款與此前價(jià)格不同的產(chǎn)品的排名,結(jié)果是15,這有4個(gè)連續(xù)價(jià)格相同的產(chǎn)品。我們建議你仔細(xì)研究和理解這個(gè)小示例,因?yàn)樗且粋€(gè)非常好的測(cè)試,它可以檢查使用和理解行篩選的能力,以及如何使用迭代器(在本例中是FILTER)創(chuàng)建它們,以及如何通過使用EARLIER方法訪問外部的值。

請(qǐng)注意

EARLIER接受第二個(gè)參數(shù),即跳過的步驟數(shù),這樣可以跳過兩個(gè)或多個(gè)行篩選。此外,還有一個(gè)名為EARLIEST的函數(shù),它允許直接訪問為表定義的最外層的行篩選。坦白說,EARLIER?或EARLIEST的第二個(gè)參數(shù)都不是經(jīng)常使用的:雖然有兩個(gè)嵌套的行篩選的場(chǎng)景很常見,但是有三個(gè)或更多的場(chǎng)景是很少發(fā)生的事情。

在拋開這個(gè)示例之前,值得注意的是,如果想要將這個(gè)值轉(zhuǎn)換為一個(gè)更好的排序(也就是說,從1開始并依次增加1的值,即創(chuàng)建一個(gè)1、2、3……序列),然后計(jì)數(shù)價(jià)格而不是計(jì)數(shù)產(chǎn)品就足夠了。

通過在前面章節(jié)中的函數(shù)學(xué)習(xí),幫助你寫出如下公式:

Product[UnitPriceRankDense] =
COUNTROWS?(
FILTER?(?VALUES?( Product [UnitPrice]?),
Product[UnitPrice] >?EARLIER?( Product[UnitPrice] ))) +?1

在圖4-9中,可以看到新的計(jì)算列結(jié)果:

圖4-9 [unitpricerank]連續(xù)顯示了一個(gè)更實(shí)用的排名,因?yàn)樗?jì)算的是價(jià)格,而不是產(chǎn)品。

我們強(qiáng)烈建議學(xué)習(xí)和理解EARLIER,因?yàn)槟銓⒔?jīng)常使用它。然而,重要的是要注意到在許多場(chǎng)景中可以使用變量來代替—以避免使用EARLIER。此外,謹(jǐn)慎使用變量會(huì)使代碼更容易閱讀。這取決于你。

例如,可以使用這個(gè)表達(dá)式來計(jì)算之前的計(jì)算列:

Product[UnitPriceRankDense] =?
VAR?CurrentPrice?= Product[UnitPrice]
RETURN
COUNTROWS?(?FILTER?(
VALUES?( Product[UnitPrice] ), Product[UnitPrice] >?CurrentPrice)) +?1

該公式使用一個(gè)變量,將當(dāng)前的Product[UnitPrice]存儲(chǔ)在CurrentPrice變量中,稍后將使用(RETURN取出)它來執(zhí)行比較。

你可以為變量提供一個(gè)有用的名稱,以使代碼更容易閱讀,這樣,不必每次讀寫表達(dá)式時(shí)需要理解遍歷行篩選傳遞的計(jì)算。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容