第4章了解評(píng)估上下文
4.1 介紹評(píng)估上下文
到此柒昏,您已經(jīng)學(xué)習(xí)了DAX語(yǔ)言的基礎(chǔ)知識(shí)嫂侍。您知道如何創(chuàng)建計(jì)算列和度量值,并且對(duì)DAX中使用的常用函數(shù)有很好的了解委造。在本章中,您將使用該語(yǔ)言進(jìn)入更高的層次:在學(xué)習(xí)DAX語(yǔ)言的扎實(shí)理論背景之后均驶,您將成為真正的DAX擁護(hù)者昏兆。
利用到目前為止所獲得的知識(shí),您已經(jīng)可以創(chuàng)建許多有趣的報(bào)告妇穴,但是您需要學(xué)習(xí)評(píng)估上下文才能創(chuàng)建更復(fù)雜的公式爬虱。實(shí)際上隶债,評(píng)估環(huán)境是DAX所有高級(jí)函數(shù)的基礎(chǔ)。
我們想給讀者一些警告跑筝。評(píng)估上下文的概念很簡(jiǎn)單死讹,您將很快學(xué)會(huì)和理解它。但是曲梗,您需要徹底理解一些細(xì)微的注意事項(xiàng)和細(xì)節(jié)赞警。否則,您將在DAX學(xué)習(xí)道路上的某個(gè)時(shí)刻感到迷失虏两。我們一直在向公共和私人班級(jí)中的數(shù)千名用戶教授DAX愧旦,因此我們知道這很正常。在某些時(shí)候定罢,您會(huì)感覺公式的作用像魔術(shù)一樣笤虫,因?yàn)槭怯行У模悄焕斫鉃槭裁醋尜臁2挥脫?dān)心:不止你是這樣琼蚯。大多數(shù)DAX學(xué)生都曾經(jīng)這樣,將來很多人也會(huì)這樣蝙场。這僅表示他們對(duì)評(píng)估上下文還不夠清楚凌停。那時(shí)的解決方案很簡(jiǎn)單:回到本章,再讀一遍售滤,
此外罚拟,在使用 CALCULATE 函數(shù)時(shí),評(píng)估上下文也起著重要的作用完箩,CALCULATE 函數(shù)可能是功能最強(qiáng)大且最難學(xué)習(xí)的DAX函數(shù)赐俗。我們將在第5章“了解 CALCULATE 和 CALCULATETABLE”中介紹 CALCULATE ,然后在本書的其余部分中使用它弊知。在沒有扎實(shí)了解評(píng)估上下文的情況下理解 CALCULATE 是有問題的阻逮。另一方面,不嘗試使用 CALCULATE 來了解評(píng)估上下文的重要性幾乎沒有可能秩彤。因此叔扼,根據(jù)我們以前寫過的書的經(jīng)驗(yàn),本章和后一章總是被標(biāo)記成兩部分漫雷,并且頁(yè)面的角被折疊瓜富。
在本書的其余部分中,我們將使用這些概念降盹。然后与柑,在第14章“高級(jí)DAX概念”中,您將通過擴(kuò)展表來完成評(píng)估上下文的學(xué)習(xí)。請(qǐng)注意价捧,本章的內(nèi)容還不是對(duì)評(píng)估上下文的明確描述丑念。對(duì)評(píng)估上下文的更詳細(xì)描述是基于擴(kuò)展表的描述,但是在對(duì)評(píng)估上下文的基礎(chǔ)有很好的了解之前结蟋,很難了解擴(kuò)展表脯倚。因此,我們以不同的步驟介紹整個(gè)理論椎眯。
介紹評(píng)估上下文
有兩個(gè)評(píng)估上下文:篩選上下文和行上下文挠将。在下一部分中,您將學(xué)習(xí)它們是什么以及如何使用它們編寫DAX代碼编整。在了解它們是什么之前舔稀,重要的是要指出一點(diǎn):它們是不同的概念,具有不同的功能和完全不同的用法掌测。
DAX新手最常犯的錯(cuò)誤是混淆兩個(gè)上下文内贮,就好像行上下文是篩選器上下文的微小變化一樣。不是這種情況汞斧。篩選上下文篩選數(shù)據(jù)夜郁,而行上下文則迭代表。DAX進(jìn)行迭代時(shí)粘勒,不會(huì)進(jìn)行篩選竞端;并且當(dāng)它進(jìn)行篩選時(shí),它不會(huì)迭代庙睡。即使這是一個(gè)簡(jiǎn)單的概念事富,我們也從經(jīng)驗(yàn)中知道很難在腦海中留下印記。我們的大腦似乎更喜歡一條短途學(xué)習(xí)之路——當(dāng)它認(rèn)為存在某些相似之處時(shí)乘陪,它將兩個(gè)概念合并為一體來使用它們统台。別被騙了。每當(dāng)您感覺到兩個(gè)評(píng)估上下文看起來相同時(shí)啡邑,就在心中停下來重復(fù)這句話贱勃,就像咒語(yǔ):“篩選上下文篩選,行上下文迭代谤逼,它們是不相同的贵扰。”
評(píng)估上下文是評(píng)估DAX表達(dá)式的上下文流部。實(shí)際上戚绕,任何DAX表達(dá)式都可以在不同的上下文中提供不同的值。這種行為很直觀贵涵,這就是為什么無(wú)需事先了解評(píng)估上下文就可以編寫DAX代碼的原因列肢。您可能已經(jīng)在編寫DAX代碼的書中達(dá)到了這一點(diǎn),而沒有了解評(píng)估上下文宾茂。因?yàn)槟枰啻陕恚袁F(xiàn)在是時(shí)候變得更加精確了,以正確的方式建立DAX的基礎(chǔ)跨晴,并準(zhǔn)備好發(fā)揮DAX的全部功能欧聘。
了解篩選上下文
讓我們首先了解什么是評(píng)估上下文。所有DAX表達(dá)式都在上下文中求值端盆。上下文是評(píng)估公式的求值“環(huán)境”怀骤。例如,思考下列度量值:
Sales Amount := SUMX ( Sales, Sales[Quantity] * Sales[Net Price] )
此公式計(jì)算 Sales 表中數(shù)量(Sales[Quantity])乘以價(jià)格(Sales[Net Price])的總和焕妙。我們可以在報(bào)告中使用此度量并查看結(jié)果蒋伦,如圖4-1所示。
僅這個(gè)數(shù)字看起來并不有趣焚鹊。但是痕届,如果您仔細(xì)考慮,該公式將精確計(jì)算出一個(gè)預(yù)期值:所有銷售額(Sales Amount)的總和末患。在真實(shí)的報(bào)告中研叫,很可能會(huì)按某列來切分?jǐn)?shù)據(jù)。例如璧针,我們可以選擇產(chǎn)品品牌(Brand)嚷炉,在行上使用它,然后矩陣報(bào)表開始顯示有趣的業(yè)務(wù)見解探橱,如圖4-2所示申屹。
總計(jì)仍然存在,但現(xiàn)在是較小值的總和走搁。每個(gè)值以及所有其他值都提供了更詳細(xì)的見解独柑。但是,您應(yīng)該注意私植,正在發(fā)生一些奇怪的事情:該公式并未計(jì)算出我們顯然要求的內(nèi)容忌栅。實(shí)際上,在報(bào)表的每個(gè)單元格中曲稼,公式不再計(jì)算所有銷售額的總和索绪。相反,它計(jì)算給定品牌的銷售額贫悄。最后瑞驱,請(qǐng)注意,代碼中沒有任何地方說它可以(或應(yīng)該)對(duì)數(shù)據(jù)子集起作用窄坦。此篩選發(fā)生在公式之外唤反。
由于DAX執(zhí)行公式的評(píng)估上下文凳寺,每個(gè)單元格計(jì)算的值都不同。您可以將公式的評(píng)估上下文視為DAX評(píng)估公式的單元格的周圍區(qū)域彤侍。
DAX在相應(yīng)的上下文中評(píng)估所有公式肠缨。即使公式相同,結(jié)果也有所不同盏阶,因?yàn)镈AX針對(duì)不同的數(shù)據(jù)子集執(zhí)行相同的代碼晒奕。
該上下文稱為“篩選上下文”,顧名思義名斟,它是篩選表上下文脑慧。曾經(jīng)編寫的任何公式都將具有不同的值,具體取決于用于執(zhí)行其評(píng)估的篩選上下文砰盐。這種行為雖然直觀闷袒,但由于隱藏了許多復(fù)雜性,因此需要很好地理解岩梳。
報(bào)告的每個(gè)單元格都有不同的篩選上下文霜运。您應(yīng)該考慮到每個(gè)單元格都有不同的評(píng)估——就像是一個(gè)不同的查詢一樣,獨(dú)立于同一報(bào)表中的其他單元格蒋腮。引擎可能會(huì)執(zhí)行某種程度的內(nèi)部?jī)?yōu)化以提高計(jì)算速度淘捡,但是您應(yīng)該假設(shè)每個(gè)單元格都對(duì)基礎(chǔ)DAX表達(dá)式進(jìn)行了獨(dú)立且自主的評(píng)估。因此池摧,圖4-2中“總計(jì)(Total)”行的計(jì)算不是通過對(duì)報(bào)告的其他行求和來進(jìn)行的焦除。它是通過匯總Sales表的所有行來計(jì)算的,盡管這意味著已經(jīng)為同一報(bào)表中的其他行計(jì)算了其他迭代作彤。因此膘魄,依據(jù)DAX表達(dá)式,“總計(jì)”行中的結(jié)果可能顯示不同的結(jié)果竭讳,該結(jié)果與同一報(bào)表中的其他行無(wú)關(guān)创葡。
注意
在這些示例中,為簡(jiǎn)單起見绢慢,我們使用矩陣灿渴。我們也可以使用查詢定義評(píng)估上下文,您將在以后的章節(jié)中進(jìn)一步了解它胰舆。就目前而言骚露,最好保持簡(jiǎn)單,只考慮報(bào)告缚窿,以簡(jiǎn)化和直觀地理解概念棘幸。
當(dāng)品牌在行上時(shí),篩選上下文為每個(gè)單元格篩選一個(gè)品牌倦零。如果通過在列上添加年份來增加矩陣的復(fù)雜性误续,則會(huì)獲得圖4-3中的報(bào)告吨悍。
現(xiàn)在,每個(gè)單元格都顯示與一個(gè)品牌和一年有關(guān)的數(shù)據(jù)子集蹋嵌。這樣做的原因是畜份,每個(gè)單元格的篩選上下文現(xiàn)在都篩選了品牌和年份。在“總計(jì)”行中欣尼,篩選僅在品牌上,而在“總計(jì)”列中停蕉,篩選僅在年份上愕鼓。總計(jì)是唯一一個(gè)計(jì)算所有銷售額之和的單元格慧起,因?yàn)楹Y選條件沒有對(duì)模型應(yīng)用任何篩選條件菇晃。 此時(shí)的游戲規(guī)則應(yīng)該很清楚:我們用于切片和切塊的列越多,矩陣每個(gè)單元格中的篩選上下文將篩選出更多的列蚓挤。如果將 Store [Continent] 列添加到行中磺送,結(jié)果將再次有所不同,如圖4-4所示灿意。
現(xiàn)在估灿,每個(gè)單元格的篩選上下文是篩選品牌,國(guó)家和年份缤剧。換句話說馅袁,篩選上下文包含一整套用于報(bào)表行和列的字段。
注意
字段是在視覺的行還是列上荒辕,還是在切片器和/或頁(yè)面/報(bào)表/視覺對(duì)象的篩選上汗销,還是在我們可以使用報(bào)表創(chuàng)建的任何其他類型的篩選中,所有這些都是不相關(guān)的抵窒。所有這些篩選都有助于定義單個(gè)篩選上下文弛针,DAX使用該上下文來評(píng)估公式。在行或列上顯示字段對(duì)于美學(xué)目的很有用李皇,但是DAX計(jì)算值的方式?jīng)]有變化削茁。
Power BI中的視覺交互通過組合圖形界面中的不同元素來構(gòu)成篩選上下文。實(shí)際上掉房,單元格的篩選上下文是通過將來自行付材,列,切片器以及用于篩選的任何其他視覺對(duì)象的所有篩選合并在一起來計(jì)算的圃阳。例如厌衔,查看圖4-5。
左上方單元格的篩選上下文(A.Datum,CY 2007页徐,57,276.00)不僅篩選視覺對(duì)象的行和列苏潜,還篩選即將到來的職業(yè)(專業(yè))和大陸(歐洲),篩選上下文來自各種視覺對(duì)象变勇。所有這些篩選都有助于定義對(duì)一個(gè)單元格有效的單個(gè)篩選上下文恤左,DAX在評(píng)估公式之前將其應(yīng)用于整個(gè)數(shù)據(jù)模型。
篩選上下文的更正式定義是說篩選上下文是一組篩選搀绣。篩選又是元組的列表飞袋,元組是一些已定義列的一組值。圖4-6顯示了篩選上下文的直觀表示链患,在該上下文中評(píng)估突出顯示的單元格巧鸭。報(bào)告的每個(gè)元素都有助于創(chuàng)建篩選上下文,并且報(bào)告中的每個(gè)單元格都有不同的篩選上下文麻捻。 該圖使用符號(hào)纲仍,報(bào)告和箭頭來描述篩選上下文。
圖4-6的篩選上下文包含三個(gè)篩選贸毕。第一個(gè)篩選包含一個(gè)日歷年的元組郑叠,值CY2007。第二個(gè)篩選包含兩個(gè)用于教育的元組明棍,值“ High School”和“ Partial College”锻拘。第三個(gè)篩選包含Brand的單個(gè)元組,值Contoso击蹲。您可能會(huì)注意到署拟,每個(gè)篩選僅包含一列的元組。稍后歌豺,您將學(xué)習(xí)如何創(chuàng)建具有多列的元組推穷。多列元組是DAX開發(fā)人員手中既強(qiáng)大又復(fù)雜的工具。
在結(jié)束本介紹之前类咧,讓我們回顧一下本節(jié)開始時(shí)使用的度量值:
Sales Amount := SUMX ( Sales, Sales[Quantity] * Sales[Net Price] )
這是讀取先前度量值的正確方法:對(duì)于當(dāng)前篩選上下文中可見的 Sales 中所有行馒铃,該度量值計(jì)算“數(shù)量”乘以“凈價(jià)”之和。
這同樣適用于更簡(jiǎn)單的聚合痕惋。例如区宇,考慮以下度量值:
Total Quantity := SUM ( Sales[Quantity] )
它將 Sales 表中所有當(dāng)前篩選上下文中可見的行的“數(shù)量([Quantity])”列求和。通過考慮相應(yīng)的SUMX版本值戳,您可以更好地了解其工作原理:
Total Quantity := SUMX ( Sales, Sales[Quantity] )
查看SUMX定義议谷,我們可能認(rèn)為篩選上下文會(huì)影響Sales表達(dá)式的求值,該表達(dá)式僅返回在當(dāng)前篩選上下文中可見的Sales表的行堕虹。的確如此卧晓,但是您應(yīng)該考慮篩選上下文也適用于以下度量值芬首,這些度量值沒有相應(yīng)的迭代器:
Customers := DISTINCTCOUNT ( Sales[CustomerKey] ) -- Count customers in filter context
Colors :=
VAR ListColors = DISTINCT ( 'Product'[Color] ) -- Unique colors in filter context
RETURN COUNTROWS ( ListColors ) -- Count unique colors
在這一點(diǎn)上,花費(fèi)大量時(shí)間強(qiáng)調(diào)篩選上下文始終處于活動(dòng)狀態(tài)并且會(huì)影響公式結(jié)果的概念可能看起來很花哨逼裆。但是郁稍,請(qǐng)記住,DAX要求您非常精確胜宇。DAX的大部分復(fù)雜性都不在于學(xué)習(xí)新功能耀怜。相反,復(fù)雜性來自許多細(xì)微概念的存在桐愉。當(dāng)這些概念混合在一起時(shí)财破,就會(huì)出現(xiàn)一個(gè)復(fù)雜的場(chǎng)景。現(xiàn)在仅财,篩選上下文由報(bào)告定義。一旦您了解了如何自己創(chuàng)建篩選上下文(下一章中將介紹的一項(xiàng)關(guān)鍵技能)碗淌,那么了解公式的每個(gè)部分中哪個(gè)篩選上下文處于活動(dòng)狀態(tài)就至關(guān)重要盏求。
了解行上下文
在上一節(jié)中,您了解了篩選上下文亿眠。在本節(jié)中碎罚,您現(xiàn)在將學(xué)習(xí)第二種類型的評(píng)估上下文:行上下文。請(qǐng)記住纳像,盡管行上下文和篩選上下文都是評(píng)估上下文荆烈,但是它們不是同一概念。正如您在上一節(jié)中所學(xué)到的竟趾,顧名思義憔购,篩選上下文的目的是篩選表。另一方面岔帽,行上下文不是篩選表的工具玫鸟。相反,它用于遍歷表和評(píng)估列值犀勒。
這次我們使用不同的公式進(jìn)行考慮屎飘,定義了一個(gè)計(jì)算列以計(jì)算毛利率:
Sales[Gross Margin] = Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] )
所得的計(jì)算列中的每一行都有一個(gè)不同的值,如圖4-7所示贾费。
不出所料,表的每一行在計(jì)算列中都有一個(gè)不同的值褂萧。實(shí)際上押桃,由于表達(dá)式中使用的三列的每一行都有給定的值,因此导犹,最終表達(dá)式計(jì)算出不同的值是很自然的結(jié)果怨规。與篩選上下文一樣陌宿,原因是存在評(píng)估上下文。這次波丰,上下文不篩選表壳坪。相反,它標(biāo)識(shí)要進(jìn)行計(jì)算的行掰烟。
注意
行上下文引用DAX表表達(dá)式的結(jié)果中的一行爽蝴。請(qǐng)勿將其與報(bào)告中的一行混淆。DAX無(wú)法直接引用報(bào)告中的行或列纫骑。在Power BI中的矩陣中以及在Excel中的數(shù)據(jù)透視表中顯示的值是在篩選上下文中DAX度量值的計(jì)算結(jié)果蝎亚、存儲(chǔ)在表中的值或計(jì)算列的值。
換句話說先馆,我們知道已計(jì)算列是逐行計(jì)算的发框,但是DAX如何知道它當(dāng)前在迭代哪一行?它知道該行煤墙,因?yàn)橛辛硪粋€(gè)評(píng)估上下文提供行——-它就是行上下文梅惯。當(dāng)我們?cè)诰哂幸话偃f(wàn)行的表上創(chuàng)建一個(gè)計(jì)算列時(shí),DAX將創(chuàng)建一個(gè)行上下文仿野,該行上下文使用該行上下文作為游標(biāo)铣减,對(duì)在表上逐行迭代的表達(dá)式求值。
當(dāng)我們創(chuàng)建計(jì)算列時(shí)脚作,默認(rèn)情況下DAX將創(chuàng)建行上下文葫哗。在這種情況下,無(wú)需手動(dòng)創(chuàng)建行上下文:計(jì)算列始終在行上下文中執(zhí)行球涛。您已經(jīng)學(xué)習(xí)了如何通過開始迭代手動(dòng)創(chuàng)建行上下文劣针。實(shí)際上,可以將毛利率寫為度量值亿扁,如以下代碼所示:
Gross Margin :=
SUMX (
Sales,
Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] )
)
在這種情況下酿秸,因?yàn)樵摯a用于度量值,所以沒有自動(dòng)行上下文魏烫。SUMX是一個(gè)迭代函數(shù)辣苏,它創(chuàng)建一個(gè)行上下文,該行上下文開始逐行遍歷Sales表哄褒。在迭代過程中稀蟋,它在行上下文內(nèi)執(zhí)行SUMX的第二個(gè)表達(dá)式。因此呐赡,在迭代的每個(gè)步驟中退客,DAX都知道要對(duì)表達(dá)式中使用的三個(gè)列名稱使用哪個(gè)值。
當(dāng)我們創(chuàng)建計(jì)算列或在迭代內(nèi)計(jì)算表達(dá)式時(shí),將存在行上下文萌狂。沒有其他方法可以創(chuàng)建行上下文档玻。此外,當(dāng)我們想要獲取特定行的列的值時(shí)茫藏,要一個(gè)行上下文是有幫助的误趴。例如,以下度量值定義無(wú)效务傲。實(shí)際上凉当,它嘗試計(jì)算 Sales [Net Price] 的值,并且沒有行上下文提供需要對(duì)其執(zhí)行計(jì)算的行:
Gross Margin := Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] )
當(dāng)對(duì)計(jì)算列執(zhí)行該表達(dá)式時(shí)售葡,該表達(dá)式有效看杭;如果用于度量,則該表達(dá)式無(wú)效挟伙。原因不是度量值和計(jì)算列具有使用DAX的不同方式楼雹。原因是所計(jì)算列具有自動(dòng)行上下文,而度量值則沒有尖阔。如果要在度量?jī)?nèi)逐行評(píng)估表達(dá)式贮缅,則需要開始迭代以創(chuàng)建行上下文。
注意
列引用需要一個(gè)行上下文才能從表中返回該列的值诺祸。列引用還可以用作幾個(gè)沒有行上下文的DAX函數(shù)的參數(shù)携悯。例如祭芦,DISTINCT和DISTINCTCOUNT可以將列引用作為參數(shù)筷笨,而無(wú)需定義行上下文。但是龟劲,DAX表達(dá)式中的列引用需要評(píng)估行上下文胃夏。
在這一點(diǎn)上,我們需要重復(fù)一個(gè)重要的概念:行上下文不是篩選一行的一種特殊的篩選上下文昌跌。行上下文不會(huì)以任何方式篩選模型仰禀。行上下文僅向DAX指示要在表中使用哪一行。如果要對(duì)模型應(yīng)用篩選蚕愤,則使用的工具是篩選上下文答恶。另一方面,如果用戶想逐行評(píng)估表達(dá)式萍诱,則行上下文將完成這項(xiàng)工作悬嗓。