微軟BI 之SSAS 系列 - 實現Cube 以及角色扮演維度,度量值格式化和計算成員的創建


在熟悉完下面這三種維度的創建方式之后,就可以開始創建我們的第一個 Cube 了。

SSAS 系列 - 自定義的日期維度設計

SSAS 系列 - 基於雪花模型的維度設計

SSAS系列 - 關於父子維度的設計

我們將使用下面的這些腳本來創建一些維度表和事實表,數據源的來源是 AdventureWorksDW2012,但由於數據列太多因此我精簡了一些表並且自定義了 DimDate 表。

USE BIWORK_SSIS
GO
SET NOCOUNT ON
IF OBJECT_ID('FactInternetSales','U') IS NOT NULL
DROP TABLE FactInternetSales

IF OBJECT_ID('FactResellerSales','U') IS NOT NULL
DROP TABLE FactResellerSales

IF OBJECT_ID('DimEmployee','U') IS NOT NULL
DROP TABLE DimEmployee

IF OBJECT_ID('DimDate','U') IS NOT NULL
DROP TABLE DimDate 

IF OBJECT_ID('DimProduct','U') IS NOT NULL
DROP TABLE DimProduct 

IF OBJECT_ID('DimProductSubcategory','U') IS NOT NULL
DROP TABLE DimProductSubcategory

IF OBJECT_ID('DimProductCategory','U') IS NOT NULL
DROP TABLE DimProductCategory
GO

CREATE TABLE DimDate
(
    DateKey INT PRIMARY KEY,
    ShortDateName NVARCHAR(12) NOT NULL,
    FullDateName NVARCHAR(20)  NOT NULL, 
    DayNumberOfWeek TINYINT NOT NULL,
    DayNameOfWeek NVARCHAR(10) NOT NULL,
    DayNumberOfMonth TINYINT NOT NULL,
    DayNumberOfYear SMALLINT NOT NULL, 
    WeekNumberOfYear TINYINT NOT NULL,
    IsWeekend NVARCHAR(7) NOT NULL, 
    IsLeapYear BIT NOT NULL,
    MonthKey INT NOT NULL,
    MonthNumberOfYear TINYINT NOT NULL,
    MonthNameOfYear NVARCHAR(10) NOT NULL,
    MonthNameWithYear NVARCHAR(20) NOT NULL, 
    CalendarQuarterKey INT NOT NULL, 
    CalendarQuarterNumber TINYINT NOT NULL, 
    CalendarQuarterNameWithYear NVARCHAR(20) NOT NULL, 
    CalendarSemesterNumber TINYINT NOT NULL,
    CalendarYearKey SMALLINT NOT NULL, 
    CalendarYearName NVARCHAR(20) NOT NULL, 
    FiscalQuarterKey INT,
    FiscalQuarterNumber TINYINT NOT NULL, 
    FiscalQuarterName NVARCHAR(20),
    FiscalSemester TINYINT NOT NULL,
    FiscalYearKey SMALLINT NOT NULL, 
    FiscalYearName NVARCHAR(20),
)

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME

SELECT @StartDate = '2005-01-01',
       @EndDate = '2013-12-31'

WHILE (@StartDate <= @EndDate)
BEGIN
    INSERT INTO DimDate 
    (
        DateKey,
        ShortDateName,
        FullDateName, 
        DayNumberOfWeek,
        DayNameOfWeek,
        DayNumberOfMonth,
        DayNumberOfYear, 
        WeekNumberOfYear,
        IsWeekend,
        IsLeapYear, 
        MonthKey,
        MonthNumberOfYear,
        MonthNameOfYear, 
        MonthNameWithYear,  
        CalendarQuarterKey,
        CalendarQuarterNumber,
        CalendarQuarterNameWithYear,
        CalendarSemesterNumber,
        CalendarYearKey, 
        CalendarYearName,  
        FiscalQuarterNumber, 
        FiscalSemester,
        FiscalYearKey 
    )
    SELECT CAST(CONVERT(VARCHAR(8),@StartDate,112) AS INT) AS 'DateKey',
           CONVERT(VARCHAR(20), @StartDate,106) AS 'ShortDateName',
           CONVERT(VARCHAR(2),DATENAME(DD,@StartDate)) 
                    + ' ' 
                    + DATENAME(MM,@StartDate) 
                    + ' ' 
                    + CONVERT(CHAR(4), DATEPART(YY,@StartDate)) AS 'FullDateName', -- 1 July 2005  
           DATEPART(DW,@StartDate) AS 'DayNumberOfWeek',
           DATENAME(DW,@StartDate) AS 'DayNameOfWeek',
           DATENAME(DD,@StartDate) AS 'DayNumberOfMonth',
           DATENAME(DY,@StartDate) AS 'DayNumberOfYear',
           DATEPART(WW,@StartDate) AS 'WeekNumberOfYear',
           CASE WHEN DATEPART(DW,@StartDate) IN (1,7)  
                    THEN 'Weekend'
                ELSE 'Weekday' 
           END AS 'IsWeekend',
           CASE WHEN ((YEAR(@StartDate) % 4 = 0) AND (YEAR(@StartDate) % 100 != 0 OR YEAR(@StartDate) % 400 = 0))
                    THEN 1
                ELSE 0
           END AS 'IsLeapYear', 
           DATEPART(YY,@StartDate) * 100 + DATEPART(MM,@StartDate) AS 'MonthKey', -- 200507
           DATEPART(MM,@StartDate) AS 'MonthNumberOfYear',
           DATENAME(MM,@StartDate) AS 'MonthNameOfYear',
           DATENAME(MM,@StartDate) + ' ' + CONVERT(CHAR(4),DATEPART(YY,@StartDate)) AS 'MonthNameWithYear',  -- July 2005  
           DATEPART(YY,@StartDate) * 100 + DATEPART(QQ,@StartDate) AS 'CalendarQuarterKey',  -- 200503
           DATEPART(QQ,@StartDate) AS 'CalendarQuarterNumber',
           'CY ' + CONVERT(CHAR(4),DATEPART(YY,@StartDate)) 
                 + ' Qtr ' 
                 + CONVERT(CHAR(1), DATEPART(QQ,@StartDate)) AS 'CalendarQuarterNameWithYear', -- CY 2005 Qtr 3  
           CASE WHEN DATEPART(MM,@StartDate) BETWEEN 1 AND 6
                    THEN 1
                ELSE 2
           END AS 'CalendarSemester',
           DATEPART(YY,@StartDate) AS 'CalendarYearKey',  
           'CY ' + CONVERT(CHAR(4),DATEPART(YY,@StartDate)) AS 'CalendarYearName', -- CY 2005 
           CASE WHEN DATEPART(MM,@StartDate) BETWEEN 1 AND 6
                     THEN DATEPART(QQ,@StartDate) + 2
                ELSE DATEPART(QQ,@StartDate) - 2
           END AS 'FiscalQuarter',
           CASE WHEN DATEPART(MM,@StartDate) BETWEEN 1 AND 6
                    THEN 2
                ELSE 1
           END AS 'FiscalSemester',
           CASE WHEN DATEPART(MM,@StartDate) BETWEEN 1 AND 6
                    THEN DATEPART(YY,@StartDate) 
                ELSE DATEPART(YY,@StartDate) + 1
           END AS 'FiscalYear' 
    
           UPDATE DimDate 
           SET FiscalQuarterKey = FiscalYearKey * 100 + FiscalQuarterNumber,  -- 200601
               FiscalYearName = 'FY ' + CONVERT(CHAR(4), FiscalYearKey),   -- FY 2006
               FiscalQuarterName =  'FY ' + CONVERT(Char(4), FiscalYearKey) + ' Qtr ' + CONVERT(CHAR(1), FiscalQuarterNumber) -- FY 2006 Qtr 1      
           WHERE DateKey = CONVERT(INT,CONVERT(VARCHAR(8),@StartDate,112))
        
    SET @StartDate = @StartDate + 1
END

SELECT EmployeeKey,
       ParentEmployeeKey,
       EmployeeNationalIDAlternateKey,
       CASE WHEN ISNULL(MiddleName,'') = ''  THEN FirstName +' '+ LastName  
            ELSE FirstName +' '+ MiddleName +' '+LastName
       END AS FullName,
       Title 
INTO DimEmployee
FROM AdventureWorksDW2012.dbo.DimEmployee

SELECT ProductKey,
       ProductAlternateKey,
       ProductSubcategoryKey,
       EnglishProductName,
       StandardCost,
       Color,
       SafetyStockLevel,
       ListPrice,
       Class,
       Size,
       StartDate,
       EndDate,
       [Status],
       ProductAlternateKey + ' (' + CONVERT (Char(10), StartDate, 120) + ')' AS ProductID
INTO DimProduct
FROM AdventureWorksDW2012.dbo.DimProduct

SELECT ProductSubcategoryKey,
       ProductSubcategoryAlternateKey,
       EnglishProductSubcategoryName,
       ProductCategoryKey 
INTO DimProductSubcategory
FROM AdventureWorksDW2012.dbo.DimProductSubcategory

SELECT ProductCategoryKey,
       ProductCategoryAlternateKey,
       EnglishProductCategoryName
INTO DimProductCategory
FROM AdventureWorksDW2012.dbo.DimProductCategory 

SELECT ProductKey,
       OrderDateKey,
       EmployeeKey,
       SalesOrderLineNumber,
       SalesOrderNumber,
       UnitPrice,
       ProductStandardCost,
       SalesAmount
INTO FactResellerSales
FROM AdventureWorksDW2012.dbo.FactResellerSales

SELECT ProductKey,
       OrderDateKey,
       DueDateKey,
       ShipDateKey,
       SalesOrderNumber,
       SalesOrderLineNumber,
       OrderQuantity,
       UnitPrice,
       SalesAmount
INTO FactInternetSales
FROM AdventureWorksDW2012.dbo.FactInternetSales

------------------------------------------------------------------------------
-- Add Primary Key Constraint
------------------------------------------------------------------------------  
ALTER TABLE DimEmployee 
ADD CONSTRAINT PK_EmployeeKey PRIMARY KEY CLUSTERED (EmployeeKey)

ALTER TABLE DimProduct 
ADD CONSTRAINT PK_ProductKey PRIMARY KEY CLUSTERED(ProductKey)

ALTER TABLE DimProductSubcategory 
ADD CONSTRAINT PK_SubcategoryKey PRIMARY KEY CLUSTERED(ProductSubcategoryKey)

ALTER TABLE DimProductCategory 
ADD CONSTRAINT PK_CategoryKey PRIMARY KEY CLUSTERED(ProductCategoryKey)

ALTER TABLE FactInternetSales 
ADD CONSTRAINT PK_InternetSales PRIMARY KEY CLUSTERED(SalesOrderNumber,SalesOrderLineNumber)

ALTER TABLE FactResellerSales 
ADD CONSTRAINT PK_ResellerSales PRIMARY KEY CLUSTERED(SalesOrderNumber,SalesOrderLineNumber)

------------------------------------------------------------------------------
-- Add Primary Key Constraint
------------------------------------------------------------------------------
ALTER TABLE FactResellerSales
ADD CONSTRAINT FK_Reseller_EmployeeKey FOREIGN KEY(EmployeeKey) REFERENCES DimEmployee(EmployeeKey)

ALTER TABLE FactResellerSales
ADD CONSTRAINT FK_Reseller_ProductKey FOREIGN KEY(ProductKey) REFERENCES DimProduct(ProductKey)

ALTER TABLE FactResellerSales
ADD CONSTRAINT FK_Reseller_OrderDateKey FOREIGN KEY(OrderDateKey) REFERENCES DimDate(DateKey) 

ALTER TABLE FactInternetSales
ADD CONSTRAINT FK_Internet_ProductKey FOREIGN KEY(ProductKey) REFERENCES DimProduct(ProductKey)

ALTER TABLE FactInternetSales
ADD CONSTRAINT FK_Internet_OrderDateKey FOREIGN KEY(OrderDateKey) REFERENCES DimDate(DateKey)

ALTER TABLE FactInternetSales
ADD CONSTRAINT FK_Internet_ShipDateKey FOREIGN KEY(ShipDateKey) REFERENCES DimDate(DateKey)

ALTER TABLE FactInternetSales
ADD CONSTRAINT FK_Internet_DueDateKey FOREIGN KEY(DueDateKey) REFERENCES DimDate(DateKey) 

ALTER TABLE DimProduct 
ADD CONSTRAINT FK_Product_SubcatetoryKey FOREIGN KEY (ProductSubcategoryKey) REFERENCES DimProductSubcategory (ProductSubcategoryKey)

ALTER TABLE DimProductSubcategory 
ADD CONSTRAINT FK_Subcategory_CatetoryKey FOREIGN KEY (ProductCategoryKey) REFERENCES DimProductCategory (ProductCategoryKey)

SELECT * FROM DimEmployee 
SELECT * FROM DimDate 
SELECT * FROM DimProduct
SELECT * FROM DimProductSubcategory
SELECT * FROM DimProductCategory
SELECT * FROM FactResellerSales
SELECT * FROM FactInternetSales
View Code

下面可以看到維度表和事實表,其中包括了常用的時間維度,父子維度和雪花型維度的概念。另外要注意到 FactInternetSales 表中的 OrderDateKey, DueDateKey, ShipDateKey 都關聯到了 DimDate 的主鍵 DateKey。 在后面有一個概念 Role-Playing Dimension 角色扮演維度就和這里的幾個時間列有關。

創建數據源,數據源視圖以及時間維度,父子維度,標准維度等過程就不再重復了,可以參照文章開頭部分的幾篇文章。

下面顯示的是 Product 維度以及維度屬性,同時有兩個自定義的層次結構 Product by Category 和 Size by Color。

時間維度 - Date 以及兩個自定義的屬性層次結構 Calendar Date 和 Fiscal Date 。

父子維度 Employee -

創建完各個維度之后,我們將創建多維數據集 Cube。指定數據源視圖 DSV_BIWORK,並選擇事實表,這個向導將為每一個事實表創建一個度量值組,包含了由表中各列所創建的度量值。

有兩個事實表,因此就有兩個度量值組,並且向導將為非維度鍵的事實表中的每一個數值列創建一個度量值。由於我們這里不需要那么多,所以只選擇部分度量值。另外要注意,度量值的名稱源於事實表中的列,所有名稱由可能相同。但是在多維數據集中,由於度量值的名稱必須是唯一的,所以向導會在重復的度量值名稱后添加所屬的度量值組名稱。

下一步的時候多維數據集的向導識別了度量值組即之前的事實表與之相關的維度表,因此這里全部會顯示出來。

為多維數據集取一個名稱並保存。

可以看到在 Cube 文件夾中出現一個 BIWORK_FirstCube.cube ,這就是我們創建好的多維數據集。 最左側能看到度量值組和度量值,左下側可以看到多維數據集中的維度。

那么要注意到我們在數據庫中的維度是 DimDate ,但是在多維數據集中就變成了三個時間維度 Order Date,Due Date 和 Ship Daet,這三個多維數據集維度就是角色扮演維度 Role-Playing Dimension。 因為它們本身就是 DimDate, 但是由於 FactInternetSales 中有三個時間列 OrderDateKey, DueDateKey 和 ShipDateKey 都關聯到了 DimDate 表中的 DateKey。因此如果就只有一個 Date 時間維度,那么就分不清這個時間維度到底是指的 Order Date , Due Date 還是 Ship Date,這就是角色扮演維度的產生的原因。

角色扮演維度解釋了數據庫維度 Database Dimension 和多維數據集維度 Cube Dimension 的差異。一個維度的屬性和數據包含在分析服務數據庫中,這就是數據庫維度。一個多維數據集可能包含一個或多個多維數據集維度,它引用了數據庫維度的屬性和數據。

維度用法顯示了各個多維數據集的維度是如何與度量值組進行關聯的,交叉的內容顯示了關聯的維度屬性。

部署這個項目然后到 Browser 瀏覽器中查看多維數據集,注意在 SQL Server 2012 版本中取消了之前在 SQL Server 2008 R2 版本中出現的能夠顯示多維數據的設計,而變成了在 SSRS 報表開發時的多維數據集的設計樣式。 另外,添加了一個 Excel 連接,其實是要求開發者使用 Excel 在 Excel 中查看分析服務中多維數據集的數據。

部署完成后也可以到分析服務中查看多維數據集,左邊可以看到數據庫維度的 Date 維度,右邊創建完一個 MDX 查詢頁面后可以看到多維數據集中的角色扮演維度以及其它維度,度量值組和度量值。

由於 Order Date 角色扮演維度在兩個度量值組中同時出現,所以其實它就可以認為是 Date 維度,為了精簡一下命名,可以先添加一個新的時間維度,然后再把 Order Date 維度給刪除掉。

先添加一個時間維度。

可以看到與 FactResellerSales 的關聯關系,是通過 OrderDateKey 進行關聯的,同樣的道理 FactInternetSales 也是這樣關聯的。

刪除 Order Date 維度。

直接使用 Date 維度,這樣名稱更加簡潔一下,它的作用和 Order Date 的作用是一樣的。

再次部署之后,我們就可以看到一個比較簡潔的 Order Date 維度的替身 Date 維度了。

可以在剛才看到的瀏覽器中打開 Excel  工具,在這里面就會自動加載多維數據集中的維度和度量值組。比如要查看 Internet Sales Amount 和 Reseller Sales Amount 在 Product Category 下的表現情況,那么就可以很容易的拖拽實現。

也可以通過 MDX 查詢實現這樣的效果,查詢在 Product Category  成員下的 Internet Sales Amount 和 Reseller Sales Amount 的情況。

但是在 MDX 查詢中可以看到查詢出來的結果並沒有良好的格式化,因此回到開發設計中對度量值進行格式化,右鍵屬性。

Reseller Sales Amount - Format String - 輸入下面的格式 ,同理 Internet Sales Amount 也這么選擇,表示金額的格式。

而對於 Reseller Sales Count, Order Quantity 和 Internet Sales Count 就只需要像下面這樣設置就可以了。

重新部署之后在 MDX 中查詢的結果 -

在 Excel 中看到的結果 -

有的時候我們可能需要在 MDX 中做出這樣的查詢,即通過 Internet Sales Amount 和 Reseller Sales Amount 計算銷售總額,會通過定義一個計算成員來實現。

WITH MEMBER [Measures].[Sales Amount Total]
AS
   [Measures].[Internet Sales Amount] + [Measures].[Reseller Sales Amount]
,FORMAT_STRING ="$#,#"
SELECT {
        [Measures].[Internet Sales Amount],
        [Measures].[Reseller Sales Amount],
        [Measures].[Sales Amount Total]
       } ON COLUMNS,
NON EMPTY([Product].[Category].Members) ON ROWS
FROM [BIWORK_FirstCube]

如果是我們自己開發的 SSRS 報表來查詢數據沒有問題,但是可能有很多時候客戶會直接從 Excel 上去查詢這些數據,那么我們可以先在設計度量值的時候將這些總額設計好。可以通過在多維數據集中在某一個維度(一般指的是度量值維度)上添加一個計算成員,將相關的計算公式直接存儲在這個成員中。

在多維數據集設計器中選擇計算 Calculations 並在 Script Organizer 的第一個 Command 下右鍵新建一個計算成員。

定義一個名稱並注意需要帶上方括號 [Sales Amount Total],將兩個度量值拖到 Expression 中,並在 Format string 中指定一個帶字符串的金額格式。

部署項目並重新連接就能看到度量值組中多了一個計算成員,可以切換到代碼模式,就能看到這個計算的成員的創建就已經在服務器端存在了,實際上這個創建計算成員的語法和我們在 MDX 查詢的語法是一樣的。

CALCULATE; 
CREATE MEMBER CURRENTCUBE.[Measures].[Sales Amount Total]
 AS [Measures].[Internet Sales Amount] + [Measures].[Reseller Sales Amount], 
FORMAT_STRING = "$#,#", 
VISIBLE = 1  ;

既然已經存在這個度量值計算成員了,那么就直接可以在 MDX 中查詢了。

並且在 Excel 中也可以直接使用這個計算成員了,當然對於使用者來說這就是一個度量值。

在分析服務中創建計算成員一來可以提高計算成員(度量值)的重用性,二來這種常用的度量值計算成員放在一起也便於組織查看和管理,可以避免在各個客戶端 MDX 查詢中的計算成員創建的不統一,最后就是如果用戶並不是使用我們自定義開發的報表,而是通過像 Excel 這種客戶端的工具,常用的匯總計算成員事先定義好后面使用就很方便了。

更多 BI 文章請參看 BI 系列隨筆列表 (SSIS, SSRS, SSAS, MDX, SQL Server) 如果覺得這篇文章看了對您有幫助,請幫助推薦,以方便他人在 BIWORK 博客推薦欄中快速看到這些文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM