[SQL]建立時間資料表

[SQL]建立時間資料表

之前在剛接觸 BI 的時候,時常會有個納悶,為什麼要產生個時間資料表,看書上一些 Sample 都只是看起來要把時間分出年、季、月,因此也就沒有太多的去了解了。

 

最近在測試一些 Power Pivot 的時候才發覺,如果當我們的資料來源來自許多不同的表格的時候,透過時間資料表來將這些資料給串再一起,就可以免去要去把這些表格要在事前先把它給 Union 在一起,但要怎麼去產生這樣的時間資料表,直覺上就是要去把所有的資料表的時間放到一個 Table 內,感覺上似乎就有點麻煩。

 

昨天在聽一場課程中,老師介紹到一個方式,原來利用 CTE 遞迴的方式來做處理,就可以解決了,老師參考書上的做法列出一個範例。

-- 取自 Microsoft SQL Server 2012 Analysis Services: The BISM Tabular Model    P. 294
DECLARE @StartYear AS INT = 2000 ; 
DECLARE @EndYear AS INT = 2006 ;

WITH Years 
    AS ( SELECT YYYY = @StartYear 
    UNION ALL 
    SELECT YYYY + 1 
    FROM Years 
    WHERE YYYY < @EndYear 
    ), 
Months 
    AS ( SELECT MM = 1 
    UNION ALL 
    SELECT MM + 1 
    FROM Months 
    WHERE MM < 12 
    ), 
Days 
    AS ( SELECT DD = 1 
    UNION ALL 
    SELECT DD + 1 
    FROM Days 
    WHERE DD < 31 
    ), 
DatesRaw 
    AS ( SELECT YYYY = YYYY, 
    MM = MM, 
    DD = DD, 
    ID_Date = YYYY * 10000 + MM * 100 + DD, 
    Date = CASE WHEN ISDATE(YYYY * 10000 + MM * 100 + DD) = 1 
    THEN CAST(CAST(YYYY * 10000 + MM * 100 
    + DD AS VARCHAR) AS DATE) 
    ELSE NULL 
    END 
    FROM Years 
    CROSS JOIN Months 
    CROSS JOIN Days 
    WHERE ISDATE(YYYY * 10000 + MM * 100 + DD) = 1 
    ) 
SELECT d.ID_Date, 
    d.Date, 
    [Year] = YEAR(d.Date), 
    MonthNumber = MONTH(d.Date), 
    [Month] = DATENAME(MONTH, d.Date), 
    DayOfMonth = DAY(d.Date), 
    DayOfWeekNumber = DATEPART(dw, d.Date), 
    [DayOfWeek] = DATENAME(dw, d.Date), 
    WorkingDay = CAST(CASE DATEPART(dw, d.Date) 
    WHEN 1 THEN 0 -- Sunday 
    WHEN 7 THEN 0 -- Saturday 
    ELSE 1 -- Might lookup for a holidays table here 
    END AS BIT) 
FROM DatesRaw d 
ORDER BY d.Date

 

上述這個範例在 CTE 內先利用三個子 Table,將年、月、日分開產生出集合,接著在用 Cross Join 把這些合併起來,因此我依樣畫葫蘆,做一個自己的版本來使用

DECLARE @StartYear AS INT = 2000 ; 
DECLARE @EndYear AS INT = 2006 ;

WITH AllDays 
    AS ( 
        SELECT [Date] = CAST( CAST(@StartYear as varchar ) + '0101' AS DATE )
        UNION ALL 
        SELECT [Date] = DATEADD( day, 1 , [Date] ) FROM AllDays where [Date] < CAST( CAST(@EndYear as varchar ) + '1231' AS DATE ) 
        ) 
SELECT 
    CONVERT(varchar,d.[Date],112) ID_Date,
    d.Date,
    [Year] = YEAR(d.Date), 
    MonthNumber = MONTH(d.Date), 
    [Month] = DATENAME(MONTH, d.Date), 
    DayOfMonth = DAY(d.Date), 
    DayOfWeekNumber = DATEPART(dw, d.Date), 
    [DayOfWeek] = DATENAME(dw, d.Date), 
    WorkingDay = CAST(CASE DATEPART(dw, d.Date) 
    WHEN 1 THEN 0 -- Sunday 
    WHEN 7 THEN 0 -- Saturday 
    ELSE 1 -- Might lookup for a holidays table here 
    END AS BIT) 
FROM AllDays d OPTION( MaxRecursion 10000 )

 

由於 CTE 預設的最大地迴是 100 ,因此在原範例中最大的遞迴數量是 31,因此不用加上查詢提示,而我這個做法則是用加法的方式讓他一天一天去跑,因此只要超過一年就會超過預設值的限制,因此我在最後加上一個 MAXRECURSION 的查詢提示,並且設定為 10000,如果有需要更多的可以在調整那個數字。