hive中進行增量統計-full join的用法-如何合併表中兩列

我將其他相關不必要的細節隱去,只保留最終的技術點,希望能對以後有需要的朋友提供些許幫助。
最近在進行hive表的統計的時候有這樣的一個需求,
hive中增量統計

一 、任務需求:
有這樣的兩個表 table0,table01,
table0爲原始數據表:

age num total pday
50 20 100 20190420
60 20 100 20190420
70 20 100 20190420
80 20 100 20190420

age爲年齡,50後 60後 70後 80後
num爲在20190420這一天新增的人數
total 爲截至到20190420這一天的所有人數

還有一張table1表,長這個樣子

age num pday
50 10 20190421
60 10 20190421
70 10 20190421
80 10 20190421

很容易理解,年齡爲50後的人 今天又新增了10人,年齡爲60後的人今天也新增了10人,以此類推
需求是,需要統計在4月21日的總人數,生成像table0那樣的表格,也就是以下這張表,我稱爲結果表 result_table

age num total pday
50 10 110 20190421
60 10 110 20190421
70 10 110 20190421
80 10 110 20190421

我們以50後爲例,表中num表示的是今日新增10人,總人數變成了昨天的100加上今天的10人,共110人。

sql語句如下,比較簡單

select 
	t0.age,
	t1.num,
	t1.num+t0.total as total
from
	(select age,num,total from table0 where pday=20190420)  as t0
join 
	(select  age ,num from table1 where pday=20190421)  as t1
on 
	t0.age=t1.age

二、出現的問題
1 很容易產生的問題如下:在未來的某一天,新增的人只有50後 60後,沒有80 90後,那table0與table1進行join的結果就只能有50後60後, 80和90的就會消失,大家想想是不是這個道理。如下表,就是在4月25日新增的人只有50和60的人,那麼80 90的數據就憑空消失了,這斷然不是我們想要的結果,我們想要的結果是,即使在20190425這一天沒有新增80 90後的人,那也應該繼承自上一天的數據,不應該因爲沒有join到就砍掉這部分人。
2 再比如,若未來某一天新增的人除了50 60 70 80 的人,還新增了90後的人,怎麼辦?新增的90後在table0中是查不到總人數的,那麼t0.total就是取不到數據的,理論上很容易想到,應該把今天新增的90後的人數作爲總人數,以下兩個表爲問題示意圖,沒有理解的朋友希望仔細思考一下。

總結起來就是 左右表不能完全join起來。該怎麼辦

age num total pday
50 30 140 20190422
60 30 140 20190422
age num total pday
50 30 140 20190422
60 30 140 20190425
70 30 140 20190425
80 30 140 20190425
90 20190425

三 解決方案
我們需求的本意是想要左表中,右表中age都要保留,無論是否能連接起來。所以此處應該使用full join。
在hive中是支持full join的,也就是左右表中年齡存在的,都應該保留起來,這顯然可以解決此問題。
可是直接將sql0中的語句中join變爲full join是不行的,還要考慮在左表中查不到數據,或者說在右表中查不到數據,該如何進行相加?
這時候問題就比較複雜了,我們重新回顧一下這兩張表
table0

age num total pday
50 20 100 20190420
60 20 100 20190420
70 20 100 20190420
80 20 100 20190420

table1

age num pday
60 10 20190421
70 10 20190421
80 10 20190421
90 10 20190421

請注意此數據和之前簡單版數據的區別:
table0表中含有50 60 70 80 ,table1表中含有60 70 80 90

首先我們來看看執行full join之後表是什麼樣子,sql:

select * from 
  table0
full join
  table1
on table0.age=table1.age
age num total pday age num pday
60 10 110 20190420 60 10 20190421
70 10 110 20190420 70 10 20190421
80 10 110 20190420 80 10 20190421
50 10 110 20190420
90 10 20190421

接下來就是將左右表中age和age進行組合 ,左右表中num 和num組合,total生成新的total

我們先理清邏輯,很明顯,左表中age爲null就要顯示右表中的age,左表中num爲null 就要使用右表中num
至於total比較複雜,
第一種情況,左表中有total,右表中有num,那生成的total 就是兩者相加
第二種 左表中有total,右表中無num,那新生成的total 就是原來的total
第三種 左表中無 total,右表中有num,那新生成的total 就是num
第四種 左表中無total,右邊中無num,那新生成的total 就是null 我們可以默認爲0(數量)
理清了邏輯就是看使用哪些方法來實現功能了。
首先
我們可以使用case when 函數
case when t0.age is null then t1.age else t0.age end
或者可以使用coalesce函數
coalesce(value1,value2 ,…)
value1爲null 就顯示value2的值,以此類推,具體使用方法在此就不說明了。
我們在此使用case when。
因爲total的處理比較複雜,使用coalesce滿足不了需求

下面貼出來我再hive裏進行測試的代碼(mysql不支持full join 我有沒有oracle數據庫 只能使用hive了 大哭)
1 創建表格

   create table table0(
    age int comment 'age',
    num int comment '數量',
    total int comment '總數'
    )
    partitioned by (pday int)
    row format delimited
    fields terminated by '\t'
    lines terminated by  '\n'
    stored as textfile;
------------------------
    create table table1(
    age int comment 'age',
    num int comment '數量'
    )
    partitioned by (pday int)
    row format delimited
    fields terminated by '\t'
    lines terminated by  '\n'
    stored as textfile;

2 準備文本文件
50 20 100 20190420
60 20 100 20190420
70 20 100 20190420
80 20 100 20190420

60 10 20190421
70 10 20190421
80 10 20190421
90 10 20190421
3 加載到hive表中

hive (default)> load data local inpath "/home/hadoop/myself_shell/test0" 
into table table0 partition(pday=20190420);
hive (default)> load data local inpath "/home/hadoop/myself_shell/test1" 
into table table1 partition(pday=20190421);

4 運行hive 執行sql語句,以下是完整的sql語句

select 
	case when t1.age is null then t0.age else t1.age end as age,
	case when t1.num is null then t0.num else t1.num end as num,
	case when t1.num is null and t0.total is null then 0
    	 when t1.num is null and t0.total is not null then t0.total 
		 when t1.num is not null and t0.total is null then t1.num
		 when t1.num is not null and t0.total is not null then t1.num+t0.total
	else 0
	end as total
from
	(select age,num,total from table0 where pday=20190420)  as t0
full join
	(select  age ,num from table1 where pday=20190421)  as t1
on 
	t0.age=t1.age

5 運行結果

age num total pday
50 20 100 20190421
60 10 110 20190421
70 10 110 20190421
80 10 110 20190421
90 10 10 20190421
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章