第5章了解 CALCULATE 和 CALCULATETABLE
在本章中,我們將繼續(xù)探索DAX語言的強(qiáng)大功能,并詳細(xì)說明一個函數(shù):CALCULATE。相同的注意事項適用于 CALCULATETABLE,它計算并返回表而不是標(biāo)量值。為了簡單起見,我們將在示例中引用 CALCULATE,但是請記住 CALCULATETABLE 顯示相同的行為。
CALCULATE 是DAX中最重要,最有用和最復(fù)雜的函數(shù),因此值得用一整章來講解。該函數(shù)本身易于學(xué)習(xí);它僅執(zhí)行一些任務(wù)。復(fù)雜性來自于以下事實(shí):CALCULATE 和 CALCULATETABLE 是DAX中唯一可以創(chuàng)建新篩選上下文的函數(shù)。因此,盡管它們是簡單的函數(shù),但在公式中使用 CALCULATE 或 CALCULATETABLE 會立即增加其復(fù)雜性。
本章與上一章一樣艱難。我們建議您仔細(xì)閱讀它,對 CALCULATE 有一個大致的了解,然后繼續(xù)學(xué)習(xí)本書的其余部分。一旦您對某個公式感到迷惑,請回到本章并從頭開始重新閱讀。每次閱讀時,您都會有新的收獲。
CALCULATE 和 CALCULATETABLE介紹
上一章描述了兩個評估上下文:行上下文和篩選上下文。行上下文自動存在于計算列中,并且可以使用迭代函數(shù)以編程方式創(chuàng)建行上下文。另一方面,篩選上下文是由報表創(chuàng)建的,我們還沒有描述如何以編程方式創(chuàng)建篩選上下文。CALCULATE 和CALCULATETABLE 是操作篩選上下文所需的唯一函數(shù)。確實(shí),CALCULATE 和CALCULATETABLE 是可以通過操作現(xiàn)有篩選上下文創(chuàng)建新篩選上下文的唯一函數(shù)。從這里開始,我們將僅顯示基于CALCULATE 的示例,但請記住,對于返回表的 CALCULATETABLE DAX表達(dá)式可以執(zhí)行相同的操作。在本書第12章,有更多使用CALCULATETABLE 的示例。
創(chuàng)建篩選上下文
我們將通過一個實(shí)際示例介紹為什么要創(chuàng)建新的篩選上下文。如以下各節(jié)所述,編寫代碼而無法創(chuàng)建新的篩選上下文會導(dǎo)致冗長且難以理解的代碼。下面的示例,說明創(chuàng)建新的篩選上下文如何極大地改善最初看起來相當(dāng)復(fù)雜的代碼。
Contoso 是一家在世界各地銷售電子產(chǎn)品的公司。有些產(chǎn)品被冠以 Contoso 品牌,其他產(chǎn)品具有不同的品牌。要求在報告中比較 Contoso 品牌產(chǎn)品和競爭對手的毛利和毛利率。報告的第一部分需要進(jìn)行以下計算:
Sales Amount := SUMX ( Sales, Sales[Quantity] * Sales[Net Price] )
Gross Margin := SUMX ( Sales, Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] ) )
GM % := DIVIDE ( [Gross Margin], [Sales Amount] )
DAX的一個出色的方面是,可以在現(xiàn)有度量值之上構(gòu)建更復(fù)雜的計算。實(shí)際上,您可以在GM%的定義中欣賞到這一點(diǎn),GM%是 Sales 表一個計算毛利率的度量值。GM%調(diào)用兩個事先定義的度量值,將它們相除。如果已經(jīng)有一個計算值的度量值,則可以調(diào)用該度量值,而不用重寫完整的代碼。
使用上面定義的三個度量值,可以構(gòu)建第一個報告,如圖5-1所示。
圖5-1 通過這三個度量值可對不同類別毛利的快速了解
構(gòu)建報告的下一步更加復(fù)雜。實(shí)際上,我們想要的最終報告是圖5-2中的報告,該報告顯示了另外兩列:Contoso 品牌產(chǎn)品的毛利和毛利率(以數(shù)量和百分比表示)。
圖5-2 報告的最后兩列顯示了 Contoso 品牌產(chǎn)品的毛利和毛利率
運(yùn)用到目前為止所獲得的知識,您已經(jīng)可以為這兩個度量值編寫代碼。因為要求是將計算范圍限制為一個品牌,所以一種解決方案是使用 FILTER 將毛利的計算范圍限制在Contoso 產(chǎn)品:
Contoso GM :=
VAR ContosoSales = -- Saves the rows of Sales which are related
FILTER ( -- to Contoso-branded products into a variable
Sales,
RELATED ( 'Product'[Brand] ) = "Contoso"
)
VAR ContosoMargin = -- Iterates over ContosoSales
SUMX ( -- to only compute the margin for Contoso
ContosoSales,
Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] )
)
RETURN
ContosoMargin
ContosoSales 變量包含與所有 Contoso 品牌產(chǎn)品相關(guān)的 Sales 表的行。一旦計算出變量,SUMX就會在ContosoSales上進(jìn)行迭代以計算毛利。由于迭代位于 Sales 表上,而篩選位于 Product 表上,因此需要使用 RELATED 來檢索 Sales 表中每一行的相關(guān)產(chǎn)品。以類似的方式,可以通過重復(fù)運(yùn)用兩次 ContosoSales 變量來計算 Contoso 的毛利率:
Contoso GM % :=
VAR ContosoSales = -- Saves the rows of Sales which are related
FILTER ( -- to Contoso-branded products into a variable
Sales,
RELATED ( 'Product'[Brand] ) = "Contoso"
)
VAR ContosoMargin = -- Iterates over ContosoSales
SUMX ( -- to only compute the margin for Contoso
ContosoSales,
Sales[Quantity] * ( Sales[Net Price] - Sales[Unit Cost] )
)
VAR ContosoSalesAmount = -- Iterates over ContosoSales
SUMX ( -- to only compute the sales amount for Contoso
ContosoSales,
Sales[Quantity] * Sales[Net Price]
)
VAR Ratio =
DIVIDE ( ContosoMargin, ContosoSalesAmount )
RETURN
Ratio
Contoso GM% 的代碼更長一些,但是,從邏輯的角度來看,遵循的模式與Contoso GM相同。盡管這些方法有效,但DAX最初的優(yōu)雅明顯已經(jīng)喪失。實(shí)際上,該模型已經(jīng)包含計算毛利和計算毛利率的度量值。但是,由于需要篩選新的數(shù)值,因此我們必須重寫表達(dá)式以添加條件。
值得強(qiáng)調(diào)的是,基本度量值 Gross Margin 和GM%已經(jīng)可以計算 Contoso 的值。從圖5-2中可以看到,Contoso 的 Gross Margin (毛利)等于3,877,070.65,GM%(毛利率)等于52.73%。如圖5-3所示,通過按品牌劃分的基本度量值的 Gross Margin 和 GM% 可以獲得相同的數(shù)字。
圖5-3 按品牌劃分時,基本度量值將計算 Contoso 的毛利和毛利率
在突出顯示的單元格中,報告創(chuàng)建的篩選上下文正在篩選 Contoso 品牌。篩選上下文篩選模型。由于將Sales 鏈接到 Product 的關(guān)系,Product [Brand] 列上的篩選上下文將篩選 Sales 表。因為篩選上下文對整個模型起作用,所以使用篩選上下文可以間接篩選表。
如果我們可以使DAX通過以編程方式,創(chuàng)建僅篩選 Contoso 品牌產(chǎn)品的篩選上下文,來計算毛利度量值,那么我們對后兩個度量值的實(shí)現(xiàn)將容易得多。這可以通過 CALCULATE 來實(shí)現(xiàn)。
CALCULATE 的完整描述將在本章后面介紹。首先,我們來看 CALCULATE 的語法:
CALCULATE ( Expression, Condition1, ... ConditionN )
CALCULATE 可以接受任意數(shù)量的參數(shù)。唯一的必需參數(shù)是第一個,即要求值的表達(dá)式。第一個參數(shù)之后的條件稱為篩選參數(shù)。CALCULATE 基于一組篩選參數(shù)創(chuàng)建一個新的篩選上下文。一旦計算出新的篩選上下文,CALCULATE會將其應(yīng)用于模型,然后繼續(xù)對表達(dá)式進(jìn)行求值。因此,通過利用CALCULATE,Contoso Margin 和 Contoso GM% 的代碼變得更加簡單:
Contoso GM :=
CALCULATE (
[Gross Margin], -- Computes the gross margin
'Product'[Brand] = "Contoso" -- In a filter context where brand = Contoso
)
Contoso GM % :=
CALCULATE (
[GM %], -- Computes the gross margin percentage
'Product'[Brand] = "Contoso" -- In a filter context where brand = Contoso
)
歡迎回來,簡潔優(yōu)雅!通過創(chuàng)建一個強(qiáng)制使品牌成為 Contoso 的篩選上下文,人們可以依靠現(xiàn)有的度量值并更改其行為,而不必重寫度量值的代碼。
CALCULATE 允許您通過在當(dāng)前上下文中操作篩選,來創(chuàng)建新的篩選上下文。如您所見,這導(dǎo)致了簡單而優(yōu)雅的代碼。在下節(jié)中,我們將對 CALCULATE 的行為提供完整且更正式的定義,詳細(xì)描述 CALCULATE 的功能以及如何利用其功能。確實(shí),就目前而言,我們示例有一些難度。Contoso 度量值的初始定義在語義上不等同于最終定義,有一些差異需要很好地理解。
CALCULATE 介紹
既然您已經(jīng)初步了解了CALCULATE,現(xiàn)在該開始學(xué)習(xí)此函數(shù)的詳細(xì)信息了。如前所述,CALCULATE 是唯一可以修改篩選上下文的DAX函數(shù)。請記住,當(dāng)我們提到 CALCULATE 時,我們還包括 CALCULATETABLE。CALCULATE 不會修改篩選上下文:它是通過將其篩選參數(shù)與現(xiàn)有篩選上下文合并來創(chuàng)建新的篩選上下文。一旦 CALCULATE 結(jié)束,它的篩選上下文將被丟棄,先前的篩選上下文將再次生效。
我們介紹了CALCULATE 的語法為:
CALCULATE ( Expression, Condition1, ... ConditionN )
第一個參數(shù)是CALCULATE 將計算的表達(dá)式。在計算表達(dá)式之前,CALCULATE 將計算篩選參數(shù),并使用它們來操縱篩選上下文。
關(guān)于CALCULATE 的第一件事要注意的是,篩選參數(shù)不是布爾條件:篩選參數(shù)是表。當(dāng)將布爾條件用作 CALCULATE 的篩選參數(shù)時,DAX都會將其轉(zhuǎn)換為值表。
在上一節(jié)中,我們使用了以下代碼:
Contoso GM :=
CALCULATE (
[Gross Margin], -- Computes the gross margin
'Product'[Brand] = "Contoso" -- In a filter context where brand = Contoso
)
使用布爾條件只是完整 CALCULATE 語法的快捷方式。這稱為語法糖。它是這樣寫的:
Contoso GM :=
CALCULATE (
[Gross Margin], -- Computes the gross margin
FILTER ( -- Using as valid values for Product[Brand]
ALL ( 'Product'[Brand] ), -- any value for Product[Brand]
'Product'[Brand] = "Contoso" -- which is equal to "Contoso"
)
)
這兩種語法是等效的,在性能或語義上沒有區(qū)別。話雖這么說,特別是當(dāng)您第一次學(xué)習(xí)CALCULATE 時,篩選參數(shù)始終用表的形式是很有用的,這樣 CALCULATE 的行為更加明顯,一旦習(xí)慣了CALCULATE 語義,使用語法的精簡版本就更加方便了,精簡版本更短,更容易閱讀。
篩選參數(shù)是一個表,即一個值列表。作為篩選參數(shù)的表,定義了在表達(dá)式求值期間,對于列而言可見的值列表。在上一個示例中,FILTER 返回的表僅一行,該行 Product [Brand] 的值等于 “Contoso”。換句話說,“Contoso” 是 CALCULATE 對 Product [Brand] 列顯示的唯一值。因此,CALCULATE 會在模型中篩選僅包含 Contoso品牌的產(chǎn)品。考慮以下兩個定義:
Sales Amount :=
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
Contoso Sales :=
CALCULATE (
[Sales Amount],
FILTER (
ALL ( 'Product'[Brand] ),
'Product'[Brand] = "Contoso"
)
)
Contoso Sales 的 CALCULATE 中的 FILTER 的篩選參數(shù)ALL(Product [Brand] )掃描所有產(chǎn)品品牌; 因此,產(chǎn)品品牌上任何以前存在的篩選都會被新篩選覆蓋。在按品牌劃分的報表中使用度量值時,這一點(diǎn)更加明顯。在圖5-4中:Contoso Sales列中 的所有行/品牌報告的銷售額與Contoso 的 Sales Amount 的價值相同。
圖5-4 Contoso Sales 用新的 Contoso 篩選覆蓋了現(xiàn)有篩選
在每一行中,報告都會創(chuàng)建一個相關(guān)品牌的篩選上下文。例如,在Litware的行中,報告創(chuàng)建的原始篩選上下文僅包含顯示Litware產(chǎn)品的篩選。然后,CALCULATE 評估其篩選參數(shù),該參數(shù)返回僅包含 Contoso 的表。新創(chuàng)建的篩選將覆蓋同一列上先前存在的篩選。圖5-5中可看到該過程的圖形表示。
圖5-5 Litware 的篩選被 CALCULATE 評估的 Contoso 篩選覆蓋
CALCULATE不會覆蓋整個原始篩選器上下文。它僅替換 filter 參數(shù)中包含的列上先前存在的篩選。實(shí)際上,如果將報表更改為按 Product [Category] 切片,結(jié)果將有所不同,如圖5-6所示。
圖5-6 如果報表按類別篩選,則品牌上的篩選將被合并并且不會覆蓋
現(xiàn)在,報告正在篩選 Product [Category],而 CALCULATE 在 Product [Brand] 上應(yīng)用篩選以評估 Contoso Sales 度量值。這兩個篩選不適用于 Product 的同一列,因此,不會發(fā)生覆蓋,并且兩個篩選可以作為新的篩選上下文一起工作。結(jié)果,每個單元格都顯示了給定類別的 Contoso 的 Sales Amount 。如圖5-7所示。
圖5-7 CALCULATE 覆蓋同一列上的篩選。如果它們在不同的列上,它將合并篩選
現(xiàn)在您已經(jīng)了解了CALCULATE的基礎(chǔ)知識,我們可以總結(jié)一下它的語義:
- CALCULATE 復(fù)制當(dāng)前篩選上下文。
- CALCULATE 評估每個篩選參數(shù),并為每個條件生成指定列的有效值列表。
- 如果兩個或多個篩選參數(shù)影響同一列,則使用AND運(yùn)算符(或使用數(shù)學(xué)術(shù)語中的交集)將它們合并在一起。
- CALCULATE 使用新篩選替換模型中列上的現(xiàn)有篩選。如果一列已具有篩選,則新的篩選將替換現(xiàn)有的篩選。另一方面,如果該列沒有篩選,則CALCULATE 將新篩選添加到篩選上下文中。
- 準(zhǔn)備好新的篩選上下文后,CALCULATE將篩選上下文應(yīng)用于模型,并計算第一個參數(shù):表達(dá)式。最后,CALCULATE還原原始篩選上下文,返回計算結(jié)果。
注意
CALCULATE 執(zhí)行另一項非常重要的任務(wù):將任何現(xiàn)有的行上下文轉(zhuǎn)換為等效的篩選上下文。在本章后面的“了解上下文轉(zhuǎn)換”,您可以找到關(guān)于此主題的更詳細(xì)的討論。如果您需要重新閱讀本節(jié),請記住:CALCULATE 從現(xiàn)有的行上下文中創(chuàng)建一個篩選上下文。
CALCULATE 接受兩種類型的篩選:
- 值列表,以表表達(dá)式的形式。在這種情況下,提供要在新的篩選上下文中顯示的值的確切列表。篩選可以是具有任意數(shù)量列的表。篩選中僅考慮不同列中的現(xiàn)有值組合。
- 布爾條件,例如 Product [Color] =“ White”。這些篩選需要在單個列上工作,因為結(jié)果需要是單列的值列表。這種類型的篩選參數(shù)也稱為謂詞。
如果您將語法與布爾條件一起使用,則DAX會將其轉(zhuǎn)換為值列表。因此,無論何時編寫此代碼:
Sales Amount Red Products :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red"
)
DAX將表達(dá)式轉(zhuǎn)換為:
Sales Amount Red Products :=
CALCULATE (
[Sales Amount],
FILTER (
ALL ( 'Product'[Color] ),
'Product'[Color] = "Red"
)
)
因此,只能在具有布爾條件的篩選參數(shù)中引用一列。DAX需要在 FILTER 函數(shù)中檢測要迭代的列,該函數(shù)是在后臺自動生成的。如果布爾表達(dá)式引用了兩列或更多列,則必須顯式編寫 FILTER 迭代,這將在本章后面學(xué)習(xí)。
用 CALCULATE 計算百分比
現(xiàn)在我們已經(jīng)介紹了CALCULATE,我們可以使用它來定義幾個計算。本節(jié)的目的是使您注意一些有關(guān)CALCULATE的細(xì)節(jié),這些細(xì)節(jié)乍一看并不明顯。在本章的后面,我們將介紹CALCULATE的更多高級的方面。現(xiàn)在,我們重點(diǎn)介紹使用 CALCULATE 時可能遇到的一些問題。
經(jīng)常出現(xiàn)的模式是百分比模式。使用百分比時,準(zhǔn)確定義所需的計算非常重要。在這組示例中,您將了解CALCULATE 和 ALL 函數(shù)的不同用法是如何提供不同的結(jié)果的。
從簡單的百分比計算開始。要構(gòu)建以下報告,以顯示銷售額及其占總計的百分比。可以在圖5-8中看到想要獲得的結(jié)果。
圖5-8 銷售百分比顯示當(dāng)前類別相對于總計的百分比
為了計算百分比,需要將當(dāng)前篩選上下文中的 Sales Amount 值除以忽略現(xiàn)有篩選的篩選上下文中的 Sales Amount 的值。實(shí)際上,Audio 的1.26%是384,518.16除以30,591,343.98得來的。
在報告的每一行中,篩選上下文已經(jīng)包含當(dāng)前類別。因此,對于 Sales Amount ,結(jié)果將按給定類別自動篩選。比率的分母需要忽略當(dāng)前的篩選上下文,以便評估總計。由于 CALCULATE 的篩選參數(shù)是表,因此提供一個表函數(shù)就足夠了,該函數(shù)將忽略類別上的當(dāng)前篩選上下文,并始終返回所有類別,而與任何篩選無關(guān)。您先前已了解到此函數(shù)為 ALL。查看以下度量值定義:
All Category Sales :=
CALCULATE ( -- Changes the filter context of
[Sales Amount], -- the sales amount
ALL ( 'Product'[Category] ) -- making ALL categories visible
)
ALL從篩選上下文中刪除 Product [Category] 列上的篩選。因此,在報告的任何單元格中,它都會忽略 Category 中存在的任何篩選。效果是刪除了報告行所應(yīng)用的 Category 篩選。查看圖5-9中的結(jié)果。您可以看到,報表的每一行度量值 All Category Sales 一直返回相同的值—Sales Amount 的總計。
圖5-9 ALL刪除了Category上的篩選,因此CALCULATE定義了一個篩選上下文,而Category上沒有任何篩選
度量值 All Category Sales 本身并沒有用。用戶不太可能希望創(chuàng)建一個在所有行上顯示相同值的報告。但是,該值非常適合作為我們要計算的百分比的分母。計算百分比的公式可以這樣寫:
Sales Pct :=
VAR CurrentCategorySales = -- CurrentCategorySales contains
[Sales Amount] -- the sales in the current context
VAR AllCategoriesSales = -- AllCategoriesSales contains
CALCULATE ( -- the sales amount in a filter context
[Sales Amount], -- where all the product categories
ALL ( 'Product'[Category] ) -- are visible
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllCategoriesSales
)
RETURN
Ratio
如本例所示,表函數(shù)和 CALCULATE 結(jié)合起來使用可以輕松編寫有用的度量值。在本書中我們經(jīng)常使用這種技術(shù),因為它是DAX中的主要計算工具。
注意
ALL 用作CALCULATE 的篩選參數(shù)時,具有特定的語義。實(shí)際上,它不會用值替換篩選上下文。而是,CALCULATE 使用 ALL 從篩選上下文中刪除類別列上的篩選。此行為有些復(fù)雜難以遵循的副作用,不屬于本節(jié)介紹內(nèi)容。我們將在本章稍后詳細(xì)介紹。
在編寫百分比計算時,請注意小細(xì)節(jié),這一點(diǎn)很重要。實(shí)際上,如果按類別對報告進(jìn)行切片,則百分比效果很好。該代碼將篩選從類別中刪除,但它不涉及任何其他現(xiàn)有篩選。如果報告中添加了其他篩選,則結(jié)果可能不完全是您想要的結(jié)果。例如,圖5-10中的報告,其中我們在報告的行中添加了 Product [Color] 列作為第二級明細(xì)。
圖5-10 將 Color 添加到報告中會在 Color 級別上產(chǎn)生意外結(jié)果
從百分比來看,類別級別的值是正確的,而顏色級別的值是錯誤的。事實(shí)上,顏色百分比并沒有累加起來——既沒加入類別級別,也沒加入100%。要了解這些值的含義以及如何對其進(jìn)行評估,始終專注于一個單元格并準(zhǔn)確了解篩選上下文發(fā)生了什么是很有幫助的。專注于圖5-11。
圖5-11 Product [Category] 上的 ALL 刪除了類別中的篩選,但保留了完整的顏色篩選
該圖將代碼、報告和符號組合在一起,以闡明 Product [Category] 上的 ALL 刪除了 Category 上的篩選,但 Color上的篩選保持不變。
報告創(chuàng)建的原始篩選上下文既包含 Category 篩選,也包含 Color 篩選。 Product[Color] 上的篩選不會被 CALCULATE 覆蓋,只會刪除 Product[Category] 篩選。結(jié)果,最終的篩選上下文僅包含 Color。因此,比率的分母包含任何類別所有給定顏色(黑色)產(chǎn)品的銷售額。
計算錯誤不是 CALCULATE 的意外行為。問題在于公式被設(shè)計為專門用于類別中的篩選,而其他任何篩選都保持不變。在另一個報告中,相同的公式非常有意義。看一下如果切換列的順序,生成一個報告,該報告先按顏色進(jìn)行切片,然后按類別進(jìn)行分類,會發(fā)生什么情況,如圖5-12所示。
圖5-12中的報告更有意義。該度量值計算出的結(jié)果相同,但是由于報表的布局,它更加直觀。顯示的百分比是給定顏色內(nèi)各類別的百分比。一個一個顏色,百分比總和為100%。
換句話說,當(dāng)要求用戶計算百分比時,應(yīng)特別注意確定百分比的分母。CALCULATE 和 ALL 是要使用的主要工具,但是公式的規(guī)范取決于業(yè)務(wù)需求。
回到示例:目標(biāo)是修正計算,以便針對類別或顏色的篩選計算百分比。有多種執(zhí)行操作的方法,這些方案導(dǎo)致的結(jié)果略有不同,值得深入研究。
一種解決方案是讓CALCULATE 從類別和顏色中刪除篩選。向 CALCULATE 添加多個篩選參數(shù)可以實(shí)現(xiàn)此目標(biāo):
Sales Pct :=
VAR CurrentCategorySales =
[Sales Amount]
VAR AllCategoriesAndColorSales =
CALCULATE (
[Sales Amount],
ALL ( 'Product'[Category] ), -- The two ALL conditions could also be replaced
ALL ( 'Product'[Color] ) -- by ALL ( 'Product'[Category], 'Product'[Color] )
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllCategoriesAndColorSales
)
RETURN
Ratio
后一版本的 Sales Pct 與包含顏色和類別的報表配合使用時很好,但是仍然受到與前一版本類似的局限。雖然它會產(chǎn)生正確的顏色和類別百分比(如圖5-13所示),但是一旦將其他列添加到報表中,它就會失敗。
圖5-13 將ALL應(yīng)用于產(chǎn)品類別和顏色,現(xiàn)在百分比加起來是正確的
在報告中添加另一列,將產(chǎn)生與迄今為止所遇到到的不一致相同的問題。如果用戶想要刪除Product 表上的所有篩選來創(chuàng)建一個百分比,仍然可以使用 ALL 函數(shù)將整個表作為參數(shù)傳遞:
Sales Pct All Products :=
VAR CurrentCategorySales =
[Sales Amount]
VAR AllProductSales =
CALCULATE (
[Sales Amount],
ALL ( 'Product' )
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllProductSales
)
RETURN
Ratio
Product 上的 ALL 將刪除 Product 的任何列上的所有篩選。在圖5-14中,您可以看到該計算的結(jié)果。
圖5-14 Product 表上使用的 ALL 將刪除 Product 所有列中的篩選
到目前為止,您已經(jīng)看到,通過將 CALCULATE 和 ALL 一起使用,可以從一列,多個列或整個表中刪除篩選。CALCULATE 的真正功能在于它提供了多個操作篩選上下文的選項,并且其功能并不止于此。實(shí)際上,可能還希望對不同表中的列進(jìn)行切片來分析百分比,例如,如果按產(chǎn)品類別和客戶所在洲對報告進(jìn)行了切片,那么我們創(chuàng)建的最后一個度量值還不完美,如圖5-15所示。
圖5-15對多個表的列進(jìn)行切片仍然顯示意外結(jié)果
此時,問題很明顯。分母處的度量值從 Product 中刪除了所有篩選,但該篩選保留在 Customer [Continent] 上。因此,分母計算給定洲所有產(chǎn)品的總銷售額。
與前面的方案一樣,可以通過將多個篩選作為 CALCULATE 的參數(shù)來從多個表中刪除該篩選:
Sales Pct All Products and Customers :=
VAR CurrentCategorySales =
[Sales Amount]
VAR AllProductAndCustomersSales =
CALCULATE (
[Sales Amount],
ALL ( 'Product' ),
ALL ( Customer )
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllProductAndCustomersSales
)
RETURN
Ratio
通過在兩個表上使用 ALL,現(xiàn)在 CALCULATE 從兩個表中刪除了篩選。如預(yù)期的那樣,結(jié)果是一個正確累加的百分比,如圖5-16所示。
圖5-16 在兩個表上使用ALL將同時刪除兩個表的篩選上下文
與兩列一樣,兩個表也面臨相同的挑戰(zhàn)。如果用戶將第三表中的另一列添加到上下文中,則該度量值將不會從第三表中刪除篩選。當(dāng)他們想要從任何可能影響計算的表中刪除篩選時,一種可行的解決方案是從事實(shí)表本身中刪除任何篩選。在我們的模型中,事實(shí)表是 Sales。不管哪個篩選與 Sales 表進(jìn)行交互,此度量值都可以計算累加百分比:
Pct All Sales :=
VAR CurrentCategorySales =
[Sales Amount]
VAR AllSales =
CALCULATE (
[Sales Amount],
ALL ( Sales )
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllSales
)
RETURN
Ratio
此度量值利用關(guān)系從可能篩選 Sales 的任何表中刪除篩選。現(xiàn)在,我們無法詳細(xì)解釋其工作方式,因為它利用了擴(kuò)展表,我們在第14章 “高級DAX概念” 中對此進(jìn)行了介紹。您可以通過查看圖5-17來了解其行為,在圖5-17中,我們從報告中刪除了金額,并在各列上添加了日歷年。請注意,日歷年屬于日期表,該度量值未使用。但是,刪除日期篩選是從 Salse 中刪除篩選的一部分。
圖5-17 事實(shí)表上的 ALL 也會從相關(guān)表中刪除所有篩選
在結(jié)束百分比這一長期練習(xí)之前,我們想展示篩選上下文操作的另一個最終示例。正如您在圖5-17中看到的那樣,百分比始終與合計相對,完全符合預(yù)期。如果目標(biāo)是僅計算占當(dāng)年總計的百分比,該怎么辦?在這種情況下,需要仔細(xì)準(zhǔn)備由 CALCULATE 創(chuàng)建的新篩選上下文。實(shí)際上,分母需要計算銷售總額,而不考慮除當(dāng)前年度以外的任何篩選條件。這需要兩個操作:
- 從事實(shí)表中刪除所有篩選
- 恢復(fù)年度篩選
請注意,這兩個條件是同時應(yīng)用的,盡管看起來這兩個步驟是一個接一個地進(jìn)行的。您已經(jīng)學(xué)習(xí)了如何從事實(shí)表中刪除所有篩選。最后一步是學(xué)習(xí)如何還原現(xiàn)有篩選。
注意
本節(jié)的目的是說明操作篩選上下文的基本技術(shù)。在本章的后面,您將看到另一種更簡單的方法,可以通過使用 ALLSELECTED 來解決這一特定要求—占可見總數(shù)的百分比。
在第3章 “使用基本表函數(shù)” 中,您學(xué)習(xí)了 VALUES 函數(shù)。VALUES 返回當(dāng)前篩選上下文中的列的值列表。由于 VALUES 的結(jié)果是一個表,因此可以將其用作 CALCULATE 的篩選參數(shù)。結(jié)果, CALCULATE 在給定列上應(yīng)用了一個篩選,將其值限制為 VALUES 返回的值。看下面的代碼:
Pct All Sales CY :=
VAR CurrentCategorySales =
[Sales Amount]
VAR AllSalesInCurrentYear =
CALCULATE (
[Sales Amount],
ALL ( Sales ),
VALUES ( 'Date'[Calendar Year] )
)
VAR Ratio =
DIVIDE (
CurrentCategorySales,
AllSalesInCurrentYear
)
RETURN
Ratio
一旦在報告中使用該度量值,則該度量值每年占比為100%,并且仍將計算除年份以外其他篩選的百分比。您會在圖5-18中看到這一點(diǎn)。
圖5-18 通過使用VALUES,可以恢復(fù)篩選器上下文的一部分,并從原始篩選器上下文讀取它。
圖5-19 描述了這個復(fù)雜公式的全部行為。
這是該圖的回顧:
- 包含4.22%(2007年手機(jī)銷售量)的單元格具有一個篩選器上下文,該篩選器上下文篩選CY 2007 的手機(jī)。
-
CALCULATE 有兩個篩選參數(shù):ALL(Sales)和 VALUES(Date [Calendar Year])。
- ALL(Sales)從 Sales 表中刪除篩選。
- VALUES(Date [Calendar Year])在原始篩選上下文中評估 VALUES 函數(shù),該函數(shù)仍受列中 CY 2007 的影響。因此,它返回當(dāng)前篩選上下文中唯一可見的年份,即 CY 2007。
CALCULATE 的兩個篩選參數(shù)將應(yīng)用于當(dāng)前篩選上下文,從而導(dǎo)致一個篩選上下文僅包含 Calendar Year上的篩選。分母僅在 CY 2007 的篩選上下文中計算總銷售額。
清楚理解 CALCULATE 的篩選參數(shù)是在調(diào)用 CALCULATE 的原始篩選上下文中評估的,這一點(diǎn)至關(guān)重要。實(shí)際上,CALCULATE 會更改篩選上下文,但這僅在評估篩選參數(shù)之后發(fā)生。
在表上使用 ALL,緊接著在列上使用 VALUES ,是一種用于用同一列上的篩選替換篩選上下文的技術(shù)。
注意
前面的示例也可以通過使用 ALLEXCEPT 獲得。ALL / VALUES 的語義與 ALLEXCEPT 不同。在第10章 “使用篩選上下文” 中,您將看到 ALLEXCEPT 與 ALL / VALUES 技術(shù)之間差異的完整描述。
如您在這些示例中所見,CALCULATE 本身并不是一個復(fù)雜的函數(shù)。它的行為很容易描述。同時,一旦您開始使用 CALCULATE,代碼的復(fù)雜性就會變得很高。確實(shí),您需要專注于篩選上下文并確切了解 CALCULATE 如何生成新的篩選上下文。一個簡單的百分比就隱藏了很多復(fù)雜性,而復(fù)雜性全在細(xì)節(jié)中。在真正掌握評估上下文的處理之前,DAX有點(diǎn)神秘。釋放語言全部功能的關(guān)鍵在于掌握評估上下文。此外,在所有這些示例中,我們只需要管理一個CALCULATE。在復(fù)雜的公式中,由于存在許多 CALCULATE 實(shí)例,因此在同一代碼中具有四個或五個不同的上下文并不常見。
整個關(guān)于百分比的本章節(jié)至少閱讀兩次,這是一個好主意。根據(jù)我們的經(jīng)驗,二讀要容易得多,它使您可以專注于代碼的重要方面。我們想展示這個例子,以強(qiáng)調(diào)理論的重要性。代碼的微小變化對公式計算出的數(shù)字有重要影響。在您進(jìn)行了二讀之后,請繼續(xù)進(jìn)行下一節(jié),下節(jié)我們將重點(diǎn)放在理論上而不是在實(shí)際示例上。
KEEPFILTERS簡介
在前面的部分中,您了解到 CALCULATE 的篩選參數(shù)會覆蓋同一列上任何以前存在的篩選。因此,無論 Product [Category] 上是否存在任何先前的篩選,以下度量值都會返回 Audio 的銷售:
Audio Sales :=
CALCULATE (
[Sales Amount],
'Product'[Category] = "Audio"
)
正如您在圖5-20中看到的那樣,Audio 的值在報表的所有行上都重復(fù)。
圖5-20 Audio Sales 始終顯示音頻產(chǎn)品的銷售。
CALCULATE 會覆蓋應(yīng)用新篩選的列上的現(xiàn)有篩選。篩選上下文的所有其余列均保持不變。如果您不想覆蓋現(xiàn)有篩選,則可以使用 KEEPFILTERS 包裝篩選參數(shù)。例如,如果要在篩選上下文中顯示Audio 時顯示 Audio 銷售量,而在篩選上下文中不顯示 Audio 時顯示空白值,則可以編寫以下度量值:
Audio Sales KeepFilters :=
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Category] = "Audio" )
)
KEEPFILTERS 是您學(xué)習(xí)的第二個CALCULATE 修飾符,第一個是 ALL。我們將在本章后面進(jìn)一步介紹 CALCULATE 修飾符。KEEPFILTERS 更改了 Calculate 將篩選應(yīng)用于新篩選上下文的方式。與其在同一列上覆蓋現(xiàn)有篩選,不如將新篩選添加到現(xiàn)有篩選中。因此,只有已篩選類別已包含在篩選上下文中的單元格才會產(chǎn)生可見結(jié)果。您會在圖5-21中看到這一點(diǎn)。
圖5-21 Audio Sales KeepFilters 僅顯示 Audio 行和 Total 行的 Audio 銷售
KEEPFILTERS 完全按照其名稱的含義進(jìn)行操作。它不會覆蓋現(xiàn)有篩選,而是保留現(xiàn)有篩選并將新篩選添加到篩選上下文中。我們可以使用圖5-22來描述行為。
圖5-22 使用 KEEPFILTERS 生成的篩選上下文同時篩選 Cell 和 Audio
因為 KEEPFILTERS 避免了覆蓋,所以將由 CALCULATE 的 filter 參數(shù)生成的新篩選添加到上下文中。如果我們在 Cell Phone 行中查看度量值 Audio Sales KeepFilters 的值,則結(jié)果篩選上下文包含兩個篩選:一個篩選為 Cell Phone ;另一個篩選為 Audio。這兩個條件的交集導(dǎo)致一個空集,從而產(chǎn)生一個空白結(jié)果。
當(dāng)在一列中選擇了多個元素時,KEEPFILTERS 的行為將更加清晰。例如,考慮以下度量值帶有和不帶有 KEEPFILTERS 篩選 Audio 和 Computers 的情形:
Always Audio-Computers :=
CALCULATE (
[Sales Amount],
'Product'[Category] IN { "Audio", "Computers" }
)
KeepFilters Audio-Computers :=
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Category] IN { "Audio", "Computers" } )
)
圖5-23中的報告顯示,帶有 KEEPFILTERS 的版本僅計算 Audio 和 Computers 的銷售額值,而所有其他類別均留空。總計行僅考慮 Audio 和 Computers。
圖5-23 使用KEEPFILTERS 將原始篩選上下文和新篩選上下文合并在一起
KEEPFILTERS可以與謂詞或表一起使用。實(shí)際上,先前的代碼也可以用更冗長的方式編寫:
KeepFilters Audio-Computers :=
CALCULATE (
[Sales Amount],
KEEPFILTERS (
FILTER (
ALL ( 'Product'[Category] ),
'Product'[Category] IN { "Audio", "Computers" }
)
)
)
這僅僅是出于教育目的的示例。您應(yīng)該使用可用于篩選參數(shù)的最簡單的謂詞語法。篩選單個列時,可以避免顯式編寫 FILTER。但是,稍后您將看到更復(fù)雜的篩選條件需要用顯式的 FILTER。在那些情況下,可以在顯式的 FILTER 函數(shù)周圍使用 KEEPFILTERS 修飾符,下一節(jié)將會看到。
篩選單列
在上一節(jié)中,我們介紹了引用 CALCULATE 中單列的篩選參數(shù)。重要的是要注意,您可以在一個表達(dá)式中具有對同一列的多個引用。例如,以下是有效的語法,因為它兩次引用同一列(Sales [Net Price] )。
Sales 10-100 :=
CALCULATE (
[Sales Amount],
Sales[Net Price] >= 10 && Sales[Net Price] <= 100
)
實(shí)際上,這被轉(zhuǎn)換為以下語法:
Sales 10-100 :=
CALCULATE (
[Sales Amount],
FILTER (
ALL ( Sales[Net Price] ),
Sales[Net Price] >= 10 && Sales[Net Price] <= 100
)
)
CALCULATE 生成的結(jié)果篩選上下文僅在 Sales [Net Price] 列上添加一個篩選。關(guān)于謂詞在CALCULATE 中作為篩選參數(shù)的一個重要說明是,盡管它們看起來像條件,但它們是表。如果您閱讀了最后兩個代碼片段中的第一個,則看起來 CALCULATE 會評估條件。而是 CALCULATE 評估滿足條件的所有 Sales [Net Price] 值的列表。然后,CALCULATE 使用此值表將篩選應(yīng)用于模型。
當(dāng)兩個條件在邏輯 AND 中時,它們可以表示為兩個單獨(dú)的篩選。實(shí)際上,上一個表達(dá)式等同于以下表達(dá)式:
Sales 10-100 :=
CALCULATE (
[Sales Amount],
Sales[Net Price] >= 10,
Sales[Net Price] <= 100
)
但是,請記住,CALCULATE 的多個篩選參數(shù)始終與邏輯 AND 合并。因此,對于邏輯 OR 語句,必須使用單個篩選,例如以下措施:
Sales Blue+Red :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red" || 'Product'[Color] = "Blue"
)
通過編寫多個篩選,您可以在一個篩選上下文中合并兩個獨(dú)立的篩選。由于沒有同時具有藍(lán)色和紅色的產(chǎn)品,因此以下措施始終會產(chǎn)生空白結(jié)果:
Sales Blue and Red :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red",
'Product'[Color] = "Blue"
)
實(shí)際上,前一個度量值對應(yīng)于使用單個篩選的以下度量值:
Sales Blue and Red :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red" && 'Product'[Color] = "Blue"
)
filter 參數(shù)始終返回篩選上下文中允許的顏色空白列表。因此,該度量值始終返回空白值。
只要篩選參數(shù)引用單個列,就可以使用謂詞。我們建議您這樣做,因為生成的代碼更容易閱讀。您也應(yīng)該針對邏輯 AND 條件執(zhí)行此操作。但是,永遠(yuǎn)不要忘記您僅依賴語法加糖。CALCULATE 始終與表一起使用,盡管緊湊語法可能會另外建議。
另一方面,只要篩選參數(shù)中有兩個或多個不同的列引用,就必須將 FILTER 條件寫為表表達(dá)式。您將在下一節(jié)中學(xué)習(xí)。
復(fù)雜條件下的篩選
引用多個列的篩選參數(shù)需要顯式的表表達(dá)式。重要的是要了解可用于編寫此類篩選的不同技術(shù)。請記住,用謂詞創(chuàng)建所需列數(shù)最少的篩選通常是最佳實(shí)踐。
考慮一個僅將金額大于或等于1,000的交易的銷售額相加的度量值。要獲取每筆交易的金額,需要將 Quantity 和 Net Price 列相乘。這是因為您沒有在示例 Contoso 數(shù)據(jù)庫中為 Sales 表的每一行存儲該金額的列。您可能會想編寫類似以下表達(dá)式的代碼,但不幸的是,該表達(dá)式無法使用:
Sales Large Amount :=
CALCULATE (
[Sales Amount],
Sales[Quantity] * Sales[Net Price] >= 1000
)
該代碼無效,因為 filter 參數(shù)在同一表達(dá)式中引用了兩個不同的列。因此,DAX無法將其自動轉(zhuǎn)換為合適的 FILTER 條件。編寫所需篩選的最佳方法是使用一個表,該表只有謂詞中引用列的組合:
Sales Large Amount :=
CALCULATE (
[Sales Amount],
FILTER (
ALL ( Sales[Quantity], Sales[Net Price] ),
Sales[Quantity] * Sales[Net Price] >= 1000
)
)
這將導(dǎo)致一個篩選上下文,該篩選上下文包含一個具有兩列和若干行的篩選,它們對應(yīng)于滿足篩選條件的“ Quantity 和Net Price ”的唯一組合。如圖5-24所示。
圖5-24 多列篩選僅包含 Quantity 和 Net Price 的組合,產(chǎn)生的結(jié)果大于或等于1,000。
該篩選產(chǎn)生圖5-25的結(jié)果。
圖5-25 Sales Large Amount 僅顯示大額交易
請注意,圖5-25中的切片器沒有篩選任何值:顯示的兩個值是 Net Price 的最小值和最大值。下一步是顯示度量值如何與切片器交互。在類似 Sales Large Amount 的度量值中,當(dāng)您覆蓋 Quantity 或 Net Price 上的現(xiàn)有篩選時,需要注意。確實(shí),由于 filter 參數(shù)在兩列上使用 ALL,因此它將忽略同一列上任何先前存在的篩選,在此示例中包括切片器的篩選。圖5-26中的報告與圖5-25相同,但是這一次,切片器篩選了500到3,000之間的 Net Price 格。結(jié)果令人驚訝。
圖5-26 當(dāng)前價格范圍內(nèi)沒有 Audio 的銷售,仍然顯示 Sales Large Amount 結(jié)果
Audio 和 Music, Movies and Audio Books 的 Sales Large Amount 的值存在是意外的。實(shí)際上,對于這兩個類別, Net Price 格范圍在500到3,000之間(這是切片器生成的篩選條件)沒有銷售。盡管如此, Sales Large Amount 度量值仍在顯示結(jié)果。
原因是切片器創(chuàng)建的 Net Price 篩選上下文被 Sales Large Amount 度量值忽略,它覆蓋了 Quantity 和 Net Price 上的現(xiàn)有篩選。如果您仔細(xì)比較圖5-25和5-26,您會注意到 Sales Large Amount 的值是相同的,就像未將切片器添加到報告中一樣。的確,Sales Large Amount 完全忽略了切片器。
如果您專注于某個單元格,例如 Audio 的 Sales Large Amount 的值,則執(zhí)行以下代碼計算其值:
Sales Large Amount :=
CALCULATE (
CALCULATE (
[Sales Amount],
FILTER (
ALL ( Sales[Quantity], Sales[Net Price] ),
Sales[Quantity] * Sales[Net Price] >= 1000
)
),
'Product'[Category] = "Audio",
Sales[Net Price] >= 500
)
從代碼中,您可以看到最里面的 ALL 將忽略外部 CALCULATE 設(shè)置的 Sales [Net Price] 上的篩選。在那種情況下,您可以使用 KEEPFILTERS 來避免覆蓋現(xiàn)有篩選:
Sales Large Amount KeepFilter :=
CALCULATE (
[Sales Amount],
KEEPFILTERS (
FILTER (
ALL ( Sales[Quantity], Sales[Net Price] ),
Sales[Quantity] * Sales[Net Price] >= 1000
)
)
)
新的 Sales Large Amount KeepFilter 度量值產(chǎn)生的結(jié)果如圖5-27所示。
圖5-27 使用 KEEPFILTERS,計算也考慮了外部限幅器
指定復(fù)雜篩選的另一種方法是使用表篩選而不是列篩選。這是DAX新手的首選技術(shù)之一,盡管使用起來非常危險。實(shí)際上,可以使用表篩選來編寫先前的度量值:
Sales Large Amount Table :=
CALCULATE (
[Sales Amount],
FILTER (
Sales,
Sales[Quantity] * Sales[Net Price] >= 1000
)
)
您可能還記得,CALCULATE 的所有篩選參數(shù)都是在 CALCULATE 本身之外的篩選上下文中求值的。因此,基于 Sales 的迭代僅考慮在現(xiàn)有篩選上下文中篩選的行,其中包含基于 Net Price 的篩選。因此,Sales Large Amount Table 度量值的語義對應(yīng)于 Sales Large Amount KeepFilter 度量值。
盡管此技術(shù)看起來很簡單,但您應(yīng)謹(jǐn)慎使用它,因為它可能會對性能和結(jié)果準(zhǔn)確性產(chǎn)生嚴(yán)重影響。我們將在第14章中詳細(xì)介紹這些問題。現(xiàn)在,請記住,最佳方法是始終使用盡可能少的列數(shù)篩選。
此外,您應(yīng)該避免使用表篩選,因為它們通常更“昂貴”。Sales表可能非常大,并且逐行對其進(jìn)行掃描以評估謂詞可能是一項耗時的操作。另一方面,Sales Large Amount KeepFilter 中的篩選僅迭代 Quantity 和 Net Price 的唯一組合的 Quantity 。該數(shù)字通常比整個Sales表的行數(shù)小得多。
CALCULATE 中的評估順序
當(dāng)您查看DAX代碼時,評估的自然順序便是最里面的優(yōu)先。例如,看下面的表達(dá)式:
Sales Amount Large :=
SUMX (
FILTER ( Sales, Sales[Quantity] >= 100 ),
Sales[Quantity] * Sales[Net Price]
)
DAX需要在開始 SUMX 評估之前評估 FILTER 的結(jié)果。實(shí)際上, SUMX 會迭代一個表。由于該表是 FILTER 的結(jié)果,因此 SUMX 無法在 FILTER 完成其工作之前開始執(zhí)行。除 CALCULATE 和 CALCULATETABLE 外,此規(guī)則對所有DAX函數(shù)均適用。實(shí)際上,CALCULATE 首先評估其篩選參數(shù),并且最后才評估第一個參數(shù),即提供 CALCULATE 結(jié)果的表達(dá)式。
而且,由于 CALCULATE 更改了篩選上下文,因此事情變得更加復(fù)雜。所有篩選參數(shù)均在 CALCULATE 之外的篩選上下文中執(zhí)行,并且每個篩選均獨(dú)立評估。相同 CALCULATE 中篩選的順序無關(guān)緊要。因此,以下所有措施都是完全等效的:
Sales Red Contoso :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red",
KEEPFILTERS ( 'Product'[Brand] = "Contoso" )
)
Sales Red Contoso :=
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Brand] = "Contoso" ),
'Product'[Color] = "Red"
)
Sales Red Contoso :=
VAR ColorRed =
FILTER (
ALL ( 'Product'[Color] ),
'Product'[Color] = "Red"
)
VAR BrandContoso =
FILTER (
ALL ( 'Product'[Brand] ),
'Product'[Brand] = "Contoso"
)
VAR SalesRedContoso =
CALCULATE (
[Sales Amount],
ColorRed,
KEEPFILTERS ( BrandContoso )
)
RETURN
SalesRedContoso
使用變量定義的 Sales Red Contoso 版本比其他版本更冗長,但是如果篩選是帶有顯式篩選的復(fù)雜表達(dá)式,則還是要使用它。這樣,更容易理解篩選是在“計算”之前評估的。
對于嵌套的 CALCULATE 語句,此規(guī)則變得更加重要。實(shí)際上,最外面的篩選首先被應(yīng)用,最里面的篩選隨后被應(yīng)用。了解嵌套 CALCULATE 語句的行為很重要,因為每次嵌套度量值調(diào)用時都會遇到這種情況。例如,考慮以下度量值,其中 Sales Green 調(diào)用 Sales Red:
Sales Red :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red"
)
Green calling Red :=
CALCULATE (
[Sales Red],
'Product'[Color] = "Green"
)
為了使嵌套的度量值調(diào)用更加明顯,我們可以通過以下方式擴(kuò)展 Sales Green:
Green calling Red Exp :=
CALCULATE (
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red"
),
'Product'[Color] = "Green"
)
評估順序如下:
- 首先,外部 CALCULATE 應(yīng)用篩選,Product [Color] =“ Green”。
- 其次,內(nèi)部 CALCULATE 應(yīng)用篩選,Product [Color] =“ Red”。該篩選將覆蓋先前的篩選。
- 最后,DAX用 Product [Color] =“ Red” 的篩選計算 [Sales Amount]。
因此,紅色和綠色調(diào)用紅色的結(jié)果仍然是紅色,如圖5-28所示。
該報告顯示每個類別的銷售額,紅色,綠色(紅色)和紅色(綠色)。我們注意到,最后三個度量值返回相同的結(jié)果,即按類別對紅色產(chǎn)品的銷售額。
注意
我們提供的說明僅用于培訓(xùn)目的。實(shí)際上,引擎對篩選上下文使用惰性評估,因此,在存在諸如前面代碼的篩選參數(shù)覆蓋的情況下,可能永遠(yuǎn)不會對外部篩選進(jìn)行評估,因為它是無用的。但是,此行為僅僅是優(yōu)化,它不會以任何方式改變 CALCULATE 的語義。
我們可以通過另一個示例查看評估的順序以及如何評估篩選上下文。請考慮以下度量值:
Sales YB :=
CALCULATE (
CALCULATE (
[Sales Amount],
'Product'[Color] IN { "Yellow", "Black" }
),
'Product'[Color] IN { "Black", "Blue" }
)
由 Sales YB 生成的篩選上下文的評估在圖5-29中可見。
如前所示,Product [Color] 上最里面的篩選會覆蓋最外面的篩選。因此,度量值結(jié)果顯示黃色或黑色的乘積之和。通過在最里面的 CALCULATE 中使用 KEEPFILTERS,通過保留兩個篩選而不是覆蓋現(xiàn)有篩選來構(gòu)建篩選上下文:
Sales YB KeepFilters :=
CALCULATE (
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Color] IN { "Yellow", "Black" } )
),
'Product'[Color] IN { "Black", "Blue" }
)
圖5-29 最里面的篩選覆蓋外面的篩選
由 Sales YB KeepFilters 生成的篩選上下文的評估在圖5-30中可見。
圖5-30 通過使用 KEEPFILTERS,CALCULATE不會覆蓋以前的篩選上下文
因為兩個篩選保持在一起,所以它們是相交的。因此,在新的篩選上下文中,唯一可見的顏色是黑色,因為它是兩個篩選中唯一的值。
但是,同一 CALCULATE 中的篩選參數(shù)的順序無關(guān)緊要,因為它們是獨(dú)立應(yīng)用于篩選上下文的。