該錶的歷史數據變化:如何最好地處理在一個漸變維度(SCD2)
該公司爲我工作已經使用金博爾方法倉庫一個漸變維度(僱員數據)。包含此數據的維度表包含一個主鍵(int identity employee_key
,用作其他表中的代理),自然鍵(employee_id
),有效日期範圍(valid_date
和invalid_date
)以及隨時間跟蹤的各種SCD1和SCD2數據元素。下面是一個簡化的例子:
employee_key | employee_id | valid_date | invalid_date | employee_name | employee_role
1 | 1001 | 1/1/2015 | 6/1/2015 | Bob | DBA
2 | 1001 | 6/2/2015 | NULL | Bob | Developer
3 | 1002 | 1/1/2015 | NULL | Jill | DBA
在上述例子中,employee_key
是主鍵(替代)和employee_id
是自然鍵。其他值應該有希望是自我解釋。此表反映:
- Bob是從2015年1月1日開始到2015年6月1日結束的DBA。
- Bob是一名開發人員,開始於2015年6月2日,目前擔任該角色。
- Jill是從2015年1月1日開始的DBA,目前擔任該角色。
現在,我們也有一些參考這個維度的事實表。一個這樣的事實表包含了員工記錄的所有時間,並且細緻到當天。我們並不真正關注這些表的結構,只是使用代理鍵鏈接到我們的員工維度,並且他們通常包含很多行(在10M-200M之間)。下面是一個包含時間的事實表的示例記錄:
calendar_dt | employee_id | employee_key | time_code | hours
1/1/2015 | 1001 | 1 | 1234 | 2.25
1/1/2015 | 1001 | 1 | 21 | 3.50
1/2/2015 | 1001 | 1 | 21 | 8.00
...
6/1/2015 | 1001 | 1 | 21 | 4.00
通過代理鍵employee_key
成爲一個重要的商業目的鏈接到員工層面 - 它能夠準確的歷史報告,而不昂貴的聯接使用BETWEEN
操作。例如,它讓我們說,Bob在2015年6月1日記錄的時間可歸因於他的DBA角色,並且Bob在2015年6月2日記錄的時間可歸因於他的開發人員角色。
據我所知,這是一個有點標準的金球實施。
問題:
此實現不處理數據的修正很好。比方說,在我們之前的例子中,HR告訴我們Bob轉移到分析師角色的有效日期範圍爲2015年5月1日至2015年6月1日,並且他們未能將其輸入到系統中。這給我們帶來了一個主要問題:我們需要將employee_key = 1
所在的行拆分成兩行,並使用不同的有效/無效日期。此外,我們需要找到現在錯誤地引用employee_key = 1
並更新它們的所有地方。這裏是問題:
- 我們需要在一些巨大的表上運行昂貴的更新操作。每次需要進行更正時,我們都不能做到這一點。
- 尺寸行拆分需要手動完成,使表有數據輸入錯誤或有效/無效日期範圍重疊的風險。
- 拆分行違反了一條重要規則:主鍵是不可變的,一旦賦值就永遠不會改變。
解決方案:
我能想到的一些解決這一辦法的,但沒有一個是優雅:
- 與更新替代的關鍵數據的噩夢只是承擔。也許強制糾正發生在正常的時間表上,減少了我們運行此更新所需的次數。
- 將員工維度錶轉換爲每位員工每日行表。這具有允許在
employee_id
和calendar_dt
上進行自然鍵連接的益處。它還使這個關鍵字不可變,並允許識別適當的代理關鍵字值,而無需在維度表中查找它。無論維度表如何變化,事實表將始終引用正確的行。這有一個主要缺點,即將我們的100,000行數據錶轉換爲20M行數據。
還有什麼其他解決方案?我不能成爲唯一遇到這個問題的人......幫幫我!
注意事項:
- 我們作出這樣的數據將永遠需要一個時間要素(即粒度總是會在一天水平)的假設。
- 我們假設
employee_id
值永遠不會改變(是的,我知道這是一個危險的假設)。