Kettle構建Hadoop ETL實踐(九):事實表技術

目錄

一、事實表概述

二、週期快照

1. 修改數據倉庫模式

2. 創建快照表數據裝載Kettle轉換

三、累計快照

1. 修改數據庫模式

2. 修改增量抽取銷售訂單表的Kettle轉換

3. 修改定期裝載銷售訂單事實表的Kettle轉換

4. 修改定期裝載Kettle作業

5. 測試

四、無事實的事實表

1. 建立新產品發佈的無事實事實表

2. 初始裝載無事實事實表

3. 修改定期裝載Kettle作業

4. 測試定期裝載作業

五、遲到的事實

1. 修改數據倉庫模式

2. 修改定期裝載Kettle轉換

3. 修改裝載月銷售週期快照事實表的作業

4. 測試

六、累積度量

1. 修改模式

2. 初始裝載

3. 定期裝載

4. 測試定期裝載

5. 查詢

七、小結


        上兩篇裏介紹了幾種基本的維度表技術,並用示例演示了每種技術的實現過程。本篇說明多維數據倉庫中常見的事實表技術。我們將講述五種基本事實表擴展,分別是週期快照、累積快照、無事實的事實表、遲到的事實和累積度量。和討論維度表一樣,也會從概念開始認識這些技術,繼而給出常見的使用場景,最後以銷售訂單數據倉庫爲例,給出Kettle實現的作業、轉換和測試過程。

一、事實表概述

        發生在業務系統中的操作型事務,其所產生的可度量數值,存儲在事實表中。從最細節粒度級別看,事實表和操作型事務表的數據有一一對應的關係。因此,數據倉庫中事實表的設計應該依賴於業務系統,而不受可能產生的最終報表影響。除數字類型的度量外,事實表總是包含所引用維度表的外鍵,也能包含可選的退化維度鍵或時間戳。數據分析的實質就是基於事實表開展計算或聚合操作。

        事實表中的數字度量值可劃分爲可加、半可加、不可加三類。可加性度量可以按照與事實表關聯的任意維度彙總,就是說按任何維度彙總得到的度量和是相同的,事實表中的大部分度量屬於此類。半可加度量可以對某些維度彙總,但不能對所有維度彙總。餘額是常見的半可加度量,除了時間維度外,它們可以跨所有維度進行加法操作。另外還有些度量是完全不可加的,例如比例。對非可加度量,較好的處理方法是儘可能存儲構成非可加度量的可加分量,如構成比例的分子和分母,並將這些分量彙總到最終的結果集合中,而對不可加度量的計算通常發生在BI層或OLAP層。

        事實表中可以存在空值度量。所有聚合函數,如sum、count、min、max、avg等均可針對空值度量進行計算,其中sum、count(字段名)、min、max、avg會忽略空值,而count(1)或count(*)在計數時會將空值包含在內。然而,事實表中的外鍵不能存在空值,否則會導致違反參照完整性的情況發生。關聯的維度表應該用默認代理鍵而不是空值表示未知的條件。

        很多情況下數據倉庫需要裝載如下三種不同類型的事實表。

  • 事務事實表:以每個事務或事件爲單位,如一個銷售訂單記錄、一筆轉賬記錄等,作爲事實表裏的一行數據。這類事實表可能包含精確的時間戳和退化維度鍵,其度量值必須與事務粒度保持一致。銷售訂單數據倉庫中的sales_order_fact表就是事務事實表。
  • 週期快照事實表:這種事實表裏並不保存全部數據,只保存固定時間間隔的數據,例如每天或每週的銷售額,或每月的賬戶餘額等。
  • 累積快照事實表:累積快照用於跟蹤事實表的變化。例如,數據倉庫可能需要累積或存儲銷售訂單從下訂單的時間開始,到訂單中的商品被打包、運輸和到達的各階段的時間點數據來跟蹤訂單生命週期的進展情況。當這個過程進行時,隨着以上各種時間的出現,事實表裏的記錄也要不斷更新。

二、週期快照

        週期快照事實表中的每行彙總了發生在某一標準週期,如一天、一週或一月的多個度量。其粒度是週期性的時間段,而不是單個事務。週期快照事實表通常包含許多數據的總計,因爲任何與事實表時間範圍一致的記錄都會被包含在內。在這些事實表中,外鍵的密度是均勻的,因爲即使週期內沒有活動發生,通常也會在事實表中爲每個維度插入包含0或空值的行。

        週期快照在庫存管理和人力資源系統中有比較廣泛的應用。商店的庫存優化水平對連鎖企業的獲利將產生巨大影響。需要確保正確的產品處於正確的商店中,在正確的時間儘量減少出現脫銷的情況,並減少總的庫存管理費用。零售商希望通過產品和商店分析每天保有商品的庫存水平。在這個場景下,通常希望分析的業務過程是零售商店庫存的每日週期快照。在人力資源管理系統中,除了爲員工建立檔案外,還希望獲得員工狀態的例行報告,包括員工數量、支付的工資、假期天數、新增員工數量、離職員工數量,晉升人員數量等。這時需要建立一個每月員工統計週期快照。

        週期快照是在一個給定的時間對事實表進行一段時期的總計。有些數據倉庫用戶,尤其是業務管理者或者運營部門,經常要看某個特定時間點的彙總數據。下面在示例數據倉庫中創建一個月銷售訂單週期快照,用於按產品統計每個月總的銷售訂單金額和產品銷售數量。

1. 修改數據倉庫模式

        需求是要按產品統計每個月的銷售金額和銷售數量。單從功能上看,此數據能夠從事務事實表中直接查詢得到。例如要取得2020年10月的銷售數據,可以使用以下的語句查詢:

select b.month_sk, a.product_sk, sum(order_amount), sum(order_quantity)  
  from dw.sales_order_fact a,  
       dw.month_dim b,  
       dw.order_date_dim d
 where a.order_date_sk = d.order_date_sk  
   and b.month = d.month  
   and b.year = d.year  
   and b.month = 10   
   and b.year = 2020
 group by b.month_sk, a.product_sk ;

        只要將年、月參數傳遞給這條查詢語句,就可以獲得任何年月的統計數據。但即便是在如此簡單的場景下,我們仍然需要建立獨立的週期快照事實表。事務事實表的數據量都會很大,如果每當需要月銷售統計數據時,都從最細粒度的事實表查詢,那麼性能將會差到不堪忍受的程度。再者,月統計數據往往只是下一步數據分析的輸入信息,有時把更復雜的邏輯放到一個單一的查詢語句中效率會更差。因此,好的做法是將事務型事實表作爲一個基石事實數據,以此爲基礎,向上逐層建立需要的快照事實表。圖9-1中的模式顯示了一個名爲month_end_sales_order_fact的週期快照事實表。

圖9-1 月銷售統計週期快照事實表

        新的週期快照事實表中有兩個度量值,month_order_amount和month_order_quantity。這兩個值是不能加到sales_order_fact表中的,原因是sales_order_fact表和新度量值有不同的時間屬性,也即數據的粒度不同。sales_order_fact表包含的是單一事務記錄。新的度量值要的是每月的彙總數據。銷售週期快照是一個普通的引用兩個維度的事實表。月份維度表包含以月爲粒度的銷售週期描述符。產品代理鍵對應有效的產品維度行,也就是給定報告月的最後一天對應的產品代理鍵,以保證月末報表是對當前產品信息的準確描述。快照中的事實包含每月的數字度量和計數,它們是可加的。該快照事實表使用ORC存儲格式。使用下面的腳本建立month_end_sales_order_fact表並裝載歷史數據。

use dw;     
create table month_end_sales_order_fact (    
    order_month_sk int comment '月份代理鍵',   
    product_sk int comment '產品代理鍵',   
    month_order_amount decimal(10,2) comment '月銷售金額',   
    month_order_quantity int comment '月銷售數量'  
)  
clustered by (order_month_sk) into 8 buckets        
stored as orc tblproperties ('transactional'='true');

-- 初始裝載
insert into month_end_sales_order_fact
select d.month_sk month_sk,  
       a.product_sk product_sk,  
       sum(order_amount) order_amount,  
       sum(order_quantity) order_quantity  
  from sales_order_fact a,  
       date_dim b,    
       month_dim d  
 where a.order_date_sk = b.date_sk             
   and b.month = d.month
   and b.year = d.year  
   and b.dt < '2020-11-01'   
 group by d.month_sk , a.product_sk;

2. 創建快照表數據裝載Kettle轉換

        建立了month_end_sales_order_fact表後,現在需要向表中裝載數據。實際裝載時,月銷售週期快照事實表的數據源是已有的銷售訂單事務事實表,而並沒有關聯產品維度表。之所以可以這樣做,是因爲總是先處理事務事實表,再處理週期快照事實表,並且事務事實表中的產品代理鍵就是當時有效的產品描述。這樣做還有一個好處是,不必要非在1號裝載上月的數據,這點在後面修改定時自動執行作業時會詳細說明。用於裝載月銷售訂單週期快照事實表的Kettle作業如圖9-2所示。

圖9-2 裝載月銷售訂單週期快照事實表的作業

        “設置年月變量”所調用的Kettle轉換如圖9-3所示。

圖9-3 設置年月變量的轉換

獲取系統信息步驟取得上月第一天,公式步驟用month和year函數獲得上月對應的月份與年份,設置環境變量步驟設置MONTH和YEAR兩個全局變量用於後面SQL作業項中的替換變量。        SQL作業項中的語句如下:

delete from dw.month_end_sales_order_fact
 where month_end_sales_order_fact.order_month_sk in
 (select month_sk
    from dw.month_dim
   where month = ${MONTH}
     and year = ${YEAR});

insert into dw.month_end_sales_order_fact
select b.month_sk, a.product_sk, sum(a.order_amount), sum(a.order_quantity)
  from dw.order_date_dim d 
  left join dw.sales_order_fact a on d.order_date_sk = a.order_date_sk
 inner join dw.month_dim b on b.month = d.month and b.year = d.year
   and b.month = ${MONTH}
   and b.year = ${YEAR}
 group by b.month_sk, a.product_sk;

        第一句刪除上月數據,實現冪等操作,第二句裝載上月銷售彙總數據。前面曾經提到過,週期快照表的外鍵密度是均勻的,因此這裏使用外連接關聯訂單日期維度和事務事實表。即使上個月沒有任何銷售記錄,週期快照中仍然會有一行記錄。在這種情況下,週期快照記錄中只有月份代理鍵,其它字段值爲NULL。嚴格地說產品維度表中應該增加如‘N/A’這樣一行表示沒有對應產品時的缺省值。

        可以在每個月給定的任何一天,銷售訂單事實表定期裝載完成後,執行圖9-2所示的作業,裝載上個月的銷售訂單彙總數據。爲此需要修改定期裝載Kettle作業,如圖9-4所示。

圖9-4 增加了週期快照裝載的作業

        在定期裝載作業中增加了“判斷日期”和“裝載週期快照表”兩個作業項。“判斷日期”是一個用來判斷當天的日期JavaScript作業項,腳本如下:

var d = new Date();
var n = d.getDate();
if( n==12) {true;} else {false;}

        當日期等於12時,JavaScript作業項返回true,執行其後的“裝載週期快照表”作業項,否則轉到成功節點結束作業。“裝載週期快照表”子作業調用圖9-2所示的作業,執行完成後轉到成功節點結束。很明顯,本例中“判斷日期”的作用就是控制在並且只在一個月當中的某一天執行週期快照表的數據裝載,其它日期不做這步操作。這裏的n==12只是爲了方便測試,因爲SQL中是以上個月的年月作爲過濾條件,所以換做當月中任何一天都可以。這個作業保證了每月彙總只有在某天定期裝載執行完後纔開始,並且每月只執行一次。

        週期快照粒度表示一種常規性的重複的度量或度量集合,比如每月報表。這類事實表通常包括一個單一日期列,表示一個週期。週期快照事實必須滿足粒度需求,僅描述適合於所定義週期的時間範圍的度量。週期快照是一種常見的事實表類型,其週期通常是天、周或月。

        週期快照具有與事務粒度事實表相似的裝載特性,插入數據的過程類似。傳統上,週期快照在適當的時期結束時被裝載,就像示例演示的那樣。還有常見的一種做法是,滾動式地添加週期快照記錄。在滿足以下兩個條件時,往往採用滾動式數據裝載:一是事務數據量非常大,以至於裝載一個月的快照需要很長時間;二是快照的度量是可加的。例如可以建立每日銷售週期快照,數據從事務事實表彙總而來,然後月快照數據從每日快照彙總。這樣能夠把一個大的查詢分散到每一天進行。

三、累計快照

        累積快照事實表用於定義業務過程開始、結束以及期間的可區分的里程碑事件。通常在此類事實表中針對過程中的關鍵步驟都包含日期外鍵,幷包含每個步驟的度量,這些度量的產生一般都會滯後於數據行的創建時間。累積快照事實表中的一行,對應某一具體業務的多個狀態。例如,當訂單產生時會插入一行。當該訂單的狀態改變時,累積事實錶行被訪問並修改。這種對累積快照事實錶行的一致性修改在三種類型的事實表中具有獨特性,對於前面介紹的兩類事實表只追加數據,不會對已經存在的行進行更新操作。除了日期外鍵與每個關鍵過程步驟關聯外,累積快照事實表中還可以包含其它維度和可選退化維度的外鍵。

        累積快照事實表在庫存、採購、銷售、電商等業務領域都有廣泛應用。比如在電商訂單裏面,下單的時候只有下單時間,但是在支付的時候,又會有支付時間,同理,還有發貨時間,完成時間等等。下面以銷售訂單數據倉庫爲例,討論累積快照事實表的實現。

        假設希望跟蹤以下五個銷售訂單里程碑:下訂單、分配庫房、打包、配送和收貨,分別用狀態N、A、P、S、R表示。這五個里程碑的日期及其各自的數量來自源數據庫的銷售訂單表。一個訂單完整的生命週期由五行數據描述:下訂單時生成一條銷售訂單記錄;訂單商品被分配到相應庫房時,新增一條記錄,存儲分配時間和分配數量;產品打包時新增一條記錄,存儲打包時間和數量;類似的,訂單配送和客戶收貨時也都分別新增一條記錄,保存各自的時間戳與數量。爲簡化示例,不考慮每種狀態出現多條記錄的情況(如一條訂單中的產品可能是在不同時間點分多次出庫),並且假設這五個里程碑是以嚴格的時間順序正向進行的。

        對訂單的每種狀態新增記錄只是處理這種場景的多種設計方案之一。如果里程碑的定義良好並且不會輕易改變,也可以考慮在源訂單事務表中新增每種狀態對應的數據列,例如,新增8列,保存每個狀態的時間戳和數量。新增列的好處是仍然能夠保證訂單號的唯一性,並保持相對較少的記錄數。但是這種方案還需要額外增加一個last_modified字段記錄訂單的最後修改時間,用於增量數據抽取。因爲每條訂單在狀態變更時都會被更新,所以訂單號字段已經不能作爲變化數據捕獲的比較依據。

1. 修改數據庫模式

        執行下面的腳本將源數據庫中銷售訂單事務表結構做相應改變,以處理五種不同的狀態。

-- mysql  
use source;    
-- 修改銷售訂單事務表    
alter table sales_order    
      change order_date status_date datetime, 
      add order_status varchar(1) after status_date, 
      change order_quantity quantity int;    
  
-- 刪除sales_order表的主鍵    
alter table sales_order change order_number order_number int not null;
alter table sales_order drop primary key;
   
-- 建立新的主鍵  
alter table sales_order add id int unsigned not null auto_increment primary key comment '主鍵' first;

        說明:

  • 將order_date字段改名爲status_date,因爲日期不再單純指訂單日期,而是指變爲某種狀態日期。
  • 將order_quantity字段改名爲quantity,因爲數量變爲某種狀態對應的數量。
  • 在status_date字段後增加order_status字段,存儲N、A、P、S、R等訂單狀態之一。它描述了status_date列對應的狀態值,例如,如果一條記錄的狀態爲N,則status_date列是下訂單的日期,如果狀態是R,status_date列是收貨日期。
  • 每種狀態都會有一條訂單記錄,這些記錄具有相同的訂單號,因此訂單號不能再作爲事務表的主鍵,需要刪除order_number字段上的自增屬性與主鍵約束。
  • 添加id自增字段作爲銷售訂單表的主鍵,它是表中的第一個字段。

        依據源數據庫事務表的結構,執行下面的腳本修改Hive中相應的過渡區表。

use rds;  
alter table sales_order change order_date status_date timestamp comment '狀態日期';  
alter table sales_order change order_quantity quantity int comment '數量';  
alter table sales_order add columns (order_status varchar(1) comment '訂單狀態');

        說明:

  • 將銷售訂單事實表中order_date和order_quantity字段的名稱修改爲與源表一致。
  • 增加訂單狀態字段。
  • rds.sales_order並沒有增加id列,原因有兩個:一是該列只作爲增量檢查列,不用在過渡表中存儲;二是不需要再重新導入已有數據。

        執行下面的腳本將數據倉庫中的事務事實表增加訂單狀態列、創建累積快照事實表並添加分區。

use dw;    

-- 增加訂單狀態列
alter table sales_order_fact add columns (order_status varchar(1) comment '訂單狀態'); 

-- 創建累積快照事實表
create table sales_order_fact_accumulate
(
  order_number int COMMENT '銷售訂單號', 
  customer_sk int COMMENT '客戶維度代理鍵', 
  product_sk int COMMENT '產品維度代理鍵', 
  order_date_sk int COMMENT '日期維度代理鍵', 
  order_amount decimal(10,2) COMMENT '銷售金額', 
  order_quantity int COMMENT '銷售數量', 
  request_delivery_date_sk int COMMENT '請求交付日期', 
  sales_order_attribute_sk int COMMENT '訂單屬性代理鍵', 
  customer_zip_code_sk int COMMENT '客戶郵編代理鍵', 
  shipping_zip_code_sk int COMMENT '送貨郵編代理鍵',
  allocate_date_sk int comment '分配日期代理鍵',  
  allocate_quantity int comment '分配數量',  
  packing_date_sk int comment '打包日期代理鍵',  
  packing_quantity int comment '打包數量',  
  ship_date_sk int comment '配送日期代理鍵',  
  ship_quantity int comment '配送數量',  
  receive_date_sk int comment '收貨日期代理鍵',  
  receive_quantity int comment '收貨數量'
)
partitioned by (flag string)
clustered by (order_number) into 8 buckets    
stored as orc tblproperties ('transactional'='true');

-- 創建分區
alter table sales_order_fact_accumulate add partition (flag='active') partition (flag='readonly');

        累積快照事實表在銷售訂單事實表基礎上新增加八個字段存儲後四個狀態的日期代理鍵和度量值,並且以flag字段作爲分區鍵劃分爲active與readonly兩個分區。累積事實表的數據裝載需要面對兩個挑戰:1. ETL過程處理儘量少的數據;2. 不使用DML(Data Manipulation Language,數據操縱語言,如insert、update、delete等)。針對前者,解決方案是將整個累積事實表分爲活動和只讀兩個分區,可以通過Hive的分區表實現。活動分區存儲沒有完成全部五個里程碑的訂單數據,反之,只讀分區存儲已經完成全部五個里程碑的完整訂單數據。所有的狀態更新操作都發生在活動分區,通常活動分區相對較小。

        在傳統關係數據庫中實現增量處理累積快照,需要行級更新,但Hive中無法這樣做。這裏存在兩個限制,一是Hive的update只能set常量,不支持多表更新和子查詢,這使得不能直接用sales_order_fact來更新sales_order_fact_accumulate。如果說第一個限制還能用臨時表勉強解決的話,那第二個限制則更加難於處理。我們之前多次指出,處於性能考慮,除週期快照外的事實表裝載都是用的“ORC output”步驟,而不用“表輸出”步驟,但這帶來的問題是再對ORC表執行行級更新操作數據會出現錯誤。解決這個問題所採取的以下處理流程能完全避免使用DML。

  1. 讀取活動分區中的所有數據,同時刪除活動分區。
  2. 從源系統中抽取變化的數據,和上一步讀取的活動分區中的所有數據合併。
  3. 把完整記錄加載到只讀分區,把不完整記錄加載到活動分區。

        使用Kettle實現時,可以將活動分區中的所有數據裝載到一個臨時表中,如sales_order_fact_accumulate_tmp。該表的結構除了沒有分區鍵字段flag以外,其它與sales_order_fact_accumulate相同(因此這裏沒有列出建表語句)。至於刪除活動分區,只需要用“ORC output”步驟輸出同名文件,以覆蓋原有文件即可實現。

2. 修改增量抽取銷售訂單表的Kettle轉換

        修改後的轉換如圖9-5所示,抽取的字段名稱要做相應修改。

圖9-5 增量抽取銷售訂單表的轉換

3. 修改定期裝載銷售訂單事實表的Kettle轉換

        “銷售訂單事務數據”數據庫連接步驟的SQL需要做三點修改:將查詢中的字段名a.order_quantity改爲a.quantity order_quantity;查詢中增加a.order_status字段;SQL中出現的所有order_date改名爲status_date。修改後的SQL如下:

select a.order_number, 
       b.customer_sk, 
       c.product_sk, 
       d.date_sk, 
       a.order_amount, 
       a.quantity order_quantity,
       e.date_sk request_delivery_date_sk,
       f.sales_order_attribute_sk,
       g.customer_zip_code_sk,
       h.shipping_zip_code_sk,
       a.order_status
  from rds.sales_order a,
       dw.customer_dim b,
       dw.product_dim c,
       dw.date_dim d,
       dw.date_dim e,
       dw.sales_order_attribute_dim f,
       dw.customer_zip_code_dim g,
       dw.shipping_zip_code_dim h,
       rds.customer i 
 where a.customer_number = b.customer_number
   and a.status_date >= b.effective_date and a.status_date < b.expiry_date
   and a.product_code = c.product_code
   and a.status_date >= c.effective_date and a.status_date < c.expiry_date
   and to_date(a.status_date) = d.dt
   and to_date(a.request_delivery_date) = e.dt
   and a.verification_ind = f.verification_ind 
   and a.credit_check_flag = f.credit_check_flag    
   and a.new_customer_ind = f.new_customer_ind    
   and a.web_order_flag = f.web_order_flag 
   and a.customer_number = i.customer_number
   and i.customer_zip_code = g.customer_zip_code
   and a.status_date >= g.effective_date and a.status_date < g.expiry_date
   and i.shipping_zip_code = h.shipping_zip_code
   and a.status_date >= h.effective_date and a.status_date < h.expiry_date
   and a.entry_date >= ? and a.entry_date < ?

        “ORC output”步驟的字段最後增加String類型的order_status。

4. 修改定期裝載Kettle作業

        在“裝載事實表”作業項後增加裝載累積快照事實表的子作業,如圖9-6所示。

圖9-6 增加裝載累積快照事實表的子作業

        子作業調用一個如圖9-7所示的Kettle作業裝載累積快照事實表。

圖9-7 裝載累積快照事實表的作業

        該作業順序調用兩個Kettle轉換。“讀取活動分區數據”轉換實現分區處理流程的第一步,“數據合併與分區”轉換實現分區處理流程的第二步和第三步。

        “讀取活動分區數據”轉換如圖9-8所示。

圖9-8 讀取活動分區的轉換

        轉換的“表輸入”步驟從累積事實表讀取活動分區的全部數據,SQL如下:

select order_number, 
       customer_sk, 
       product_sk, 
       order_date_sk, 
       order_amount, 
       order_quantity, 
       request_delivery_date_sk, 
       sales_order_attribute_sk, 
       customer_zip_code_sk, 
       shipping_zip_code_sk, 
       allocate_date_sk, 
       allocate_quantity, 
       packing_date_sk, 
       packing_quantity, 
       ship_date_sk, 
       ship_quantity, 
       receive_date_sk, 
       receive_quantity
  from dw.sales_order_fact_accumulate
 where flag='active'

        “ORC output”步驟輸出文件到sales_order_fact_accumulate_tmp表所對應的的HDFS目錄下:hdfs://nameservice1/user/hive/warehouse/dw.db/sales_order_fact_accumulate_tmp/sales_order_fact_accumulate_tmp。輸出的字段是與sales_order_fact_accumulate_tmp表所對應的18個字段。每次覆蓋原同名文件,因此不用另行刪除臨時表sales_order_fact_accumulate_tmp的數據。注意勾選該步驟中的“Overwrite existing output file”選項。

        “數據合併與分區”轉換如圖9-9所示。

圖9-9 數據合併與分區的轉換

        “排序合併”步驟以order_number字段排序,合併兩個數據集合,功能類似於SQL中的union。該步驟要求它所合併的數據集合具有完全相同的字段結構,並且已經按步驟中指定的字段排序,否則可能導致錯誤結果。

        第一個數據集合是銷售訂單事實表中的增量數據,通過“讀取時間窗口”、“查詢事實表增量數據”、“字段選擇”、“排序記錄”、“行轉列”五個步驟獲得。“讀取時間窗口”步驟從時間戳表查出需要處理的起止時間,SQL爲:

select last_load , current_load from rds.cdc_time

        “查詢事實表增量數據”步驟從銷售訂單事實表查詢增量數據,SQL爲:

select order_number, 
       customer_sk, 
       product_sk, 
       order_date_sk, 
       order_amount,     
       order_quantity, 
       request_delivery_date_sk, 
       sales_order_attribute_sk, 
       customer_zip_code_sk, 
       shipping_zip_code_sk, 
       order_status
  from dw.sales_order_fact t1, dw.date_dim t2
 where t1.order_date_sk = t2.date_sk and t2.dt >= ? and t2.dt < ?

其中參數爲前一步驟輸出的last_load和current_load字段。整個定期裝載作業中,裝載過渡區數據、裝載銷售訂單事實表、裝載累積快照事實表三個部分都查詢了時間戳表rds.cdc_time,以獲得增量處理的時間窗口。比較高效的做法是將查詢rds.cdc_time表的操作作爲前導步驟只查一次,並將起始時間賦予整個作業的全局變量,後續步驟都可以引用這些變量作爲增量判斷條件。這裏每次判斷增量都查詢一次rds.cdc_time表只是出於便於調試一個目的。“字段選擇”步驟只選擇dw.sales_order_fact表輸出的11個字段。“排序記錄”步驟按order_number字段排序,這既是“行轉列”步驟的要求,也是“排序合併”步驟的要求。“行轉列”步驟的設置如圖9-10所示。

圖9-10 行轉列步驟

該步驟按order_number字段進行分組,將一組中order_status具有不同值的行轉爲固定的10列,缺失狀態的列值爲空。步驟輸出爲累積訂單表對應的18個字段。

        要合併的第二個數據集合爲當前活動分區的數據,由“查詢活動分區數據”表輸入步驟和“排序記錄 2”步驟獲得。表輸入步驟中的SQL如下:

select order_number,
       customer_sk,
       product_sk,
       order_amount,
       request_delivery_date_sk,
       sales_order_attribute_sk,
       customer_zip_code_sk,
       shipping_zip_code_sk,
       order_date_sk,
       order_quantity,
       allocate_date_sk,
       allocate_quantity,
       packing_date_sk,
       packing_quantity,
       ship_date_sk,
       ship_quantity,
       receive_date_sk,
       receive_quantity
  from dw.sales_order_fact_accumulate_tmp

sales_order_fact_accumulate_tmp表數據已由前面的“讀取活動分區數據”轉換裝載。“排序記錄 2”按order_number字段排序。

        兩個數據集合在合併後進行分組,實現將同一訂單號的多行轉爲一行。“分組”步驟中的分組字段爲前8個字段,聚合字段爲後10個字段,聚合類型選擇“最大”。聚合字段的值只有NULL和整數兩種可能,按照比較規則整數大,因此選最大值。“過濾記錄”步驟判斷receive_date_sk字段的值是否爲空,若是則輸出到活動分區,否則輸出到只讀分區。因爲假設五個里程碑只能按順序進行,依據最後一個的日期代理鍵是否有值就可區分訂單是否完整。最後兩個“ORC output”步驟生成累積事實表中兩個分區所對應的HDFS文件。活動分區對應的Folder/File name屬性如下:hdfs://nameservice1/user/hive/warehouse/dw.db/sales_order_fact_accumulate/flag=active/all,每次執行將覆蓋已有文件,注意勾選“Overwrite existing output file”選項。只讀分區對應的文件爲:hdfs://nameservice1/user/hive/warehouse/dw.db/sales_order_fact_accumulate/flag=readonly/${PRE_DATE}。${PRE_DATE}變量值是前一天的日期,以它作爲文件名的目的是追加數據,而不是全量覆蓋。只讀分區存儲已完成訂單數據,不存在更新問題,因此採取增量處理。

5. 測試

        可以按照以下步驟進行累積快照事實表的數據裝載測試。
(1)在源數據庫的銷售訂單事務表中新增兩個銷售訂單記錄。

use source;
insert into sales_order values
(143, 143, 1, 1, 'n', 'n', 'n' ,'n', '2020-11-02 11:11:11', 'N', '2020-11-10', '2020-11-02 11:11:11', 7500, 75), 
(144, 144, 2, 2, 'y', 'y', 'y', 'y', '2020-11-02 12:12:12', 'N', '2020-11-10', '2020-11-02 12:12:12', 1000, 10) ;
commit ;

(2)設置適當的cdc_time時間窗口。

update rds.cdc_time set last_load='2020-11-02';

(3)執行圖9-6所示的定期裝載作業。

(4)修改生成的HDFS文件名,避免後面再次執行作業時覆蓋已裝載數據。

hdfs dfs -mv /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-16.txt /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-02.txt
hdfs dfs -mv /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-16 /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-02

(5)    查詢sales_order_fact_accumulate表,確認定期裝載成功。此時應該只有訂單日期代理鍵列有值,其它狀態的日期值都是NULL,因爲這兩個訂單是新增的,並且還沒有分配庫房、打包、配送或收貨。

select order_date_sk,order_quantity,
       allocate_date_sk,allocate_quantity,
       packing_date_sk,packing_quantity,
       ship_date_sk,ship_quantity,
       receive_date_sk,receive_quantity,
       flag
  from dw.sales_order_fact_accumulate;

(6)在源數據庫中插入數據作爲這兩個新增訂單的分配庫房和打包里程碑。

use source;
insert into sales_order values
(145, 143, 1, 1, 'n', 'n', 'n' ,'n', '2020-11-03 11:11:11', 'A', '2020-11-10', '2020-11-03 11:11:11', 7500, 75), 
(146, 143, 1, 1, 'n', 'n', 'n' ,'n', '2020-11-03 11:11:11', 'P', '2020-11-10', '2020-11-03 11:11:11', 7500, 75), 
(147, 144, 2, 2, 'y', 'y', 'y', 'y', '2020-11-03 12:12:12', 'A', '2020-11-10', '2020-11-03 12:12:12', 1000, 10) ;
commit ;

(7)設置適當的cdc_time時間窗口。

update rds.cdc_time set last_load='2020-11-03';

(8)執行定期裝載作業。

(9)修改生成的HDFS文件名。

hdfs dfs -mv /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-16.txt /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-03.txt
hdfs dfs -mv /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-16 /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-03

(10)查詢sales_order_fact_accumulate表,確認定期裝載成功。此時訂單應該具有了分配庫房或打包的日期代理鍵和度量值。

(11)在源數據庫中插入數據作爲這兩個訂單後面的里程碑:打包、配送和收貨。注意四個狀態日期可能相同。

use source;
insert into sales_order values
(148, 143, 1, 1, 'n', 'n', 'n' ,'n', '2020-11-04 11:11:11', 'S', '2020-11-10', '2020-11-04 11:11:11', 7500, 75), 
(149, 143, 1, 1, 'n', 'n', 'n' ,'n', '2020-11-04 11:11:11', 'R', '2020-11-10', '2020-11-04 11:11:11', 7500, 75), 
(150, 144, 2, 2, 'y', 'y', 'y', 'y', '2020-11-04 12:12:12', 'P', '2020-11-10', '2020-11-04 12:12:12', 1000, 10) ;
commit ;

(12)設置適當的cdc_time時間窗口。

update rds.cdc_time set last_load='2020-11-04';

(13)執行定期裝載作業。

(14)修改生成的HDFS文件名。

hdfs dfs -mv /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-16.txt /user/hive/warehouse/rds.db/sales_order/sales_order_2020-11-04.txt
hdfs dfs -mv /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-16 /user/hive/warehouse/dw.db/sales_order_fact/sales_order_fact_2020-11-04

(15)查詢sales_order_fact_accumulate表,確認定期裝載成功。此時訂單應該具有了所有五個狀態的日期代理鍵和度量值。

        累積快照粒度表示一個有明確開始和結束過程的當前發展狀態。通常這些過程持續時間較短,並且狀態之間沒有固定的時間間隔,因此無法將它歸類到週期快照中。訂單處理是一種典型的累積快照示例。累積快照的設計和管理與其它兩類事實表存在較大的差異。所有累積快照事實表都包含一系列日期,用於描述典型的處理工作流。

        例如銷售訂單示例包含訂單日期、分配庫房日期、打包日期、配送日期以及收貨日期等,這5個不同的日期以5個不同日期值代理鍵的外鍵出現。訂單行首次建立時只有訂單日期,因爲其它的狀態都還沒有發生。當訂單在其流水線上執行時,同一個事實行被順序訪問。每當訂單狀態發生改變時,累積快照事實行就被修改。日期外鍵被重寫,各類度量被更新。通常初始的訂單生成日期不會更新,因爲它描述的是行被建立的時間,但是所有其它日期都可以被重寫。

        通常利用三種事實表類型來滿足各種需要。細節數據可以被保存到事務粒度事實表中,週期歷史可以通過週期快照獲取,而對於具有多個定義良好里程碑的處理工作流,則可以使用累積快照。

四、無事實的事實表

        在多維數據倉庫建模中,有一種事實表叫做“無事實的事實表”。普通事實表中,通常會保存若干維度外鍵和多個數字型度量,度量是事實表的關鍵所在。然而在無事實的事實表中沒有這些度量值,只有多個維度外鍵。表面上看,無事實事實表是沒有意義的,因爲作爲事實表,畢竟最重要的就是度量。但在數據倉庫中,這類事實表有其特殊用途。無事實的事實表通常用來跟蹤某種事件或者說明某些活動的範圍。

        無事實的事實表可以用來跟蹤事件的發生。例如,在給定的某一天中發生的學生參加課程的事件,可能沒有可記錄的數字化事實,但該事實行帶有一個包含日期、學生、教師、地點、課程等定義良好的外鍵。利用無事實的事實表可以按各種維度計數上課這個事件。再比如學生註冊事件,學校需要對學生按學期進行跟蹤。維度表包括學期維度、課程維度、系維度、學生維度、註冊專業維度和取得學分維度等,而事實表由這些維度的主鍵組成,事實只有註冊數,並且恆爲1,因此沒有必要用單獨一列來表示。這樣的事實表主要用於回答各種情況下的註冊數。

        無事實的事實表還可以用來說明某些活動的範圍,常被用於回答“什麼未發生”這樣的問題,例如促銷範圍事實表。通常銷售事實表可以回答促銷商品的銷售情況,可是無法回答的一個重要問題是:處於促銷狀態但尚未銷售的產品包括哪些?銷售事實表所記錄的僅僅是實際賣出的產品。事實錶行中不包括由於沒有銷售行爲而銷售數量爲零的行,因爲如果將包含零值的產品都加到事實表中,那麼事實表將變得非常巨大。這時,通過建立促銷範圍事實表,將商場需要促銷的商品單獨建立事實表保存,然後通過這個促銷範圍事實表和銷售事實表即可得出哪些促銷商品沒有銷售出去。

        爲確定當前促銷的產品中哪些尚未賣出,需要兩步過程:首先,查詢促銷無事實的事實表,確定給定時間內促銷的產品。然後從銷售事實表中確定哪些產品已經賣出去了。答案就是上述兩個列表的差集。這樣的促銷範圍事實表只是用來說明促銷活動的範圍,其中沒有任何事實度量。可能有讀者會想,建立一個單獨的促銷商品維度表能否可以達到同樣的效果呢?促銷無事實的事實表包含多個維度的主鍵,可以是日期、產品、商店、促銷等,將這些鍵作爲促銷商品的屬性是不合適的,因爲每個維度都有自己的屬性集合。

        促銷無事實事實表看起來與銷售事實表相似,然而它們的粒度存在顯著差別。假設促銷是以一週爲持續期,在促銷範圍事實表中,將爲每週每個商店中促銷的產品加載一行,無論產品是否賣出。該事實表能夠確保看到被促銷定義的鍵之間的關係,而與其它事件,如產品銷售無關。

        下面以銷售訂單數據倉庫爲例,說明如何處理源數據中沒有度量的需求。我們將建立一個無事實的事實表,用來統計每天發佈的新產品數量。產品源數據不包含產品數量信息,如果系統需要得到歷史某一天新增產品的數量,很顯然不能簡單地從數據倉庫中得到。這時就要用到無事實的事實表技術。使用此技術可以通過持續跟蹤產品發佈事件來計算產品的數量。可以創建一個只有產品(計什麼數)和日期(什麼時候計數)維度代理鍵的事實表。之所以叫做無事實的事實表是因爲表本身並沒有數字型度量值。這裏定義的新增產品是指在某一給定日期,源產品表中新插入的產品記錄,不包括由於SCD2新增的產品版本記錄。

1. 建立新產品發佈的無事實事實表

        在數據倉庫模式中新建一個產品發佈的無事實事實表product_count_fact,該表中只包含兩個字段,分別是引用日期維度表和產品維度表的外鍵,同時這兩個字段也構成了無事實事實表的邏輯主鍵。圖9-11顯示了跟蹤產品發佈數量的數據倉庫模式(只顯示與無事實事實表相關的表)。

圖9-11 無事實的事實表

        執行下面的腳本在數據倉庫模式中創建產品發佈無事實事實表。

use dw;    
create table product_count_fact (    
    product_sk int,    
    product_launch_date_sk int)
row format delimited fields terminated by ','
stored as textfile;

從字段定義看,產品維度表中的生效日期明顯就是新產品的發佈日期。本示例中無事實事實表的數據裝載沒有行級更新需求,所以該表使用CSV文本存儲格式。

2. 初始裝載無事實事實表

        創建如圖9-12所示的Kettle轉換,向無事實事實表裝載已有的產品發佈日期代理鍵。

圖9-12 初始裝載無事實事實表的轉換

“日期維度”表輸入步驟中的SQL查詢日期維度表的代理鍵和日期值:

select date_sk, dt from dw.date_dim

“產品維度”表輸入步驟中的SQL查詢產品維度表的代理鍵和產品首次發佈日期:

select product_sk, effective_date from dw.product_dim where version=1

“流查詢”步驟的設置如圖9-13所示。

圖9-13 流查詢步驟

        該步驟從“產品維度”獲得產品首次發佈日期,在“日期維度”步驟中尋找匹配的行,從而將date_sk字段從“日期維度”步驟傳遞到“流查詢”步驟的輸出流中。本例中因爲每個產品發佈日期在日期維度表中都能找到,每次查詢都會成功,所以不需要設置date_sk的默認值。現實場景中可能要查詢的數據在查找表中沒有。爲處理這種情況,建議使用一個容易識別的默認值。

        最後的“Hadoop file output”步驟將輸出流數據生成無事實事實表所對應的HDFS文件:/user/hive/warehouse/dw.db/product_count_fact/product_count_fact,字段爲product_sk和date_sk。內容標籤頁中,分隔符爲逗號,格式選擇LF terminated (Unix),編碼選擇UTF-8,其它屬性爲空。

        轉換中使用的流查詢步驟支持從各種數據源和其它步驟查詢數據,但只允許做等值查詢。在一些場景下,如維度數據和事實數據能同時準備好,先使用“表輸入”步驟獲取每個業務鍵最後一個版本的維度數據,然後再用“流查詢”步驟把“表輸入”步驟的結果作爲輸入,是查詢大型維度表的最快方式。

        這種方法速度快,是因爲查詢集裏只包括了實際需要的記錄。若客戶維度包括了三百萬行記錄(包括了歷史記錄),當前最新版本的數據可能只有總數的1/3(這是很普遍的情況),所以只要用流查詢步驟在一百萬行數據中查找就可以。即使是同樣多的數據行,使用流查詢步驟也快一些。

        與流複製相關的有一種不太常見但是跟重要的情況:一個步驟可以導致整個轉換被掛起,如圖9-14所示的例子。

圖9-14 一個會被掛起的轉換

        這個轉換要把一個客戶的消費合計數追加到客戶記錄裏,可能是用於統計每個客戶的消費百分比。該轉換的問題是從Customers步驟出來的數據既用於主數據流,也用於流查詢步驟使用的查詢數據流。在這種情況下,流查詢步驟會在全部接收完查詢數據流裏的所有數據後纔開始進行查詢,如果查詢數據流裏的數據沒有結束,流查詢步驟就會一直讀取。在轉換開始後,流查詢步驟會阻塞主數據流,一直接收查詢數據流裏的數據,等待查詢數據流裏的數據直到數據結束。而此時,流查詢步驟和Customers步驟之間的緩存會被填滿,導致Customers步驟不能再繼續發送數據給查詢數據流。這樣整個轉換就進入了死鎖狀態,兩個或多個步驟在互相等待。

        可以把轉換改成圖9-15的樣子,讓轉換讀取數據源兩次,避免循環引用。也可以把一個轉換分成兩個轉換,將查詢數據保存在臨時文件或數據庫表中。

圖9-15 通過讀兩次數據源解決死鎖問題

3. 修改定期裝載Kettle作業

        只需要將初始裝載產品數量事實表的轉換合併到定期裝載Kettle作業中,如圖9-16所示。

圖9-16 增加裝載產品數量事實表的轉換

注意需要加在裝載維度表作業項之後,因爲需要使用最新的產品維度表數據。轉換要做兩點修改,一是隻查詢新發布的產品,因此“產品維度”步驟中的SQL改爲:

select product_sk, effective_date from dw.product_dim where version=1 and effective_date = '${PRE_DATE}'

並且勾選“替換sql語句中的變量”屬性。二是“Hadoop file output”步驟生成的文件名中添加${PRE_DATE}變量以實現增量裝載。

4. 測試定期裝載作業

(1)修改源數據庫的產品表數據。可以做兩點修改:新增一個產品;更改一個已有產品的名稱。

-- mysql
use source;
update product set product_name = 'Regular Hard Disk Drive' where product_code=1;
insert into product values (5, 'High End Hard Disk Drive', 'Storage');
commit;

(2)執行定期裝載作業。

(3)上一步執行成功後,查詢產品發佈無事實事實表,確認定期裝載執行正確。此時的結果應該只是增加了一條新產品記錄,原有數據沒有變化。

        無事實事實表是沒有任何度量的事實表,它本質上是一組維度的交集。用這種事實表記錄相關維度之間存在多對多關係,但是關係上沒有數字或者文本的事實。無事實事實表爲數據倉庫設計提供了更多的靈活性。再次考慮學生上課的應用場景,使用一個由學生、時間、課程三個維度鍵組成的無事實事實表,可以很容易地回答如下問題:

  • 有多少學生在某天上了給定的一門課程?
  • 在某段時間裏,一名給定學生每天所上課程的平均數是多少?

五、遲到的事實

        數據倉庫通常建立於一種理想的假設情況下,這就是數據倉庫的度量(事實記錄)與度量的環境(維度記錄)同時出現在數據倉庫中。當同時擁有事實記錄和正確的當前維度行時,就能夠從容地首先維護維度鍵,然後在對應的事實錶行中使用這些最新的鍵。然而,各種各樣的原因會導致需要ETL系統處理遲到的事實數據。例如,某些線下的業務,數據進入操作型系統的時間會滯後於事務發生的時間。再或者出現某些極端情況,如源數據庫系統出現故障,直到恢復後才能補上故障期間產生的數據。

        在銷售訂單示例中,晚於訂單日期進入源數據的銷售訂單可以看做是一個遲到事實的例子。銷售訂單數據被裝載進其對應的事實表時,裝載日期晚於銷售訂單產生的日期,因此是一個遲到的事實。本例中因爲定期裝載的是前一天的數據,所以這裏的“晚於”指的是事務數據延遲兩天及其以上纔到達ETL系統。

        必須對標準的ETL過程進行特殊修改以處理遲到的事實。首先,當遲到度量事件出現時,不得不反向搜索維度表歷史記錄,以確定事務發生時間點的有效的維度代理鍵,因爲當前的維度內容無法匹配輸入行的情況。此外,還需要調整後續事實行中的所有半可加度量,例如由於遲到的事實導致客戶當前餘額的改變。遲到事實可能還會引起週期快照事實表的數據更新。前面第二節討論的月銷售週期快照表,如果2020年10月的銷售訂單金額已經計算並存儲在month_end_sales_order_fact快照表中,這時一個遲到的10月訂單在11月某天被裝載,那麼2020年10月的快照金額必須因遲到事實而重新計算。

        下面就以銷售訂單數據倉庫爲例,說明如何處理遲到的事實。

1. 修改數據倉庫模式

        回憶一下第二節中建立的月銷售週期快照表,其數據源自已經處理過的銷售訂單事務事實表。爲了確定事實表中的一條銷售訂單記錄是否是遲到的,需要把源數據中的登記日期列裝載進銷售訂單事實表。爲此在要銷售訂單事實表上添加登記日期代理鍵列。累積快照事實表數據由事務事實錶行轉列而來,因此也要增加登記日期代理鍵列。

        執行下面的腳本在銷售訂單事實表和累積快照事實表裏添加名爲entry_date_sk的日期代理鍵列。對於銷售訂單事實表,新增列會加到表的最後一列。對於累積快照事實表,由於它是分區表,新增列會加到分區列flag之前,其它已存在的普通列之後。

use dw;
alter table sales_order_fact add columns (entry_date_sk int comment '登記日期代理鍵'); 
alter table sales_order_fact_accumulate add columns (entry_date_sk int comment '登記日期代理鍵'); 
alter table sales_order_fact_accumulate_tmp add columns (entry_date_sk int comment '登記日期代理鍵');

2. 修改定期裝載Kettle轉換

        在給事實表添加了登記日期代理鍵列以後,需要修改數據倉庫定期裝載轉換來裝載登記日期。定期裝載銷售訂單事實錶轉換中數據庫連接步驟的SQL修改如下:

select a.order_number, 
       b.customer_sk, 
       c.product_sk, 
       d.date_sk, 
       a.order_amount, 
       a.quantity order_quantity,
       e.date_sk request_delivery_date_sk,
       f.sales_order_attribute_sk,
       g.customer_zip_code_sk,
       h.shipping_zip_code_sk,
       a.order_status,
       j.date_sk entry_date_sk
  from rds.sales_order a,
       dw.customer_dim b,
       dw.product_dim c,
       dw.date_dim d,
       dw.date_dim e,
       dw.sales_order_attribute_dim f,
       dw.customer_zip_code_dim g,
       dw.shipping_zip_code_dim h,
       rds.customer i,
       dw.date_dim j
 where a.customer_number = b.customer_number
   and a.status_date >= b.effective_date and a.status_date < b.expiry_date
   and a.product_code = c.product_code
   and a.status_date >= c.effective_date and a.status_date < c.expiry_date
   and to_date(a.status_date) = d.dt
   and to_date(a.request_delivery_date) = e.dt
   and a.verification_ind = f.verification_ind 
   and a.credit_check_flag = f.credit_check_flag    
   and a.new_customer_ind = f.new_customer_ind    
   and a.web_order_flag = f.web_order_flag 
   and a.customer_number = i.customer_number
   and i.customer_zip_code = g.customer_zip_code
   and a.status_date >= g.effective_date and a.status_date < g.expiry_date
   and i.shipping_zip_code = h.shipping_zip_code
   and a.status_date >= h.effective_date and a.status_date < h.expiry_date
   and a.entry_date >= ? and a.entry_date < ?
   and to_date(a.entry_date) = j.dt

增加了dw.date_dim j表別名、to_date(a.entry_date) = j.dt連接條件用於獲取登記日期代理鍵j.date_sk。注意sales_order源數據表及其對應的過渡表中都已經含有登記日期,只是以前沒有將其裝載進數據倉庫。相應的“ORC output”步驟的“Fields”中最後增加entry_date_sk字段。

        累積快照事實表的裝載同樣也要增加登記日期代理鍵字段。“讀取活動分區轉換”中,表輸入步驟的SQL改爲:

select order_number, 
       customer_sk, 
       product_sk, 
       order_date_sk, 
       order_amount, 
       order_quantity, 
       request_delivery_date_sk, 
       sales_order_attribute_sk, 
       customer_zip_code_sk, 
       shipping_zip_code_sk, 
       allocate_date_sk, 
       allocate_quantity, 
       packing_date_sk, 
       packing_quantity, 
       ship_date_sk, 
       ship_quantity, 
       receive_date_sk, 
       receive_quantity, 
       entry_date_sk
  from dw.sales_order_fact_accumulate
 where flag='active'

        “ORC output”步驟增加entry_date_sk字段。“數據合併與分區”轉換中的“查詢事實表增量數據”數據庫連接步驟、“字段選擇”步驟、“查詢活動分區數據”表輸入步驟、“分組”步驟中的構成分組的字段、“ORC output”和“ORC output 2”步驟均增加entry_date_sk字段。

        本節開頭曾經提到,需要爲遲到的事實行獲取事務發生時間點的有效的維度代理鍵。在SQL中使用銷售訂單過渡表的狀態日期字段限定當時的維度代理鍵。例如,爲了獲取事務發生時的客戶代理鍵,篩選條件爲:

status_date >= customer_dim.effective_date and status_date < customer_dim.expiry_date

        之所以可以這樣做,原因在於本示例滿足以下兩個前提條件:在最初源數據庫的銷售訂單表中,status_date存儲的是狀態發生時的時間;維度的生效時間與過期時間構成一條連續且不重疊的時間軸,任意status_date日期只能落到唯一的生效時間、過期時間區間內。

3. 修改裝載月銷售週期快照事實表的作業

        遲到的事實記錄會對週期快照中已經生成的月銷售彙總數據產生影響,因此必須做適當的修改。月銷售週期快照表存儲的是某月某產品彙總的銷售數量和銷售金額,表中有月份代理鍵、產品代理鍵、銷售金額、銷售數量四個字段。由於遲到事實的出現,需要將事務事實表中的數據劃分爲三類:非遲到的事實記錄;遲到的事實,但週期快照表中尚不存在相關記錄;遲到的事實,並且週期快照表中已經存在相關記錄。對這三類事實數據的處理邏輯各不相同,前兩類數據需要彙總後插入快照表,而第三種情況需要更新快照表中的現有數據。圖9-2所示裝載週期快照表作業中的SQL作業項腳本改爲:

use dw;

drop table if exists tmp;  
create table tmp as  
select a.order_month_sk order_month_sk,   
       a.product_sk product_sk,  
       a.month_order_amount + b.order_amount month_order_amount,  
       a.month_order_quantity + b.order_quantity month_order_quantity  
  from month_end_sales_order_fact a,  
       (select d.month_sk month_sk,  
               a.product_sk product_sk,  
               sum(order_amount) order_amount,  
               sum(order_quantity) order_quantity  
          from sales_order_fact a,  
               date_dim b,  
               date_dim c,  
               month_dim d  
         where a.order_date_sk = b.date_sk  
           and a.entry_date_sk <=> c.date_sk  
           and c.month = ${MONTH}   
           and c.year = ${YEAR}  
           and b.month = d.month
           and b.year = d.year     
           and b.dt <> c.dt  
         group by d.month_sk , a.product_sk) b  
 where a.product_sk = b.product_sk  
   and a.order_month_sk = b.month_sk; 
  
delete from month_end_sales_order_fact   
 where exists 
 (select 1   
    from tmp t2   
   where month_end_sales_order_fact.order_month_sk = t2.order_month_sk   
     and month_end_sales_order_fact.product_sk = t2.product_sk);
     
insert into month_end_sales_order_fact select * from tmp;  

insert into month_end_sales_order_fact
select d.month_sk, a.product_sk, sum(order_amount), sum(order_quantity)  
  from sales_order_fact a,    
       date_dim b,    
       date_dim c,    
       month_dim d  
 where a.order_date_sk = b.date_sk   
   and a.entry_date_sk <=> c.date_sk   
   and c.month = ${MONTH}   
   and c.year = ${YEAR}  
   and b.month = d.month  
   and b.year = d.year  
   and not exists (select 1   
                     from month_end_sales_order_fact p 
                    where p.order_month_sk = d.month_sk  
                      and p.product_sk = a.product_sk)
 group by d.month_sk, a.product_sk;

        按事務發生時間的先後順序,先處理第三種情況。爲了更新週期快照表數據,需要創建一個臨時表。子查詢用於從銷售訂單事實表中獲取所有上個月錄入的,並且是遲到的數據行的彙總。用b.dt <> c.dt作爲判斷遲到的條件。本示例中實際可以去掉這條判斷語句,因爲只有遲到事實會對已有的快照數據造成影響。外層查詢把具有相同產品代理鍵和月份代理鍵的遲到事實的彙總數據加到已有的快照數據行上。臨時表中存儲這個查詢的結果。注意產品代理鍵和月份代理鍵共同構成了週期快照表的邏輯主鍵,可以唯一標識一條記錄。之後使用先刪除再插入的方式更新週期快照表。從週期快照表刪除數據的操作也是以邏輯主鍵匹配作爲過濾條件。

        之後對第一、二類數據統一處理。使用相關子查詢獲取所有上個月新錄入的,並且在週期快照事實表中尚未存在的產品銷售月彙總數據,插入到週期快照表中。銷售訂單事實表的粒度是每天,而週期快照事實表的粒度是每月,因此必須使用訂單日期代理鍵對應的月份代理鍵進行比較。注意腳本中的a.entry_date_sk <=> c.date_sk,銷售訂單事實表中已有數據的entry_date_sk爲NULL,而對於含有NULL的等值比較使用<=>操作符。關於該操作符的比較規則詳見“https://wxy0327.blog.csdn.net/article/details/109570543#3.%20%E4%BF%AE%E6%94%B9%E5%AE%9A%E6%9C%9F%E8%A3%85%E8%BD%BD%E7%BB%B4%E5%BA%A6%E8%A1%A8%E7%9A%84%E8%BD%AC%E6%8D%A2”。        本示例中遲到事實對月週期快照表數據的影響邏輯並不是很複雜。當邏輯主鍵,即月份代理鍵和產品代理鍵的組合匹配時,將從銷售訂單事實表中獲取的銷售數量和銷售金額彙總值累加到月週期快照表對應的數據行上,否則將新的彙總數據添加到月週期快照表中。這個邏輯非常適合使用merge into語句,例如在Oracle中,month_sum.sql文件可以寫成如下的樣子:

declare
pre_month_date date;
month1 int;
year1 int;

begin
select add_months(sysdate,-1) into pre_month_date from dual;
select extract(month from pre_month_date), extract(year from pre_month_date) into month1, year1
  from dual;

 merge into month_end_sales_order_fact t1
 using (select d.month_sk month_sk,  
               a.product_sk product_sk,  
               sum(order_amount) order_amount,  
               sum(order_quantity) order_quantity  
          from sales_order_fact a,  
               order_date_dim b,  
               entry_date_dim c,  
               month_dim d  
         where a.order_date_sk = b.order_date_sk  
           and a.entry_date_sk = c.entry_date_sk  
           and c.month = month1   
           and c.year = year1  
           and b.month = d.month 
           and b.year = d.year      
         group by d.month_sk , a.product_sk) t2
    on (t1.order_month_sk = t2.month_sk and t1.product_sk = t2.product_sk)
  when matched then 
       update set t1.month_order_amount = t1.month_order_amount + t2.order_amount,
                 t1.month_order_quantity = t1.month_order_quantity + t2.order_quantity
  when not matched then 
       insert (order_month_sk, product_sk, month_order_amount, month_order_quantity)
       values (t2.month_sk, t2.product_sk, t2.order_amount, t2.order_quantity);

commit;

end;
/

        Hive從2.2版本開始支持merge into語句,而CDH 6.3中的Hive是2.1.1版本,並不支持merge into。

4. 測試

(1)把銷售訂單事實表的entry_date_sk字段修改爲order_date_sk字段的值。這些登記日期鍵是後面測試月快照數據裝載所需要的。
先創建一個臨時表存儲銷售訂單事實表全量數據。

use dw;
drop table if exists tmp;  
create table tmp as select * from sales_order_fact;

然後刪除銷售訂單事實表對應的HDFS目錄下的所有文件。

hdfs dfs -rm -skipTrash /user/hive/warehouse/dw.db/sales_order_fact/*

最後執行如圖9-17所示的轉換重新裝載銷售訂單事實表數據。

圖9-17 重新全量裝載銷售訂單事實表的轉換

(2)在執行定期裝載腳本前先查詢週期快照事實表和銷售訂單事實表。之後可以對比‘前’(不包含遲到事實)‘後’(包含了遲到事實)的數據,以確認裝載的正確性。

select year,
       month,
       product_name,
       month_order_amount amt,
       month_order_quantity qty
  from month_end_sales_order_fact a,
       month_dim b,
       product_dim c
 where a.order_month_sk = b.month_sk
   and a.product_sk = c.product_sk
 order by year , month , product_name;

結果:

...
2020    10    flat panel    45431.00    215
2020    10    floppy drive    14928.00    51
2020    10    hard disk drive    76179.00    248
2020    10    keyboard    40246.00    159
select product_name, sum(order_amount)
  from sales_order_fact a, product_dim b
 where a.product_sk = b.product_sk
 group by product_name
 order by product_name;

結果:

flat panel    55820.00
floppy drive    296566.00
hard disk drive    390421.00
keyboard    42357.00

(3)準備銷售訂單測試數據。可以在銷售訂單源數據表中插入三個新的訂單記錄,第一個是遲到的訂單,並且銷售的產品在週期快照表中已經存在,第二個也是遲到的訂單,但銷售的產品在週期快照表中不存在,第三個是非遲到的正常訂單。這裏需要注意,產品維度是SCD2處理的,所以在添加銷售訂單源數據時,新增訂單時間一定要在產品維度的生效與過期時間區間內。

use source;
insert into sales_order values
/* late arrival                                                       */
  (151, 145, 6, 2, 'y', 'y', 'y', 'n', '2020-10-25 01:01:01', 'N', '2020-10-30', '2020-11-22 01:01:01', 1000, 10),
  (152, 146, 6, 1, 'y', 'y', 'y', 'n', '2020-10-26 01:01:01', 'N', '2020-10-30', '2020-11-22 01:01:01', 1000, 10),
/* normal                                                             */
  (153, 147, 6, 5, 'y', 'n', 'y', 'n', '2020-11-22 01:01:01', 'N', '2020-11-30', '2020-11-22 01:01:01', 2000, 20);
 
commit;

(4)執行定期裝載作業。
        修改時間戳表,將last_load改爲前一天,然後執行定期裝載作業。

update rds.cdc_time set last_load='2020-11-22';

(5)設置Kettle所在服務器的系統日期爲下月1號 date -s "2020-12-01 `date +%T`" ,然後手工執行裝載週期快照表的Kettle作業。

(6)執行與第(2)步相同的查詢獲取包含了遲到事實的月底銷售彙總數據,對比‘前’‘後’查詢的結果,確認數據裝載正確。

select year,
       month,
       product_name,
       month_order_amount amt,
       month_order_quantity qty
  from month_end_sales_order_fact a,
       month_dim b,
       product_dim c
 where a.order_month_sk = b.month_sk
   and a.product_sk = c.product_sk
 order by year , month , product_name;

結果:

...
2020    10    flat panel    45431.00    215
2020    10    floppy drive    15928.00    61
2020    10    hard disk drive    77179.00    258
2020    10    keyboard    40246.00    159
2020    11    High End Hard Disk Drive    2000.00    20
2020    11    flat panel    10389.00    108
2020    11    floppy drive    9080.00    165
2020    11    hard disk drive    47860.00    520
2020    11    keyboard    2111.00    28
select product_name, sum(order_amount)
  from sales_order_fact a, product_dim b
 where a.product_sk = b.product_sk
 group by product_name
 order by product_name;

結果:

High End Hard Disk Drive    2000.00
flat panel    55820.00
floppy drive    297566.00
hard disk drive    391421.00
keyboard    42357.00

        從查詢結果看到,10月的快照數據由於遲到事實已經更新,11月快照正常裝載。測試後同步NTP服務器還原系統日期:

ntpdate 182.118.58.129

六、累積度量

        累積度量指的是聚合從序列內第一個元素到當前元素的數據,例如統計從每年的一月到當前月份的累積銷售額。本節說明如何在銷售訂單示例中實現累積月銷售數量和金額,並對數據倉庫模式、初始裝載、定期裝載Kettle作業和轉換做相應地修改。累積度量是半可加的,而且它的初始裝載比前面實現的要複雜。

1. 修改模式

        建立一個新的名爲month_end_balance_fact的事實表,用來存儲銷售訂單金額和數量的月累積值。month_end_balance_fact表在模式中構成了另一個星型模式。新的星型模式除了包括這個新的事實表,還包括兩個其它星型模式中已有的維度表,即產品維度表與月份維度表。圖9-18所示爲新的模式,這裏只顯示了相關的表。

圖9-18 累積度量

        下面的腳本用於創建month_end_balance_fact表。

use dw;    
create table month_end_balance_fact 
( month_sk int,    
  product_sk int,    
  month_end_amount_balance decimal(10,2),    
  month_end_quantity_balance int )
row format delimited fields terminated by ','
stored as textfile;

因爲對此事實表只有追加數據的操作操作,沒有update、delete等行級更新需求,所以這裏沒有用ORC文件格式,而是採用了CSV文本存儲格式。

2. 初始裝載

        現在要把month_end_sales_order_fact表裏的數據裝載進month_end_balance_fact表,圖9-19顯示了初始裝載month_end_balance_fact表的轉換。

圖9-19 初始裝載累積度量表

轉換中表輸入步驟的SQL如下:

select a.month_sk,    
       b.product_sk,    
       sum(b.month_order_amount) month_order_amount,    
       sum(b.month_order_quantity) month_order_quantity    
  from dw.month_dim a,    
       (select a.*,   
               b.year,   
               b.month,   
               max(a.order_month_sk) over () max_month_sk  
          from dw.month_end_sales_order_fact a, dw.month_dim b   
         where a.order_month_sk = b.month_sk) b  
 where a.month_sk <= b.max_month_sk 
   and a.year = b.year and b.month <= a.month  
 group by a.month_sk , b.product_sk

        此腳本查詢累積的月銷售訂單彙總數據,從每年的一月累積到當月,累積數據不跨年。子查詢獲取month_end_sales_order_fact表的數據,及其年月和最大月份代理鍵。外層查詢彙總每年一月到當月的累積銷售數據,a.month_sk <= b.max_month_sk條件用於限定只統計到現存的最大月份爲止。

        在關係數據庫中,出於性能方面的考慮,此類需求往往使用自連接查詢方法,而不用這種子查詢的方式。但是在Hive中,子查詢是唯一的選擇,原因有兩個:第一,Hive中兩個表join連接時,不支持關聯字段的非相等操作,而累積度量需求顯然需要類似<=的比較條件,當join中有非相等操作時,會報“Both left and right aliases encountered in JOIN ...”錯誤。第二,如果是內連接,我們可以將<=比較放到where子句中,避開Hive的限制。但是這不適合累積度量的場景。假設有產品1在一月有銷售,二月沒有銷售,那麼產品1在二月的累積銷售值應該從一月繼承。而如果使用內連接,用a.product_sk=b.product_sk做連接條件,會過濾掉產品1在二月的累積數據行,這顯然是不合理的。

        這裏也沒有使用Kettle裏的數據庫連接或流查詢步驟。如果使用數據庫連接步驟,對數據流中的每一行執行一次Hive查詢速度太慢。流查詢步驟又只支持等值連接,不適用於累積度量。所以只能在一個表輸入步驟中,利用SQL查詢執行所有邏輯。“Hadoop file output”步驟將查詢結果輸出到month_end_balance_fact表所對應的HDFS目錄。可在執行完轉換後分別查詢month_end_sales_order_fact和month_end_balance_fact表,確認初始裝載是否正確。

-- 週期快照
select b.year year,  
       b.month month,  
       a.product_sk psk,  
       a.month_order_amount amt,  
       a.month_order_quantity qty  
  from month_end_sales_order_fact a,  
       month_dim b  
 where a.order_month_sk = b.month_sk  
   and a.product_sk > 2
cluster by year, month, psk;

+-------+--------+------+-----------+------+
| year  | month  | psk  |    amt    | qty  |
+-------+--------+------+-----------+------+
| 2020  | 10     | 4    | 45431.00  | 215  |
| 2020  | 10     | 5    | 40246.00  | 159  |
| 2020  | 11     | 4    | 10389.00  | 108  |
| 2020  | 11     | 5    | 2111.00   | 28   |
| 2020  | 11     | 7    | 2000.00   | 20   |
+-------+--------+------+-----------+------+

-- 累積度量
select b.year year,  
       b.month month,  
       a.product_sk psk,  
       a.month_end_amount_balance amt,  
       a.month_end_quantity_balance qty  
  from month_end_balance_fact a,  
       month_dim b  
 where a.month_sk = b.month_sk  
   and a.product_sk > 2
cluster by year, month, psk;

+-------+--------+------+-----------+------+
| year  | month  | psk  |    amt    | qty  |
+-------+--------+------+-----------+------+
| 2020  | 10     | 4    | 45431.00  | 215  |
| 2020  | 10     | 5    | 40246.00  | 159  |
| 2020  | 11     | 4    | 55820.00  | 323  |
| 2020  | 11     | 5    | 42357.00  | 187  |
| 2020  | 11     | 7    | 2000.00   | 20   |
+-------+--------+------+-----------+------+

        可以看到,2020年10月的商品銷售金額和數量被累積到了2020年11月。產品4和5累加了10、11兩個月的銷售數據,產品7只有11月有銷售。

3. 定期裝載

        定期裝載轉換的步驟和初始裝載一樣,只需要做兩點修改。其一是表輸入步驟的SQL改爲:

select order_month_sk month_sk,    
       product_sk,    
       sum(month_order_amount) month_order_amount,    
       sum(month_order_quantity) month_order_quantity   
  from (select a.*    
          from dw.month_end_sales_order_fact a,   
               dw.month_dim b    
         where a.order_month_sk = b.month_sk    
           and b.year = ${YEAR}    
           and b.month = ${MONTH}  
         union all    
        select month_sk + 1 order_month_sk,  
               product_sk product_sk,  
               month_end_amount_balance month_order_amount,  
               month_end_quantity_balance month_order_quantity   
          from dw.month_end_balance_fact a    
         where a.month_sk in (select max(case when ${MONTH} = 1 then 0 else month_sk end)    
                                from dw.month_end_balance_fact)) t  
  group by order_month_sk, product_sk

        子查詢將累積度量表和月週期快照表做並集操作,增加上月的累積數據。最外層查詢執行銷售數據按月和產品的分組聚合。最內層的case語句用於在每年一月時重新歸零再累積。第二個修改點是將“Hadoop file output”步驟輸出的文件名中加上年月值:month_end_balance_fact_${YEAR}${MONTH}。

        該轉換每個月執行一次,裝載上個月的數據。可以在執行完月週期快照表定期裝載後執行該腳本,年月參數值由週期快照表裝載作業提供。修改後的定期裝載作業如圖9-20所示。

圖9-20 添加裝載累積度量的轉換

4. 測試定期裝載

        使用下面步驟測試非1月的裝載:
(1)執行下面的命令向month_end_sales_order_fact表添加兩條記錄。

insert into dw.month_end_sales_order_fact values (36,5,1000,10),(36,7,1000,10);

(2)將轉換中的${YEAR}、${MONTH}替換爲2020、12,手工執行累積度量定期裝載轉換。(3)查詢month_end_balance_fact表,確認累積度量數據裝載正確。

select b.year year,  
       b.month month,  
       a.product_sk psk,  
       a.month_end_amount_balance amt,  
       a.month_end_quantity_balance qty  
  from month_end_balance_fact a,  
       month_dim b  
 where a.month_sk = b.month_sk  
   and a.product_sk > 2
cluster by year, month, psk;

+-------+--------+------+-----------+------+
| year  | month  | psk  |    amt    | qty  |
+-------+--------+------+-----------+------+
| 2020  | 10     | 4    | 45431.00  | 215  |
| 2020  | 10     | 5    | 40246.00  | 159  |
| 2020  | 11     | 4    | 55820.00  | 323  |
| 2020  | 11     | 5    | 42357.00  | 187  |
| 2020  | 11     | 7    | 2000.00   | 20   |
| 2020  | 12     | 4    | 55820.00  | 323  |
| 2020  | 12     | 5    | 43357.00  | 197  |
| 2020  | 12     | 7    | 3000.00   | 30   |
+-------+--------+------+-----------+------+

        使用下面步驟測試1月的裝載:
(1)使用下面的命令向month_end_sales_order_fact表添加一條記錄,month_sk的值是37,指的是2021年1月。

insert into dw.month_end_sales_order_fact values (37,3,1000,10);

(2)使用下面的命令向month_end_balance_fact表添加一條記錄。

insert into dw.month_end_balance_fact values (36,3,1000,10);

(3)將轉換中的${YEAR}、${MONTH}替換爲2021、1,手工執行累積度量定期裝載轉換。(4)查詢month_end_balance_fact表,確認累積度量數據裝載正確。

select b.year year,  
       b.month month,  
       a.product_sk psk,  
       a.month_end_amount_balance amt,  
       a.month_end_quantity_balance qty  
  from month_end_balance_fact a,  
       month_dim b  
 where a.month_sk = b.month_sk  
   and a.product_sk > 2
cluster by year, month, psk;

+-------+--------+------+-----------+------+
| year  | month  | psk  |    amt    | qty  |
+-------+--------+------+-----------+------+
| 2020  | 10     | 4    | 45431.00  | 215  |
| 2020  | 10     | 5    | 40246.00  | 159  |
| 2020  | 11     | 4    | 55820.00  | 323  |
| 2020  | 11     | 5    | 42357.00  | 187  |
| 2020  | 11     | 7    | 2000.00   | 20   |
| 2020  | 12     | 3    | 1000.00   | 10   |
| 2020  | 12     | 4    | 55820.00  | 323  |
| 2020  | 12     | 5    | 43357.00  | 197  |
| 2020  | 12     | 7    | 3000.00   | 30   |
| 2021  | 1      | 3    | 1000.00   | 10   |
+-------+--------+------+-----------+------+

5. 查詢

        累積度量必須要小心使用,因爲它是“半可加”的。一個半可加度量在某些維度(通常是時間維度)上是不可加的。例如可以通過產品正確地累加月底累積銷售金額。

select year, month, sum(month_end_amount_balance) s  
  from month_end_balance_fact a,  
       month_dim b  
 where a.month_sk = b.month_sk  
 group by year, month  
cluster by year, month;

+-------+--------+------------+
| year  | month  |     s      |
+-------+--------+------------+
| 2020  | 3      | 98109.00   |
| 2020  | 4      | 149874.00  |
| 2020  | 5      | 239345.00  |
| 2020  | 6      | 382840.00  |
| 2020  | 7      | 470511.00  |
| 2020  | 8      | 528575.00  |
| 2020  | 9      | 538940.00  |
| 2020  | 10     | 717724.00  |
| 2020  | 11     | 789164.00  |
| 2020  | 12     | 792164.00  |
| 2021  | 1      | 1000.00    |
+-------+--------+------------+

而通過月份累加月底金額:

select product_name, sum(month_end_amount_balance) s  
  from month_end_balance_fact a,  
       product_dim b  
 where a.product_sk = b.product_sk  
 group by product_name; 

+---------------------------+-------------+
|       product_name        |      s      |
+---------------------------+-------------+
| High End Hard Disk Drive  | 5000.00     |
| flat panel                | 157071.00   |
| floppy drive              | 2112829.00  |
| hard disk drive           | 2305386.00  |
| keyboard                  | 125960.00   |
| lcd panel                 | 2000.00     |
+---------------------------+-------------+

以上查詢結果是錯誤的。正確的結果應該和下面的在month_end_sales_order_fact表上進行的查詢結果相同。

select product_name, sum(month_order_amount) s  
  from month_end_sales_order_fact a,  
       product_dim b  
 where a.product_sk = b.product_sk  
 group by product_name;

+---------------------------+------------+
|       product_name        |     s      |
+---------------------------+------------+
| High End Hard Disk Drive  | 3000.00    |
| flat panel                | 55820.00   |
| floppy drive              | 297566.00  |
| hard disk drive           | 391421.00  |
| keyboard                  | 43357.00   |
| lcd panel                 | 1000.00    |
+---------------------------+------------+

七、小結

  • 事務事實表、週期快照事實表和累積快照事實表是多維數據倉庫中常見的三種事實表。定期歷史數據可以通過週期快照獲取,細節數據被保存到事務粒度事實表中,而對於具有多個定義良好里程碑的處理工作流,則可以使用累積快照。
  • 無事實事實表是沒有任何度量的事實表,它本質上是一組維度的交集。用這種事實表記錄相關維度之間存在多對多關係,但是關係上沒有數字或者文本的事實。無事實事實表爲數據倉庫設計提供了更多的靈活性。
  • 遲到的事實指的是到達ETL系統的時間晚於事務發生時間的度量數據。必須對標準的ETL過程進行特殊修改以處理遲到的事實。需要確定事務發生時間點的有效的維度代理鍵,還要調整後續事實行中的所有半可加度量。此外,遲到事實可能還會引起週期快照事實表的數據更新。
  • 累積度量指的是聚合從序列內第一個元素到當前元素的數據。累積度量是半可加的,因此對累積度量執行聚合計算時要格外注意分組的維度。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章