5.1 CALCULATE和CALCULATETABLE介紹

第5章了解 CALCULATECALCULATETABLE

在本章中,我們將繼續(xù)探索DAX語言的強(qiáng)大功能,并詳細(xì)說明一個函數(shù):CALCULATE。相同的注意事項適用于 CALCULATETABLE,它計算并返回表而不是標(biāo)量值。為了簡單起見,我們將在示例中引用 CALCULATE,但是請記住 CALCULATETABLE 顯示相同的行為。

CALCULATE 是DAX中最重要,最有用和最復(fù)雜的函數(shù),因此值得用一整章來講解。該函數(shù)本身易于學(xué)習(xí);它僅執(zhí)行一些任務(wù)。復(fù)雜性來自于以下事實(shí):CALCULATECALCULATETABLE 是DAX中唯一可以創(chuàng)建新篩選上下文的函數(shù)。因此,盡管它們是簡單的函數(shù),但在公式中使用 CALCULATECALCULATETABLE 會立即增加其復(fù)雜性。

本章與上一章一樣艱難。我們建議您仔細(xì)閱讀它,對 CALCULATE 有一個大致的了解,然后繼續(xù)學(xué)習(xí)本書的其余部分。一旦您對某個公式感到迷惑,請回到本章并從頭開始重新閱讀。每次閱讀時,您都會有新的收獲。

CALCULATECALCULATETABLE介紹

上一章描述了兩個評估上下文:行上下文和篩選上下文。行上下文自動存在于計算列中,并且可以使用迭代函數(shù)以編程方式創(chuàng)建行上下文。另一方面,篩選上下文是由報表創(chuàng)建的,我們還沒有描述如何以編程方式創(chuàng)建篩選上下文。CALCULATECALCULATETABLE 是操作篩選上下文所需的唯一函數(shù)。確實(shí),CALCULATECALCULATETABLE 是可以通過操作現(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

圖5-1 通過這三個度量值可對不同類別毛利的快速了解

構(gòu)建報告的下一步更加復(fù)雜。實(shí)際上,我們想要的最終報告是圖5-2中的報告,該報告顯示了另外兩列:Contoso 品牌產(chǎn)品的毛利和毛利率(以數(shù)量和百分比表示)。

圖5-2

圖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 MarginGM%已經(jīng)可以計算 Contoso 的值。從圖5-2中可以看到,ContosoGross Margin (毛利)等于3,877,070.65,GM%(毛利率)等于52.73%。如圖5-3所示,通過按品牌劃分的基本度量值的 Gross MarginGM% 可以獲得相同的數(shù)字。

圖5-3

圖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)行求值。因此,通過利用CALCULATEContoso MarginContoso 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 時,我們還包括 CALCULATETABLECALCULATE 不會修改篩選上下文:它是通過將其篩選參數(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”CALCULATEProduct [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列中 的所有行/品牌報告的銷售額與ContosoSales Amount 的價值相同。

圖5-4

圖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

圖5-5 Litware 的篩選被 CALCULATE 評估的 Contoso 篩選覆蓋

CALCULATE不會覆蓋整個原始篩選器上下文。它僅替換 filter 參數(shù)中包含的列上先前存在的篩選。實(shí)際上,如果將報表更改為按 Product [Category] 切片,結(jié)果將有所不同,如圖5-6所示。

圖5-6

圖5-6 如果報表按類別篩選,則品牌上的篩選將被合并并且不會覆蓋

現(xiàn)在,報告正在篩選 Product [Category],而 CALCULATEProduct [Brand] 上應(yīng)用篩選以評估 Contoso Sales 度量值。這兩個篩選不適用于 Product 的同一列,因此,不會發(fā)生覆蓋,并且兩個篩選可以作為新的篩選上下文一起工作。結(jié)果,每個單元格都顯示了給定類別的 ContosoSales Amount 。如圖5-7所示。

圖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)確定義所需的計算非常重要。在這組示例中,您將了解CALCULATEALL 函數(shù)的不同用法是如何提供不同的結(jié)果的。

從簡單的百分比計算開始。要構(gòu)建以下報告,以顯示銷售額及其占總計的百分比。可以在圖5-8中看到想要獲得的結(jié)果。

圖5-8

圖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

圖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

圖5-10 將 Color 添加到報告中會在 Color 級別上產(chǎn)生意外結(jié)果

從百分比來看,類別級別的值是正確的,而顏色級別的值是錯誤的。事實(shí)上,顏色百分比并沒有累加起來——既沒加入類別級別,也沒加入100%。要了解這些值的含義以及如何對其進(jìn)行評估,始終專注于一個單元格并準(zhǔn)確了解篩選上下文發(fā)生了什么是很有幫助的。專注于圖5-11。

圖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調(diào)換顏色和類別順序后,結(jié)果看起來更加合理

圖5-12中的報告更有意義。該度量值計算出的結(jié)果相同,但是由于報表的布局,它更加直觀。顯示的百分比是給定顏色內(nèi)各類別的百分比。一個一個顏色,百分比總和為100%。

換句話說,當(dāng)要求用戶計算百分比時,應(yīng)特別注意確定百分比的分母。CALCULATEALL 是要使用的主要工具,但是公式的規(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

圖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

圖5-14 Product 表上使用的 ALL 將刪除 Product 所有列中的篩選

到目前為止,您已經(jīng)看到,通過將 CALCULATEALL 一起使用,可以從一列,多個列或整個表中刪除篩選。CALCULATE 的真正功能在于它提供了多個操作篩選上下文的選項,并且其功能并不止于此。實(shí)際上,可能還希望對不同表中的列進(jìn)行切片來分析百分比,例如,如果按產(chǎn)品類別和客戶所在洲對報告進(jìn)行了切片,那么我們創(chuàng)建的最后一個度量值還不完美,如圖5-15所示。

圖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

圖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

圖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

圖5-18 通過使用VALUES,可以恢復(fù)篩選器上下文的一部分,并從原始篩選器上下文讀取它。

圖5-19 描述了這個復(fù)雜公式的全部行為。

圖5-19 此圖的關(guān)鍵是仍然在原始篩選上下文中評估 VALUES

這是該圖的回顧:

  • 包含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章 “使用篩選上下文” 中,您將看到 ALLEXCEPTALL / 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ù)。

image

圖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-21l 行的 Audio 銷售

圖5-21 Audio Sales KeepFilters 僅顯示 Audio 行和 Total 行的 Audio 銷售

KEEPFILTERS 完全按照其名稱的含義進(jìn)行操作。它不會覆蓋現(xiàn)有篩選,而是保留現(xiàn)有篩選并將新篩選添加到篩選上下文中。我們可以使用圖5-22來描述行為。

圖5-22 使用 KEEPFILTERS 生成的篩選上下文同時篩選 Cell 和 Audio

圖5-22 使用 KEEPFILTERS 生成的篩選上下文同時篩選 Cell 和 Audio

因為 KEEPFILTERS 避免了覆蓋,所以將由 CALCULATEfilter 參數(shù)生成的新篩選添加到上下文中。如果我們在 Cell Phone 行中查看度量值 Audio Sales KeepFilters 的值,則結(jié)果篩選上下文包含兩個篩選:一個篩選為 Cell Phone ;另一個篩選為 Audio。這兩個條件的交集導(dǎo)致一個空集,從而產(chǎn)生一個空白結(jié)果。

當(dāng)在一列中選擇了多個元素時,KEEPFILTERS 的行為將更加清晰。例如,考慮以下度量值帶有和不帶有 KEEPFILTERS 篩選 AudioComputers 的情形:

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 的版本僅計算 AudioComputers 的銷售額值,而所有其他類別均留空。總計行僅考慮 AudioComputers

圖5-23

圖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的交易的銷售額相加的度量值。要獲取每筆交易的金額,需要將 QuantityNet 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)于滿足篩選條件的“ QuantityNet Price ”的唯一組合。如圖5-24所示。

圖5-24

圖5-24 多列篩選僅包含 QuantityNet Price 的組合,產(chǎn)生的結(jié)果大于或等于1,000。

該篩選產(chǎn)生圖5-25的結(jié)果。

圖5-25 Sales Large Amount 僅顯示大額交易

圖5-25 Sales Large Amount 僅顯示大額交易

請注意,圖5-25中的切片器沒有篩選任何值:顯示的兩個值是 Net Price 的最小值和最大值。下一步是顯示度量值如何與切片器交互。在類似 Sales Large Amount 的度量值中,當(dāng)您覆蓋 QuantityNet Price 上的現(xiàn)有篩選時,需要注意。確實(shí),由于 filter 參數(shù)在兩列上使用 ALL,因此它將忽略同一列上任何先前存在的篩選,在此示例中包括切片器的篩選。圖5-26中的報告與圖5-25相同,但是這一次,切片器篩選了500到3,000之間的 Net Price 格。結(jié)果令人驚訝。

圖5-26

圖5-26 當(dāng)前價格范圍內(nèi)沒有 Audio 的銷售,仍然顯示 Sales Large Amount 結(jié)果

AudioMusic, Movies and Audio BooksSales Large Amount 的值存在是意外的。實(shí)際上,對于這兩個類別, Net Price 格范圍在500到3,000之間(這是切片器生成的篩選條件)沒有銷售。盡管如此, Sales Large Amount 度量值仍在顯示結(jié)果。

原因是切片器創(chuàng)建的 Net Price 篩選上下文被 Sales Large Amount 度量值忽略,它覆蓋了 QuantityNet Price 上的現(xiàn)有篩選。如果您仔細(xì)比較圖5-25和5-26,您會注意到 Sales Large Amount 的值是相同的,就像未將切片器添加到報告中一樣。的確,Sales Large Amount 完全忽略了切片器。

如果您專注于某個單元格,例如 AudioSales 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,計算也考慮了外部限幅器

圖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 中的篩選僅迭代 QuantityNet 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í)行。除 CALCULATECALCULATETABLE 外,此規(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"
)

評估順序如下:

  1. 首先,外部 CALCULATE 應(yīng)用篩選,Product [Color] =“ Green”
  2. 其次,內(nèi)部 CALCULATE 應(yīng)用篩選,Product [Color] =“ Red”。該篩選將覆蓋先前的篩選。
  3. 最后,DAX用 Product [Color] =“ Red” 的篩選計算 [Sales Amount]

因此,紅色和綠色調(diào)用紅色的結(jié)果仍然是紅色,如圖5-28所示。

圖5-28最后三個度量值返回相同的結(jié)果,該結(jié)果始終是紅色產(chǎn)品的銷售額

該報告顯示每個類別的銷售額,紅色,綠色(紅色)和紅色(綠色)。我們注意到,最后三個度量值返回相同的結(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

圖5-29 最里面的篩選覆蓋外面的篩選

Sales YB KeepFilters 生成的篩選上下文的評估在圖5-30中可見。

圖5-30

圖5-30 通過使用 KEEPFILTERS,CALCULATE不會覆蓋以前的篩選上下文

因為兩個篩選保持在一起,所以它們是相交的。因此,在新的篩選上下文中,唯一可見的顏色是黑色,因為它是兩個篩選中唯一的值。

但是,同一 CALCULATE 中的篩選參數(shù)的順序無關(guān)緊要,因為它們是獨(dú)立應(yīng)用于篩選上下文的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容