DAX是專門為在數(shù)據(jù)模型上計(jì)算業(yè)務(wù)公式而設(shè)計(jì)的闯割。 大家可能已經(jīng)知道什么是數(shù)據(jù)模型彻消。 如果沒有,我們將從數(shù)據(jù)模型和關(guān)系的描述開始宙拉,為建立DAX知識奠定基礎(chǔ)宾尚。
1、什么是數(shù)據(jù)模型
數(shù)據(jù)模型是一組由關(guān)系鏈接的表
我們都知道表是什么:一組包含數(shù)據(jù)的行谢澈,每行分為幾列煌贴。 每列都有一種數(shù)據(jù)類型,并且包含一條信息澳化。 我們通常將表中的一行稱為記錄崔步。 表是組織數(shù)據(jù)的便捷方法。 表雖然本身是最簡單的形式缎谷,但它本身就是數(shù)據(jù)模型井濒。 因此灶似,當(dāng)我們在Excel工作簿中編寫名稱和數(shù)字時(shí),我們正在創(chuàng)建一個(gè)數(shù)據(jù)模型瑞你。
如果數(shù)據(jù)模型包含許多表酪惭,則它們很可能通過關(guān)系鏈接在一起。 關(guān)系是兩個(gè)表之間的鏈接者甲。 當(dāng)兩個(gè)表被關(guān)聯(lián)在一起時(shí)春感,我們說它們是相關(guān)的。 在圖形上虏缸,關(guān)系由連接兩個(gè)表的線表示鲫懒。 圖1-1顯示了數(shù)據(jù)模型的示例。
以下是關(guān)系的一些重要方面:
關(guān)系中的兩個(gè)表沒有相同的角色刽辙。 它們被稱為關(guān)系的單邊和多邊窥岩,分別用1和*表示。 在圖1-1中宰缤,重點(diǎn)關(guān)注產(chǎn)品和產(chǎn)品子類別之間的關(guān)系颂翼。 一個(gè)子類別包含許多產(chǎn)品,而一個(gè)產(chǎn)品只有一個(gè)子類別慨灭。 因此朦乏,產(chǎn)品子類別和產(chǎn)品的關(guān)系是1對多的關(guān)系
特殊類型的關(guān)系是1:1和弱關(guān)系。 在1:1關(guān)系中氧骤,兩個(gè)表都是單邊的呻疹,而在弱關(guān)系中,兩個(gè)表都可以是多邊的筹陵。 這些特殊類型的關(guān)系并不常見诲宇。后續(xù)會討論這些關(guān)系
用于創(chuàng)建關(guān)系的列在兩個(gè)表中通常具有相同的名稱,稱為關(guān)系的鍵惶翻。在關(guān)系的單邊,該列的每一行都必須具有唯一的值鹅心,并且不能包含空格吕粗。在很多情況下,相同的值可以在許多不同的行中重復(fù)旭愧。當(dāng)列的每一行都有唯一值時(shí)颅筋,該列稱為表的鍵。
關(guān)系可以形成一條鏈输枯。每個(gè)產(chǎn)品都有一個(gè)子類別议泵,每個(gè)子類別都有一個(gè)類別。因此桃熄,每個(gè)產(chǎn)品都有一個(gè)類別先口。要檢索產(chǎn)品的類別,必須遍歷兩個(gè)關(guān)系的鏈。圖1-1包含一個(gè)由三個(gè)關(guān)系組成的鏈?zhǔn)纠锞瑥匿N售開始厢汹,一直到產(chǎn)品類別。
在每種關(guān)系中谐宙,一個(gè)或兩個(gè)小箭頭可以確定交叉過濾的方向烫葬。圖1-1在“銷售”和“產(chǎn)品”之間的關(guān)系中顯示了兩個(gè)箭頭,而所有其他關(guān)系只有一個(gè)箭頭凡蜻。箭頭指示自動(dòng)篩選關(guān)系的方向(交叉篩選)搭综,通常是不鼓勵(lì)使用這種雙向過濾器的
2、理解關(guān)系的方向
每個(gè)關(guān)系可以具有一個(gè)單向或雙向交叉過濾器划栓。 過濾總是從關(guān)系的單邊到多邊進(jìn)行兑巾。 如果交叉過濾器是雙向的,也就是說茅姜,如果交叉過濾器上有兩個(gè)箭頭闪朱,則過濾也會從多邊到一側(cè)發(fā)生。
- 比如我們通過圖1-2中的以下條件去篩選,會得到圖1-3的結(jié)果
這其中的原理是因?yàn)椋篊alendar Year是屬于Date表的列钻洒。 由于Date是Sales的單邊關(guān)系奋姿,因此引擎會根據(jù)年份過濾Sales。 這就是為什么顯示的數(shù)量按年份進(jìn)行過濾的原因素标。而 由于Sales和Product表之間的關(guān)系是雙向的称诗,因此發(fā)生篩選。 當(dāng)我們在報(bào)表中放置產(chǎn)品名稱進(jìn)行計(jì)數(shù)時(shí)头遭,由于每年的過濾器通過Sales表傳遞到Product表寓免,因此我們獲得了每年銷售的產(chǎn)品數(shù)量。 如果銷售和產(chǎn)品之間的關(guān)系是單向的计维,則結(jié)果將有所不同袜香。
- 如果我們通過在行上放置“顏色”并在值區(qū)域中添加“日期計(jì)數(shù)”來修改報(bào)告,結(jié)果將有所不同鲫惶,如圖1-4所示蜈首。
行中的過濾器是Product表中的Color列。 由于Product處于Sales關(guān)系的一側(cè)欠母,因此正確地過濾了數(shù)量欢策。 產(chǎn)品名稱計(jì)數(shù)被過濾,因?yàn)樗歉鶕?jù)行中的表(即Sales)計(jì)算值赏淌。 而“日期計(jì)數(shù)”對于所有行踩寇,它始終顯示相同的值,即六水,Date表中的總行數(shù)俺孙。來自Color列的過濾器不會傳播到Date表辣卒,因?yàn)镈ate和Sales之間的關(guān)系是單向的。 因此鼠冕,盡管Sales上面有活動(dòng)的篩選器添寺,但篩選器無法傳播到Date,因?yàn)殛P(guān)系方向阻止了它懈费。
- 如果我們更改Date和Sales之間的關(guān)系以啟用雙向交叉過濾计露,則結(jié)果如圖1-5所示。
用圖1-5和圖1-4對比憎乙,可以發(fā)現(xiàn)Date的計(jì)數(shù)隨著Color的不同而不同
乍一看票罐,似乎所有的關(guān)系都應(yīng)定義為雙向的,以便使過濾器沿任何方向傳播并始終返回有意義的結(jié)果泞边。 但是現(xiàn)在就可以告訴你用這種方法設(shè)計(jì)數(shù)據(jù)模型幾乎是不合適的该押,原因后面會討論
3、DAX for Excel用戶
您可能已經(jīng)知道DAX有點(diǎn)像的Excel公式語言阵谚。 畢竟蚕礼,DAX的根源是Power Pivot for Excel,開發(fā)團(tuán)隊(duì)試圖使這兩種語言保持相似梢什。 這種相似性使向這種新語言的過渡更加容易奠蹬。 但是,有一些重要的區(qū)別嗡午。
單元格VS表格
Excel對單元格執(zhí)行計(jì)算囤躁。 使用其坐標(biāo)引用一個(gè)單元格。 因此荔睹,我們可以編寫如下公式:
= (A1 * 1.25) - B2
在DAX中狸演,不存在單元格的概念。 DAX適用于表和列僻他,而不適用于單元格宵距。 因此,DAX表達(dá)式引用表和列吨拗,這意味著編寫代碼的方式有所不同消玄。 表和列的概念在Excel中不是新的。 實(shí)際上丢胚,如果通過使用“格式為表格”功能將Excel范圍定義為表格,則可以在Excel中編寫引用表格和列的公式受扳。 在圖1-6中携龟,SalesAmount列評估一個(gè)表達(dá)式,該表達(dá)式引用同一表中的列而不是工作簿中的單元格勘高。
而在DAX中的寫法如下:
Sales[SalesAmount] = Sales[ProductPrice] * Sales[ProductQuantity]
如上所見峡蟋,每列均以其表名為前綴坟桅。 在Excel中,我們不提供表名蕊蝗,因?yàn)镋xcel公式在單個(gè)表中起作用仅乓。 但是,DAX在包含許多表的數(shù)據(jù)模型上工作蓬戚。 我們必須指定表名夸楣,因?yàn)椴煌碇械膬闪锌赡芫哂邢嗤拿Q。
DAX中的許多功能與Excel等效的功能以相同的方式工作子漩。 例如豫喧,IF函數(shù)在DAX和Excel中的讀取方式相同:
Excel IF ( [@SalesAmount] > 10, 1, 0)
DAX IF ( Sales[SalesAmount] > 10, 1, 0)
Excel和DAX的語法不同的一個(gè)重要方面是引用整個(gè)列的方式。對于使用過R和Python的人來說再熟悉不過了幢泼。 實(shí)際上紧显,在[@ProductQuantity]中,@表示“當(dāng)前行中的值”缕棵。 在DAX中孵班,無需指定值必須來自當(dāng)前行,因?yàn)檫@是該語言的默認(rèn)行為招驴。 在Excel中篙程,我們可以通過刪除@符號來引用整個(gè)列(即該列中的所有行)。 如圖1-7所示
AllSales列的值在所有行中都相同忽匈,因?yàn)樗荢alesAmount列的總計(jì)房午。 換句話說,在當(dāng)前行中的列的值與整個(gè)列的值之間在語法上存在差異丹允。DAX是不同的郭厌。 在DAX中,這是編寫圖1-7的AllSales表達(dá)式的方式:
AllSales := SUM ( Sales[SalesAmount] )
檢索特定行的列的值與整體使用列之間在語法上沒有區(qū)別雕蔽。 DAX理解我們要對列的所有值求和折柠,因?yàn)槲覀冊诰酆虾瘮?shù)(在本例中為SUM函數(shù))中使用列名,這需要將列名作為參數(shù)傳遞批狐。 因此扇售,盡管Excel需要明確的語法來區(qū)分要檢索的兩種類型的數(shù)據(jù),但DAX會自動(dòng)進(jìn)行歧義消除嚣艇。
4承冰、Excel and DAX:兩種函數(shù)式語言
兩種語言相似的一個(gè)方面是Excel和DAX都是函數(shù)式語言。 功能語言基本上是由函數(shù)調(diào)用的表達(dá)式組成食零。 在Excel和DAX中困乒,盡管語句,循環(huán)和跳轉(zhuǎn)的概念在許多編程語言中都是通用的贰谣,但它們并不存在娜搂。 在DAX中迁霎,一切都是表達(dá)式。
DAX中的迭代器
在Excel中工作時(shí)百宇,一次只能執(zhí)行一次計(jì)算考廉。 前面的示例表明,要計(jì)算銷售總額携御,我們創(chuàng)建一個(gè)包含價(jià)格乘以數(shù)量的列昌粤。 然后,作為第二步因痛,我們對其求和以計(jì)算總銷售額婚苹。 例如,該數(shù)字可用作計(jì)算每個(gè)產(chǎn)品的銷售百分比的分母鸵膏。
使用DAX膊升,您可以使用迭代器在單個(gè)步驟中執(zhí)行相同的操作。 迭代器完全按照其名稱的含義進(jìn)行操作:它遍歷一個(gè)表并在表的每一行上執(zhí)行計(jì)算谭企,將結(jié)果匯總以產(chǎn)生所請求的單個(gè)值廓译。使用前面的示例,我們現(xiàn)在可以使用SUMX迭代器計(jì)算所有銷售額的總和:
AllSales := SUMX (
Sales, Sales[ProductQuantity] * Sales[ProductPrice]
)
這種方法既有優(yōu)點(diǎn)又有缺點(diǎn)债查。
優(yōu)點(diǎn)是我們可以在一個(gè)步驟中執(zhí)行許多復(fù)雜的計(jì)算非区,而不必?fù)?dān)心添加只會對特定公式有用的列。缺點(diǎn)是盹廷,與使用Excel進(jìn)行編程相比征绸,使用DAX進(jìn)行編程的視覺效果更差。 確實(shí)俄占,我們沒有看到計(jì)算價(jià)格乘以數(shù)量的列管怠; 它僅是整個(gè)計(jì)算的一部分。
其實(shí)我們可以創(chuàng)建一個(gè)計(jì)算列來計(jì)算價(jià)格乘以數(shù)量的乘積缸榄。但是渤弛,除非使用DirectQuery和Aggregations,否則它會占用內(nèi)存甚带,并且會減慢計(jì)算速度
5她肯、學(xué)習(xí)DAX的理論
事實(shí)上,DAX要求首先學(xué)習(xí)理論而并不是編程語言之間的區(qū)別鹰贵。在編寫DAX代碼之前晴氨,我們需要學(xué)習(xí)DAX理論并徹底理解evaluation contexts是如何工作的,不然我們會一臉懵逼碉输。所以在我們對evaluation contexts沒有了解之前籽前,最好不要深入學(xué)習(xí)下去。不過對于這些概念后面都要介紹
6、DAX for SQL使用者
如果習(xí)慣使用SQL語言聚假,那么你已經(jīng)使用過許多表并創(chuàng)建了列之間的連接來設(shè)置關(guān)系。
實(shí)際上闰非,DAX中的計(jì)算就是查詢一組由關(guān)系連接的表和值膘格。
關(guān)系的處理
SQL和DAX之間的第一個(gè)區(qū)別是模型中關(guān)系的工作方式。
在SQL中财松,我們可以在表之間設(shè)置外鍵來聲明關(guān)系瘪贱,但是引擎永遠(yuǎn)不會在查詢中使用這些外鍵,除非我們顯式地使用它們辆毡。例如菜秦,如果我們有一個(gè)Customers表和一個(gè)Sales表,其中CustomerKey是Customers中的主鍵和Sales中的外鍵舶掖,我們可以編寫以下查詢:
SELECT Customers.CustomerName, SUM ( Sales.SalesAmount ) AS SumOfSales
FROM Sales
INNER JOIN Customers
ON Sales.CustomerKey = Customers.CustomerKey
GROUP BY Customers.CustomerName
盡管我們使用外鍵在表之間聲明關(guān)系球昨,但仍然需要顯式地在查詢中聲明連接條件。雖然這種方法使查詢更加冗長眨攘,但它很有用主慰,因?yàn)榭梢栽诓煌牟樵冎惺褂貌煌倪B接條件,從而在表達(dá)查詢的方式上有很大的自由鲫售。
在DAX中共螺,關(guān)系是模型的一部分,它們都屬于外連接情竹。當(dāng)它們在模型中定義時(shí)藐不,我們不再需要在查詢中指定連接類型,只要使用與主表相關(guān)的列秦效,DAX就會在查詢中使用自動(dòng)左外連接雏蛮。因此,在DAX中棉安,您將按照如下方式編寫之前的SQL查詢:
EVALUATE
SUMMARIZECOLUMNS (
Customers[CustomerName], "SumOfSales", SUM ( Sales[SalesAmount] )
)
因?yàn)镈AX知道銷售和客戶之間的現(xiàn)有關(guān)系底扳,所以它會按照模型自動(dòng)進(jìn)行加入。
DAX是一種函數(shù)式語言
SQL是一種聲明性語言贡耽。我們可以通過使用SELECT語句聲明要檢索的數(shù)據(jù)集來定義所需的內(nèi)容衷模,而不必?fù)?dān)心引擎如何實(shí)際檢索信息。
而DAX是一種函數(shù)式語言蒲赂。在DAX中阱冶,每個(gè)表達(dá)式都是一個(gè)函數(shù)調(diào)用,而且函數(shù)參數(shù)也可以是其他函數(shù)執(zhí)行出來的結(jié)果滥嘴。參數(shù)的計(jì)算可能導(dǎo)致DAX執(zhí)行復(fù)雜的查詢計(jì)劃來計(jì)算結(jié)果木蹬。
例如,如果我們只想檢索住在歐洲的客戶若皱,我們可以編寫這個(gè)查詢
在SQL:
SELECT
Customers.CustomerName,
SUM ( Sales.SalesAmount ) AS SumOfSales
FROM
Sales
INNER JOIN Customers
ON Sales.CustomerKey = Customers.CustomerKey
WHERE
Customers.Continent = 'Europe'
GROUP BY
Customers.CustomerName
使用DAX镊叁,我們不會在查詢中聲明WHERE條件尘颓。相反,我們使用一個(gè)特定的函數(shù)(FILTER)來獲取結(jié)果:
EVALUATE
SUMMARIZECOLUMNS (
Customers[CustomerName], FILTER(
Customers, Customers[Continent] = "Europe" ),
"SumOfSales", SUM ( Sales[SalesAmount] )
)
DAX是一種編程和查詢語言
在SQL中晦譬,查詢語言和編程語言之間存在明顯的區(qū)別——即用于在數(shù)據(jù)庫中創(chuàng)建存儲過程疤苹、視圖和其他代碼的指令集。
每種SQL語言都有自己的語句敛腌,以便程序員使用代碼豐富數(shù)據(jù)模型卧土。然而,DAX實(shí)際上沒有區(qū)分查詢和編程像樊。一組豐富的函數(shù)可以操作表尤莺,并反過來返回表。
DAX和SQL中的子查詢和條件
作為查詢語言的SQL最強(qiáng)大的特性之一是可以選擇使用子查詢生棍。
DAX具有類似的概念颤霎。然而,對于DAX子查詢足绅,它們源于語言的函數(shù)特性捷绑。
例如,要檢索購買價(jià)值超過100美元的客戶的客戶和總銷售額氢妈,我們可以用SQL編寫這個(gè)查詢:
SELECT CustomerName, SumOfSales
FROM ( SELECT Customers.CustomerName,
SUM ( Sales.SalesAmount ) AS SumOfSales FROM
Sales INNER JOIN Customers
ON Sales.CustomerKey = Customers.CustomerKey GROUP BY
Customers.CustomerName ) AS SubQuery
WHERE
SubQuery.SumOfSales > 100
我們可以在DAX中通過嵌套函數(shù)調(diào)用得到相同的結(jié)果:
EVALUATE
FILTER (
SUMMARIZECOLUMNS (
Customers[CustomerName],
"SumOfSales", SUM ( Sales[SalesAmount] )
),
[SumOfSales] > 100
)
此系列是對The_Definitive_Guide_to_DAX第2版的學(xué)習(xí)筆記
微信公眾號:紙上躬行君