使用Hive SQL創建日期維度表
一、需求
需要在Hive數倉中創建一張時間跨度為2010年-2025年的時間維度表,字段如下:
列 | 類型 | Comment | 計算邏輯(用於需求溝通) |
---|---|---|---|
date | string | 標准日期格式 | 2020-01-01 |
date_ds | string | ds日期格式 | 20200101 |
year | string | 年份 | 2020 |
month | string | 月份 | 01 |
day | string | 日期 | 01 |
day_of_week | bigint | 星期幾【1-7】 | |
week_of_year | bigint | 本年度第幾周 | 1. 以本年度第一個周一開始計算 2. 本年度前幾日如屬於上一年度的最后一周,則與上一年度最后一周的序號相同 |
week_of_month | bigint | 本月第幾周 | 與weekOfYear類似 |
二、代碼
SELECT
`date`,
date_ds,
year,
month,
day,
day_of_week,
weekofyear(`date`) as week_of_year,
--from_unixtime(unix_timestamp(`date`, "yyyy-MM-dd"), "W") as week_of_month
CAST((day(theMonday) - 1) / 7 + 1 AS BIGINT) as week_of_month
FROM (
SELECT
`date`,
regexp_replace(`date`, '-', '') as date_ds,
year(`date`) as year,
month(`date`) as month,
day(`date`) as day,
-- 請參看代碼拆析 2 date_sub(next_day(`date`, 'Mon'), 7) as theMonday,
if(datediff(next_day(`date`, 1), `date`) == 7, date_sub(`date`, 6), date_sub(next_day(`date`, 2), 7)) as theMonday,
-- 版本支持date_format,可以使用: date_format(`date`, 'u') as day_of_week
from_unixtime(unix_timestamp(`date`, "yyyy-MM-dd"), "u") as day_of_week
FROM (
SELECT date_add(`start_date`,pos) AS `date`
from (
SELECT `start_date`
FROM table_contains_startDate
) t
lateral view posexplode(split(repeat(", ", 9495), ",")) tf AS pos,val -- 9495為時間跨度
) dates
) dates_expanded
SORT BY `date`
;
-- 需要一張包含起始日期的table_contains_startDate表
代碼拆析
-- dates:獲取從start_date到往后n天的date字段數據
SELECT date_add(`start_date`,pos) AS `date`
from (
SELECT `start_date`
FROM table_contains_startDate
) t
lateral view posexplode(split(repeat(", ", 9495), ",")) tf AS pos,val --9495為時間跨度
由於筆者所使用的Hive版本子句中不允許沒有FROM
語句: FAILED: Error in semantic analysis: ERROR-0-HIVE:00003:{semantic error => sub query must have a from clause!!}}
,所以創建了一張只包含起始日期的表table_contains_startDate
,如果版本允許可以直接改為:
-- dates:獲取從start_date到往后n天的date字段數據
SELECT date_add(`start_date`,pos) AS `date`
from (
SELECT '2010-01-01'
) t
lateral view posexplode(split(repeat(", ", 9495), ",")) tf AS pos,val --9495為時間跨度
-- dates_expanded:
if(datediff(next_day(`date`, 1), `date`) == 7, date_sub(`date`, 6), date_sub(next_day(`date`, 2), 7)) as theMonday,
-- 標准Hive函數 date_sub(next_day(`date`, 'Mon'), 7) as theMonday,
--
CAST((day(theMonday) - 1) / 7 + 1 AS BIGINT) as week_of_month
由於week_of_month
字段邏輯與SimpleDateFormat
提供的W
參數邏輯不同,所以需要另謀他法。總體的思路是:取date
所在周的周一 theMonday
,使用theMonday
的 后兩位 減一的差 對7取商 再加一 即可(說起來很拗口,看代碼即可。該思路由 力丞 大佬特別贊助!)。
筆者公司next_day()
函數並非標准的Hive函數且有bug,使用標准Hive函數庫的同學使用注釋中的語句即可。
三、結果預覽
SQL結果插入維度表得到結果如下:
參考資料: