開篇介紹
SSAS 分析服務中記錄了大量的聚合值,這些聚合值在 Cube 中實際上指的就是度量值。一個給定的度量值可能聚合了來自事實表中上千上萬甚至百萬條數據,因此在設計階段我們所能看到的度量實際上就已經應用了某些聚合函數來決定這個值怎樣被聚合。
當然有可能已有的度量值遠遠還不夠,還需要在查詢的時候繼續從不同的角度去聚合一些數據以滿足實際需求,因此就會使用到各種不同的 MDX 聚合函數。
Sum 聚合
Sum 聚合的應用非常普通和常見,在 MDX 中其語法為: Sum ({SET} [, Expression])
也就是說 Sum 聚合函數操作的對象是一個集合,並且對於集合 SET 中的每一個 Tuple 元組來說可以使用到后面的表達式。 使用起來非常簡單,其作用就是把相應的度量值全部加起來,而不需要考慮這些度量值是如何來的。如果這些度量值是基於設計階段的 sum 或者 count 聚合的話,那么在 MDX 中的 Sum 聚合函數使用沒有什么問題。但是如果這些度量值在設計階段中使用到了 distinct count, max, min 等聚合方式的話,那么在 MDX 中 Sum 聚合有可能會產生一些不太准確的結果。
因此在這種情況下,特別要注意的就是要了解要被聚合的這個度量值在 Cube 設計階段它本身是如何被聚合的,這個可以通過 MDX 中的 Aggregate 函數來搞定: Aggregate( {Set} [, Expression])
和 MDX Sum 函數一樣,MDX Aggregate 函數可以聚合來自特定集合的值。與簡單的 Sum 累加值不同的是,MDX 中的 Aggregate 函數還會考慮到它所累加的度量值對象本身是如何被聚合的。如果這個度量值本身是通過 Sum 或者 Count 聚合的話,那么 Aggregate 函數就返回一個 Sum 累加之后的結果。如果度量值本身是取的 max 聚合值,那么 Aggregate 函數就返回一個 max 結果。如果度量值本身取的是 Discint count,那么 Aggregate 函數也返回一個 Distinct count 值。實際上這個過程在分析服務中還是比較復雜的,所以在使用 MDX Aggregate 函數的時候就要注意認真檢查一下那些計算值。
定義一個計算成員來聚合前5的 Sub Category 的值
SELECT { ([Measures].[Reseller Sales Amount]), ([Measures].[Reseller Transaction Count]), ([Measures].[Reseller Order Count]) } ON COLUMNS, TopCount( {[Product].[Subcategory].[Subcategory].Members}, 5, ([Measures].[Reseller Sales Amount]) ) + {([Product].[Subcategory].[All Products])} ON ROWS FROM [Step-by-Step]
這個查詢結果顯示了前 5 的產品子目錄以及 All 成員的 Reseller Sales Amount, Reseller Transaction Count 以及 Reseller Order Count。
這三個度量值實際上來源於數據倉庫中的同一個事實表,但是它們從數據倉庫到Cube這一個過程中的計算方式是不同的。
Reseller Sales Amount - 這個度量值是基於表中 Sales Amount 列上的總和。在 Cube 的設計過程中,它是通過 sum 聚合方式來實現的,聚合了 Sales Amount 列上的所有值。
Reseller Transaction Count - 度量值是以訂單為基礎 Count 了每個訂單下的 Line Item 條數。
Reseller Order Count - 度量值記錄了訂單了條數,並且是通過 Distinct Count 方式來完成聚合的,因為一個訂單可能有一條或者多條 Line Item 的記錄。
修改這個查詢,用 Aggregate函數來添加一個計算前5的成員:
WITH MEMBER [Product].[Subcategory].[Top 5] AS Aggregate( TopCount( [Product].[Subcategory].[Subcategory].Members, 5, ([Measures].[Reseller Sales Amount]) ), ([Measures].CurrentMember) ) SELECT { ([Measures].[Reseller Sales Amount]), ([Measures].[Reseller Transaction Count]), ([Measures].[Reseller Order Count]) } ON COLUMNS, TopCount( [Product].[Subcategory].[Subcategory].Members, 5, ([Measures].[Reseller Sales Amount]) ) + { ([Product].[Subcategory].[Top 5]), ([Product].[Subcategory].[All Products]) } ON ROWS FROM [Step-by-Step]
計算成員 [Product].[Subcategory].[Top 5] 在 Reseller Order Count 這一列上反映出來的度量值就是我們所期望的 non-additive 半累加度量值。因為上面我們提到過 Reseller Order Count 在Cube設計之初它是通過 Distinct Count 來統計出來 Order 的數量的。而對於累加的 Reseller Sales Amount 和 Reseller Transaction Count 度量值來說,它之前怎么設計的,在 Aggregate 中也就怎么如實給反映出來。
如果改用 SUM 函數會發現 Reseller Order Count 的數量增多了,那時因為沒有去掉重復的 Order,直接給 SUM 了。
在這里要強調的是這幾個例子並不是要說明我們應該在任何時候在MDX中都使用 Aggregate 函數而避免使用 Sum 函數。而應該是,在只需要使用 Sum 函數的時候使用 Sum 函數,如果需要基於度量值在設計之初的定義時那么就可以再使用 Aggregate 函數。
計算平均值的 AVG 函數
Avg( {Set} [, Expression])
語法和 Sum 或者 Aggregate 很類似,指定的 SET 集合中的每一個元組參入平均值的計算,所使用的度量值可以是默認的也可以是表達式中提供的,所有不為空的數值將會被計算並返回一個平均值。
先看 2003年各個月的情況
SELECT {([Measures].[Reseller Sales Amount])} ON COLUMNS, { [Date].[Calendar].[Month].[January 2003]:[Date].[Calendar].[Month].[December 2003] } ON ROWS FROM [Step-by-Step]
下面的這個例子根據 Calendar Year 求 Reseller Sales 的平均值:
WITH MEMBER [Date].[Calendar].[CY 2003 Monthly Average] AS Avg( { [Date].[Calendar].[Month].[January 2003]:[Date].[Calendar].[Month].[December 2003] }, ([Measures].CurrentMember) ) SELECT {([Measures].[Reseller Sales Amount])} ON COLUMNS, {[Date].[Calendar].[CY 2003 Monthly Average]} + { [Date].[Calendar].[Month].[January 2003]:[Date].[Calendar].[Month].[December 2003] } ON ROWS FROM [Step-by-Step]
也可以根據季度來求平均值:
WITH MEMBER [Date].[Calendar].[CY 2003 Quarterly Avg Reseller Sales] AS Avg( { [Date].[Calendar].[Calendar Quarter].[Q1 CY 2003]: [Date].[Calendar].[Calendar Quarter].[Q4 CY 2003] }, [Measures].CurrentMember ) MEMBER [Date].[Calendar].[CY 2003 Monthly Avg Reseller Sales] AS Avg( { [Date].[Calendar].[Month].[January 2003]: [Date].[Calendar].[Month].[December 2003] }, [Measures].CurrentMember ) SELECT {([Measures].[Reseller Sales Amount])} ON COLUMNS, { ([Date].[Calendar].[CY 2003 Monthly Avg Reseller Sales]), ([Date].[Calendar].[CY 2003 Quarterly Avg Reseller Sales]) } + Hierarchize( { [Date].[Calendar].[Month].[January 2003]: [Date].[Calendar].[Month].[December 2003] } + { [Date].[Calendar].[Calendar Quarter].[Q1 CY 2003]: [Date].[Calendar].[Calendar Quarter].[Q4 CY 2003] } ) ON ROWS FROM [Step-by-Step]
平均值和表達式
先看下面的這個例子,在這幅圖事實表中的 6 條交易數據,有3條是2003年的,另外3條是2004的。
如果基於這個事實表來求 Reseller Sales Amount 的平均值的話,那么計算的結果就應該把所有的值加起來然后除以 6 得到一個 150 的平均值。
如果在 MDX 中來求這個平均值的話,首先需要定義一個集合 SET。我們假設這個集合是由 CY 2003 和 CY 2004 來組成,那么我們 AVG 的函數將取得 CY 2003 的 300 和 CY 2004 的600 然后除以 2 得到一個平均值,結果是 450。
那么這兩個平均值的結果就不同了,一個是 150 一個是 450。事實上兩者都沒有錯,因為它們表示的是不同的含義。因為第一種是基於業務領域的問題:“Reseller Sales Amount 的平均值是多少?” 而第二個邏輯則表達的是 “Reseller Sales Amount 的年平均值是多少?”
基於業務領域的平均值在關系型數據庫中是一種典型的平均值計算方式,多維數據集數據庫中也可以像這樣來計算平均值但是需要借用到表達式並 Count 度量值而不是 Avg 函數。
SELECT { ([Measures].[Reseller Sales Amount]), ([Measures].[Reseller Order Count]) } ON COLUMNS, { [Date].[Calendar Year].[CY 2001]:[Date].[Calendar Year].[CY 2004] } ON ROWS FROM [Step-by-Step]
先取到 CY 2001 年 到 2004年間的 Reseller Sales Amount 和 Reseller Order Count 的數據。
為了查詢在這四年中月份的平均 Reseller Sales Amount 是多少使用到了 AVG 函數。
WITH MEMBER [Measures].[Monthly Avg Reseller Sales Amount] AS Avg( EXISTING [Date].[Calendar].[Month].Members, [Measures].[Reseller Sales Amount] ) SELECT { ([Measures].[Reseller Sales Amount]), ([Measures].[Reseller Order Count]), ([Measures].[Monthly Avg Reseller Sales Amount]) } ON COLUMNS, { [Date].[Calendar Year].[CY 2001]:[Date].[Calendar Year].[CY 2004] } ON ROWS FROM [Step-by-Step]
可以看到每一年的月平均銷售額都不一樣 -
在計算成員中,使用到了EXISTING 關鍵字就可以使得 SET 集合中的月使用了當前上下文環境,即 CY 2001 - CY 2004 的年成員。表達式中的 [Measures].[Reseller Sales Amount] 度量值將由集合中每一個月成員決定並且聚集求得平均值返回。 如果不加上 EXISTING 關鍵字,那么就沒有上下文環境了,即表示的是所有月份的 Reseller Sales Amount 的平均值。
在表達式中使用 SET 集合時 SET 集合不會被當前單元格的上下文所影響,這樣一來分析服務中的 Auto Exists 機制就無法應用。因此使用 EXISTING 關鍵字可以強制在計算成員表達式中的 SET 使用當前上下文環境。
在我的 這一篇筆記 中詳細解釋了 EXISTING 關鍵字的作用。
上面的這個查詢中 2003 年的平均額度要比 2004年的要高,是不是因為在 2003 年中訂單中的平均銷售額要更高一些呢,下面是對 Order item level 的平均值計算。
WITH MEMBER [Measures].[Average Reseller Sales Amount] AS ([Measures].[Reseller Sales Amount]) / ([Measures].[Reseller Transaction Count]) ,FORMAT_STRING="Currency" MEMBER [Measures].[Monthly Avg Reseller Sales Amount] AS Avg( EXISTING [Date].[Calendar].[Month].Members, [Measures].[Reseller Sales Amount] ) SELECT { ([Measures].[Reseller Sales Amount]), ([Measures].[Reseller Order Count]), ([Measures].[Reseller Transaction Count]), ([Measures].[Monthly Avg Reseller Sales Amount]), ([Average Reseller Sales Amount]) } ON COLUMNS, { [Date].[Calendar Year].[CY 2001]: [Date].[Calendar Year].[CY 2004] } ON ROWS FROM [Step-by-Step]
查詢的結果顯示了2004年訂單中的平均銷售額要比 2003 年的要高一些。
更多 BI 文章請參看 BI 系列隨筆列表 (SSIS, SSRS, SSAS, MDX, SQL Server) 如果覺得這篇文章看了對您有幫助,請幫助推薦,以方便他人在 BIWORK 博客推薦欄中快速看到這些文章。