DAX連接表系列(四) ⅤAR変量(1)

DAX連接表系列 (四)VAR變量(第1部分)

? ? (一) 遲到的說明

? ? ? 在本系列之前,先啰嗦幾句,其實在編寫這個系列前,就應該先需要一個說明:
? ? ? DAX所在數(shù)據(jù)模型里存在三大引擎(也不確定,但已足夠):壓縮引擎、存儲引擎、公式引擎。一般我們先不關心壓縮引擎(不可控,只能做數(shù)據(jù)優(yōu)化等來適應它)。
? ? ? 另外兩個引擎的順序是先由存儲引擎依據(jù)定義,在內(nèi)存中緩存出存儲數(shù)據(jù)(有一個專門的名稱叫做“物化”),然后公式引擎引用該存儲數(shù)據(jù)進行計算。換一種方式(DAX總是可以使用各種方式來理解)來說,所有“物化”后由公式請求引用的存儲數(shù)據(jù)(也對應于DAX的兩大步驟:篩選+計算的最簡單有力、最恰當?shù)慕忉專┛赡苡袃煞N情況:
? ? ? (1)只針對同一個表(無論一列或多列);
? ? ? (2)針對關系連接的多個列表(兩個或多個關系列表)。
? ? ? ? 實際上,99.9%的情況是這樣。就這么簡單!

? ? ? 第一種情況是我們最樂于見到的、也是最熟悉的,因為它不需要考慮關系,就像在Excel里的一個Sheet表數(shù)據(jù)區(qū)域里工作一樣。如果還需要其他sheet表的內(nèi)容,那么,就需要使用VLOOKUP引用(該引用其實就存在共同的“關系列”)。
? ? ? 我們對于DAX關系列表的最好理解方式是:參考最簡單的VLOOKUP函數(shù),而不必是復雜的關系型數(shù)據(jù)庫理論。問題來了,既然前面第一種情況是我們最樂于見到的。那么,無論是創(chuàng)建數(shù)據(jù)模型,還是定義DAX,所有這些都在有意無意的趨向于用一個表的思維方式方法來理解,這包括:
? ? ? ? 我們前面所說的擴展表、“合縱連橫”的寬表、列表集合函數(shù)定義的列表子集、以及添加參數(shù)表、變量、Power BI的新表等等。
? ? ? ? 它們都有一個共同的目標:通過這些列表方式,構建一個計算列表。所以,我們把這些列表“物化”的方式,統(tǒng)統(tǒng)可以使用一個定義:列表的連接或連接表。當然,僅僅只是一種方式而已。
? ? ? ? 我們接下來要說的是列表方式之一的變量。

? ? (二) 何為VAR變量

? ? ? 什么是VAR變量?

? ? ? VAR變量的語法非常簡單,命名一個DAX代碼的表達式,給個名稱即可。
? ? ? 依據(jù)DAX引擎原理,變量的實際是一個存儲數(shù)據(jù)(內(nèi)存虛擬數(shù)據(jù)),存在于公式引擎之前,并能被公式引用的數(shù)據(jù)。
? ? ? 當變量被賦值后,即在被公式引用后的執(zhí)行過程中,該值不會改變?;诖耍脗鹘y(tǒng)的編程語言環(huán)境來理解它,變量的作用更像是常量,而不是正則變量。
? ? ? ? 注意:該部分內(nèi)容主要參考官方的內(nèi)容,并做了進一步的簡體、整理,集中做匯總性專題介紹。

? ? ? 能使用的版本

? ? ? ?在DAX中,VAR變量(Variables)還是一個相對較新的特征,它可以在以下版本中使用。

? ? ? ? ? Power BI Desktop
? ? ? ? ? Excel 2016
? ? ? ? ? SSAS Tabular 2016
? ? ? ? ? 注意:Variables變量不能在Excel 2013或Excel 2010中使用。

? ? ? ? ? 基本語法

? ? ? (1)我們通常將變量稱為 “VAR語法”。它總是與兩個新的關鍵字 VAR 和RETURN一起使用,這也是將VAR變量用于DAX公式的典型寫法。 你必須始終先使用VAR定義,然后使用RETURN--返回來編寫一個有效的、完整的、包含變量的DAX公式:
? ? ? ? ? myFormula = VAR = VariableName1 =? ? 變量1? //? 一個有效的DAX公式? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? VAR = VariableName2 =? ? 變量2 //? 其他有效的DAX公式? ? ? ? ? ? ?
? ? ? ? ? ? RETURN < 包含variablename 1…variablename 2> --另一個可以使用variablename 1和variablename 2作為表達式的一部分的有效DAX公式。

? ? (2)可以定義為變量(VAR)的有:標量值、值列表、定義列表的函數(shù)式等,例如:
? ? ? ? VAR myConstantValue = 5? ? ? ? ? ? ? ? ? ? ? ? -- 標量值
? ? ? ? VAR myFunction = MAX(Customer[Sales]) -- 值列表
? ? ? ? VAR myTable = FILTER(Sales,Customer[Sales] >50)? - -列表

? ? ? (3)以下是準官方關于VAR的解釋:

(三) VAR變量在DAX里的運用

? ? 使用VAR

? ? ? 雖然VAR變量是 DAX 2015 中的一個新功能,而且2015版本的 DAX 語言也有許多新的功能,但沒有一個像變量VAR那樣能改變DAX規(guī)則。
? ? ? 變量由關鍵字VAR來引入要定義的變量。你可以在單個表達式中放置所需的任意多個變量,并且每一個都可以有它自己的 VAR 定義。然后,使用RETURN關鍵字定義要作為結果返回的表達式。在RETURN表達式中,可以使用由變量替換的計算值。然而,還有很多關于變量的知識。

? ? ? 首先,變量的主要功能是使 DAX 代碼更容易編寫; 以及變量能大大提高代碼的可讀性和可重復使用性,語法也非常簡單。如下面的示例所示:

? ? ? ? [Growth%]:= VAR CurrentSales:? =? SUM(Sales[Quantity])
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? VAR SalesLastYear =? CALCULATE ( SUM ( Sales[Quantity] ),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
? ? ? ? RETURN
? ? ? ? IF ( AND ( CurrentSales <> 0, SalesLastYear <> 0 ),
? ? ? ? ? DIVIDE ( CurrentSales - SalesLastYear, SalesLastYear))

? ? ? 從上一個示例中,你已經(jīng)可以理解到代碼可讀性方面的優(yōu)勢。通過將表達式分配一個名稱,以便代碼可以更簡單地讀取和維護。
? ? ? 其次,可以各種方式來定義變量,并將其用于度量值、計算列或查詢中。
? ? ? 例如:以下方式可以定義為變量:

? ? (1)標量值-變量

? ? ? ? VAR myScalarValue = SUM(Sales[Extended Amount])

? ? (2)列表--變量

? ? ? ? VAR myTable = FILTER(Customer,Customer[Post Code] = 50210)
? ? ? ? 上面的變量myTable返回了一個虛擬表,它可以在DAX的任何位置使用,例如在DAX公式中使用的表,以及CALCULATE函數(shù)中的篩選器等。

? ? (3)其他要點

? ? ? ? ? 每一個變量在公式的RETURN 部分開始之前被評估一次;
? ? ? ? ? 變量里可以包含其他變量;
? ? ? ? ? 變量在初始計算篩選(列表篩選和行篩選)中執(zhí)行;
? ? ? ? ? 變量計算的結果將被緩存(作為存儲數(shù)據(jù)),以便在公式中重復調(diào)用;
? ? ? ? ? 變量被賦值后,在被公式引用的執(zhí)行過程中,該值不能改變。這時,它更像一個常量。

? (4)優(yōu)勢(好處)

? ? ? 被賦予名稱的變量其實可以理解為存儲數(shù)據(jù)里添加了“新增列表”(物化的列表),既然這樣,何不直接使用度量值、計算字段名稱?因為這些也都是可以再次被DAX公式引用,且與定義變量是一樣的方式。那么,為何還要定義VAR變量的名稱列表?
? ? ? ? 答案是:除了度量值、計算列或查詢,使用VAR語法來構建DAX,還有其他很多好處,特別是當你需要:定義“當前行”時。我們總結以下:
? ? ? 1)公式可以更容易編寫。一開始可能需要花更多的時間來寫這個公式,但是最終可能會節(jié)省時間,通過更快的找到公式的工作版本,減少重復工作并節(jié)省時間;
? ? ? 2)公式可以更容易讀懂。讀者可以一步一步地理解公式的邏輯,從而更容易確定公式的設計目的。當使用注釋、新行和空格時更是如此;
? ? ? 3)公式需要更改時,可以更容易維護;
? ? ? 4)公式中的變量可能更利于提升性能。變量可以將重復子字符串一次性寫入,然后可多次使用。這對公式編寫和性能都有好處(盡管在此語法之前,DAX已經(jīng)有一些好的緩存特性);
? ? ? 5)公式中的變量可以替代EARLIER函數(shù)(當在復雜公式中嵌套行篩選時)。因為定義的變量始終在初始篩選器中執(zhí)行,這種語法可以更容易的理解DAX當前行的概念。

? ? ? 實際運用中,比如:

? ? ? ? 1)使用VAR變量語法,替換DAX的某些篩選條件,來簡化DAX代碼;
? ? ? ? 2)使用VAR變量命名,定義一張?zhí)摂M的列表或表(指定某個物化的一個或多個列表);
? ? ? ? 3)使用VAR變量命名,定義需要的值列表,并分別放置于不同的計算篩選環(huán)境里來構建DAX的一個個邏輯條件(比如類似于EARLIER()函數(shù)的“當前行”),然后將這些條件按條件步驟組裝成一個復雜的計算邏輯條件。

? ? VAR定義方式

? ? ? 我們將前面提到的實際運用中的三點分別舉例:
? ? ? 1)使用VAR變量語法,替換DAX的某些篩選條件,來簡化DAX代碼;
? ? ? 這與引用中間度量值作用是一樣的。使用變量的目的是:由于其中的中間度量值很長,或者使用很頻繁。比如,假如在一個DAX計算式中,SUM('Sales'[銷售額]) 這個表達式多處需要引用,這時候:可以使用度量值名稱定義它:比如“當前銷售額”:
? ? ?
使用VAR變量來命名它,之后在其后的公式中引用該變量名稱。比如使用該VAR參與計算同比、環(huán)比、累計比等。
? ? ? 依據(jù) VAR語法,首先用 VAR定義該DAX表達式,然后使用RETURN 取出來,在后續(xù)公式中引用這段VAR。
? ? ? =VAR? Sales_Fist = SUM('Sales'[銷售額])? // 定義一個函數(shù)計算式變量
? ? ? ? RETURN? // 取出變量? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? 年初至今銷售=TOTALYTD(Sales_Fist,Dates[Date]) // 作為度量計算的參數(shù)。
? ? ? ? 一般情況下,很少將一個度量值名直接指定為VAR變量,因為度量值字段也可以直接被公式重復引用。注意:最好不要直接將度量定義為變量。后面會進一步說明。
? ? ? ? 如果已經(jīng)先定義了一個:當前銷售 = SUM('Sales'[銷售額]) 的[當前銷售]度量,可以將篩選條件定義為VAR變量:
? ? ? =VAR YTD_T= TOTALYTD ('Dates'[Date])? //定義一個篩選列表
? ? ? RETURN? // 取出變量? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? 年初至今銷售=CALCULATE([當前銷售], YTD_T) //作為度量的列表篩選條件。

? ? ? 2)使用VAR變量命名,定義一張?zhí)摂M的列表或表(指定某個物化的列表);即變量可以定義為表(一個或多個列表的表)。
? ? ? 我們知道,列表間的并、交、補集合方式,在DAX里計算比較頻繁,它需要CALCULATETABLE、FILTER等表函數(shù)或者UNION、CROSSJOIN等集合表函數(shù)定義的列表,并將其結果表格作為復雜計算的參數(shù)。
? ? ? ? 同理,依據(jù) VAR語法,首先用 VAR定義該DAX表達式(表),然后使用RETURN 取出來,并在后續(xù)公式中引用這段VAR。
? ? ? 比如,有一個包含客戶表、銷售表、時期表的數(shù)據(jù)模型,要計算最近時期有銷售的老客戶數(shù)(條件:當前最近時期前都有銷售),它涉及三個關系表中的客戶列、銷售列以及時期列三列。
? ? ? = VAR Cur_Table =CALCULATETABLE( ‘客戶表’ ,’銷售表‘)? // 所有時期所有客戶表
? ? ? = VAR Per_Table =CALCULATETABLE( ‘客戶表’ ,
? ? ? ? ? ? ? ? CALCULATETABLE( ’銷售表‘ ,
? ? ? ? FILTER( ‘時期表’, ‘時期表’[時期]<=MIN( ‘時期表’[時期]))) // 最初時期前有銷售的
? ? ? ? RETURN
? ? ? ? CALCULATE( COUNTROWS(EXCEPT( Cur_Table, Per_Table))) // 差集部分
? ? ? 我們用VAR對客戶表中的客戶表、老客戶子集表分別使用VAR變量進行定義。然后利用兩個表的補集(EXCEPT)計算。
? ? ? 注:可以參考之前的關于新、老客戶的VAR變量結合交、并、補集函數(shù) 的公式。

? ? ? 再例如,你還可以定義一個類似如下的表達式:
? ? ? [RedSalesLastYear] :=
? ? ? VAR
RedProducts = FILTER (ALL ( Product[Color] ), Product[Color] = "Red" )
? ? ? VAR LastYear =? SAMEPERIODLASTYEAR ( 'Date'[Date] )
? ? ? RETURN
? ? ? CALCULATE ( SUM ( Sales[Quantity] ),RedProducts,LastYear )
? ? ? 在此示例中,RedProducts 以及LastYear是表,而不是標量值。

? ? ? 3)使用VAR變量命名,定義需要的值列表,并分別放置于不同的計算篩選環(huán)境里來構建DAX的邏輯條件(比如類似于EARLIER()函數(shù)的“當前行”)。即也可以使用標量值來定義變量,其語法或用法與定義為表沒有區(qū)別:
? ? ? 例如之前的:VAR myScalarValue = SUM(Sales[Extended Amount])

? ? ? 4)其他注意以及方式運用
? ? ? 另一個可能不明顯的一點是:變量可以是表達式的一部分;而且無論在表達式的任何位置,都可以定義變量;
? ? ? 補充:你可以使用以下語法在 DAX 表達式的任意位置定義一個變量:
? ? ? VAR NAME = RETURNED VALUE
? ? ? 還有,變量可以是任何數(shù)據(jù)類型(數(shù)值、布爾值、時期、文本),也包括整個表。
? ? ? 請記住,每次在 DAX 表達式中引用變量時,Power BI 必須根據(jù)定義重新計算它的值。 因此,在函數(shù)中盡量避免使用重復的變量。
? ? ? 此外,DAX在變量定義的篩選中定義變量,而不是在使用變量的篩選中定義變量。例如,假設你要檢索Sales--銷售表中的所有products--產(chǎn)品的列表,它們分別占總銷售額的1% 以上的產(chǎn)品列表。在標準 DAX,變量之前,你必須寫:

? ? ? ? EVALUATE
? ? ? ? ADDCOLUMNS ( FILTER ( VALUES ( Product[Product Name] ),
? ? ? ? [SalesAmount]>= CALCULATE ( [SalesAmount],ALL ( Product ) ) * 0.01 ),
? ? ? ? "SalesOfProduct",[SalesAmount])

? ? ? 通過利用內(nèi)部CALCULATE中ALL ( Product ),可以將當前產(chǎn)品的銷售額與所有產(chǎn)品的銷售額的1% 進行比較。結果是四種不同顏色的產(chǎn)品以及度量結果:

? ? ? 這個公式以及所有遵循類似模式的公式的問題是,一旦在產(chǎn)品外部放置任何篩選器,例如通過CALCULATETABLE定義的外部表,它就會停止工作。例如,以下查詢將返回銷售超過1% 的所有產(chǎn)品中的黑色產(chǎn)品,而不是銷售超過1% 的產(chǎn)品。

? ? ? EVALUATE
? ? ? ? CALCULATETABLE ( ADDCOLUMNS (
? ? ? ? FILTER (VALUES ( Product[Product Name] ),
? ? ? ? [SalesAmount] >= CALCULATE ( [SalesAmount],ALL ( Product ) ) * 0.01? ),
? ? ? ? "SalesOfProduct",[SalesAmount]? ), Product[Color] = "Black")

? ? ? ? 結果是單行,前一個圖中的一行產(chǎn)品:

? ? ? ? 通過使用變量,無論外部篩選器如何,查詢都將變得更容易編寫和工作:

? ? ? ? EVALUATE
? ? ? ? CALCULATETABLE ( ADDCOLUMNS (
? ? ? ? VAR OnePercentOfSales = [SalesAmount] * 0.01
? ? ? ? RETURN
? ? ? ? FILTER (VALUES ( Product[Product Name] ),
? ? ? ? [SalesAmount] >= OnePercentOfSales ), "SalesOfProduct",[SalesAmount]),
? ? ? ? Product[Color] = "Black")

? ? ? 現(xiàn)在的結果是預期的一個值:即第5 行的黑色產(chǎn)品。

? ? ? ? 這是值得花上幾分鐘閱讀的公式,因為它顯示了變量的獨特功能。變量 OnePercentOfSales 在 ADDCOLUMNS 中被定義。因此,它在ADDCOLUMNS的計算篩選 ( 即黑色產(chǎn)品的篩選器)中評估的。因此,1%的銷售額也就是黑色產(chǎn)品的價值。
? ? ? 一旦變量被評估,開始迭代產(chǎn)品表,在迭代循環(huán)中,計算 [SalesAmount],并返回當前產(chǎn)品的銷售額,而 OnePercentOfSales 則返回1% 銷售的黑色產(chǎn)品。因為避免了在迭代中使用ALL和修改當前篩選器,所以,不管使用 CALCULATETABLE 設置了什么外部篩選器,后一種公式都有效。換句話說,變量能訪問外部篩選器,這可能是一個經(jīng)驗豐富的 DAX 程序員最需要的功能。

? ? ? 補充說明:因為變量實際是一個存儲數(shù)據(jù),既可以關聯(lián)外部壓縮數(shù)據(jù),也可以被公式引用。即壓縮數(shù)據(jù)-存儲數(shù)據(jù)-公式計算數(shù)據(jù)。
? ? ?
當然,你應該不限于在度量值或查詢中使用變量,它們也能很好的用于計算列。事實上,一旦你開始使用變量,就可以完全擺脫EARLIER函數(shù)的約束。例如,要計算比當前價格高的產(chǎn)品數(shù)量,以前必須編寫如下所示的計算列:

? ? ? Product[ListPriceRankDense] =COUNTROWS (
? ? ? FILTER (VALUES ( Product[Unit Price] ),
? ? ? Product[Unit Price] > EARLIER ( Product[Unit Price] ) )) + 1

? ? ? ? EARLIER可能是 DAX 中最令人費解的函數(shù)功能,許多人覺得很難使用它,也許是因為其名字含義更像是OUTER --“外部的”,而不是EARLIER --“更早的”。通過使用變量,相同的表達式變得更加清晰和易于編寫:

? ? ? Product[ListPriceRankDense] = VAR? CurrentPrice = Product[Unit Price]
? ? ? ? RETURN
? ? ? ? COUNTROWS (
? ? ? ? FILTER (VALUES ( Product[Unit Price] ), Product[Unit Price] > CurrentPrice? ) ) + 1

? ? ? 即使在這個表達式中,你也可以理解為:變量是在篩選器外部單獨計算的。因此,它評估當前產(chǎn)品的價格后,再在篩選器里面使用它時,它已經(jīng)具有一個定義的當前值。所以,在此表達式中不再需要EARLIER版本。

? ? ? 最后,變量使得復雜子表達式被單個計算替代,只要存在需要多次評估的相同子表達式時,使用變量作為DAX 優(yōu)化器,來保證計算只發(fā)生一次,從而產(chǎn)生更快的代碼。

再次強調(diào):因為變量實際是一個存儲數(shù)據(jù),所以只需要存儲一次結果值,就可以被公式引擎重復利用。

基于此,同時也需要注意:最好不要將度量或基表直接定義為變量,因為,可能造成變量定義的存儲表與公式引擎引用的原表存儲表重復,而出現(xiàn)錯誤。這不絕對,關鍵是檢查物化的存儲數(shù)據(jù)否沖突。

? ? ? ? 例如考慮之前定義的:VAR myScalarValue = SUM(Sales[Extended Amount])

? ? ? 不能放置在CALCULATE的第一個參數(shù)里(計算列表):CALCULATE(myScalarValue),因為如果將變量作為CALCULATE的第一參數(shù)--計算式。那么,公式引擎只能引用該變量數(shù)據(jù)計算(存儲數(shù)據(jù)),計算的總是一個固定的、由變量定義的值,而無論后面是怎樣的篩選器(因為第一參數(shù)的任何篩選器,其篩選指向的列表都有可能是變量定義的計算列,也因為計算列表始終需要最后回到原列表計算)。

(四) VAR案例? 二部分繼續(xù)。

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

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

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