【DAX學(xué)習(xí)】1、DAX的一些事兒

image.png

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ù)模型的示例。

(圖1-1)

以下是關(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)纠?,從銷售開始,一直到產(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é)果
圖1-2

圖1-3

這其中的原理是因?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所示。
圖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所示。
image
圖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á)式引用同一表中的列而不是工作簿中的單元格。

圖1-6

而在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所示

圖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í)筆記

微信公眾號:紙上躬行君

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