數據倉庫 - 維度、事實或退化維度
我正在為一個計費系統建構一個倉庫,該系統在同一張發票上對許多服務/公用事業進行計費,因此我在符合公認的倉庫設計方法時遇到了真正的麻煩。我想我仍然非常戴著 OLTP 帽子。
例如,可以生成一張包含電費、燃氣費和行動電話費的發票。
賬單的所有三個組成部分,本身有兩個組成部分,固定費用和基於使用的費用。即使簡化為只是一部手機,它仍然意味著儲存業務所需的詳細程度我需要 2 個事實表:
FactMobileFixedCharge FactMobileCalls
這些中的每一個都進入一張發票,除此之外還有發票級別的交易,所以我現在需要一個進一步的事實表:
FactInvoiceTransaction
這個簡單範例的基本架構如下:
我所做的所有閱讀都接近
InvoiceLineItem
範例以解決發票看起來像事實和維度的問題,其中發票的事實基本上被傳播到子表,導致一些冗餘但符合標準實踐。在生產計費系統本身中,有 15 個表包含出現在發票上的項目、一個包含發票級別費用的匯總表和按類別分類的其他 15 個表中的費用匯總表,以及發票表本身。
不過,我覺得我的情況與此不符,因為發票有許多不同的東西也需要報告(發票地址、發票上的行銷材料、付款方式、發票格式、電子郵件、線上或紙質賬單,結轉餘額、自上次發票後的付款等)。雖然如果我有一個或兩個表,這是可以接受的冗餘,但對於 15 個事實表來說似乎很多。
除了在有多個表時有很多冗餘之外,我還需要連結
FactMobileCall
並FactMobileCharge
獲取同一張發票的記錄,而且我不相信退化維度是連結兩者的好方法。基本上,這一切都指向
Invoice
一個維度,尤其是在發票級別沒有測量值的情況下,但仍然感覺這應該是一個事實,因為每次導入只會有新記錄。這導致了我的問題:
- 我認為在這種情況下應將發票視為事實是否正確?
- 如果是這樣,是否可以接受更多事實來引用這一事實,或者是否有更好的方法來解決這個問題?
- 我不敢相信我是唯一遇到
InvoiceLineItem
範例似乎不適合的情況的人。有沒有公認的方法來處理這個問題?有關更多資訊,我的倉庫模式目前看起來像這樣(我已經承認 Invoice 是一個類似於維度的事實):
這就是我自己處理它的方式。請記住將您的 Facts 視為“動作”或“動詞”,將您的維度視為您的事實的描述符。
因此,您的發票和發票行項目都是事實。處理此問題的一種方法是您提到的,將事實表製作為發票級別行項目的粒度。這將導致您提到的某些事情的冗餘。如果對帳單地址維度和某些發票級別的事實(例如稅)沒有問題,甚至可以將其分解為行級別項目,然後可以匯總回發票級別的金額。
如果您的發票級別事實不能很好地分解,這種方法可能會失敗。例如,是否允許客戶使用多種付款方式?發票上是否附有不止一種類型的行銷材料?行銷材料是否與特定行項目相關?
提出這些問題肯定會導致您創建多個事實,例如 FactInvoiceLineItem、FactInvoice、FactPayments 等,您最終會將這些事實與發票編號等資訊聯繫在一起。這樣做將使您更靈活地更好地分析特定事實。
如果採用這種方法並且您正在從事實表建構多維數據集,我建議使用視圖來載入您的多維數據集。通過這種方式,您可以對發票有一個高級視圖,該發票加入了您的各種事實表,並在其中進行了度量,例如行項目數、付款次數等。
綜上所述,我相信走這條路比拆分具有相同粒度的事實表要好得多。
如果您需要更多幫助來解決問題,請隨時回復更多詳細資訊。
一個兩層的事實表通常比它的價值更麻煩。這是你 OLTP 正常化的本能 - 與之抗爭!我花了幾個月的時間來理解倉庫/星型模式範式。
所有這些發票標題資訊都可以放入幾個額外的維度中。這只會在
INT
您的事實表中添加幾列,因此所需的額外空間 - 它很重要 - 是最小的。這些維度表將只包含不同的數據集,因此實際上幾乎沒有冗餘。根據您的描述,我將從以下內容開始:
Invoices
- InvoiceSK(合成密鑰,非集群 PK)
- 固定收費(事實)
- 用法(事實)
- UsageCharge(事實)
- InvoiceNumber(退化維度)
- AccountSK(合成密鑰,FK to
Accounts
)- MarketingMaterialSK(合成密鑰,FK to
MarketingMaterial
)- ProductSK(合成密鑰,FK to
Products
)- DateBilledSK(合成密鑰,FK to
Dates
)- DatePaidSK(合成密鑰,FK to
Dates
)
Payments
- PaymentSK(合成密鑰,非集群PK)
- 支付金額(事實)
- AccountSK(合成密鑰,FK to
Accounts
)- PaymentMethodSK(合成密鑰,FK to
PaymentMethods
)- DatePaidSK(合成密鑰,FK to
Dates
)
Accounts
這將是類型 2 SCD,因此為每個事實記錄儲存的資訊(例如,地址)反映了當時文件上的資訊。
- 帳號
- 名字,姓氏
- 街道地址、城市、地區、國家、郵政編碼、區號
Products
- 產品類別(電、氣、電話)
- 產品名稱
- 產品程式碼
我會讓外鍵純粹是合乎邏輯的,而不是強制執行的;只要您的 ETL 很緊,您就不需要成本。
我不知道
PaymentMethods
您的數據庫中需要什麼。如果只是“他們使用支票、卡/借記卡,還是這是賬單調整?” 你可以把它變成一個退化的維度Payments
;如果它有卡號和到期日期等欄位,則應將其設為維度以避免載入事實表。您可能想要區分
BillingAddress
和ServiceAddress
。您可以在表中創建兩組地址欄位Accounts
,或者將地址配置到一個新表中並讓事實表引用兩者。在其他條件相同的情況下,我傾向於第一個 - 它會使事實表盡可能窄。棘手的部分在於連接
Invoices
到Payments
. 這是一個多對多的關係,在倉庫中往往會很痛苦。在許多情況下,您不需要:您可以匯總Invoices
到某個時間點,並簡單地報告到期餘額(您可以將其儲存在每月快照表中)Payments
。AccountNumber
但是,業務使用者可能需要更精確的突破。該表可以非常簡單:
PaymentAllocation
- 發票SK
- 付款SK
- 數量
- { InvoiceSK, PaymentSK } 上的 PK
…但很少決定每筆付款中有多少美元應該用於每個發票行項目。將收入分配給尚未全額支付的最早發票行項目?這是一個開始。
類似的方法適用於將成本分配給收入,反之亦然,以計算細粒度的利潤。這是很多工作,但最終您可以計算出哪些客戶和產品實際上在推動業務 - 這就是黃金。
未來的步驟可能是創建一個
Households
表來對單個地址的帳戶進行分組。