Hive join操作小結

本篇對Hive QL中join、left outer join、left semi join和full outer join等表連結操作作一簡要總結。


測試表準備

首先準備三張測試表,內容分別爲:

hql_jointest_a
idname
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h










create table if not exists hql_jointest_a (
	id bigint,
	name string
)
row format delimited
fields terminated by '\t'
lines terminated by '10'
stored as textfile;

hql_jointest_b
idscore
1 15
2 50
4 80
6 90
9 100
10 101
11 12
13 11









create table if not exists hql_jointest_b (
	id bigint,
	score bigint
)
row format delimited
fields terminated by '\t'
lines terminated by '10'
stored as textfile;


hql_jointest_c
idbook
1 數據結構
2 離散數學
3 經濟學
2 C語言基礎
3 離散數學
6 宏觀經濟學
2 邏輯學


create table if not exists hql_jointest_c (
	id bigint,
	book string
)
row format delimited
fields terminated by '\t'
lines terminated by '10'
stored as textfile;


爲使後面的論述不至於太生硬,我們給上述三張測試表的字段賦予一定的業務含義:id代表學生的學號,name是學生姓名,score是學生成績,book是學生擁有的書;在三張表中,id都是主鍵。第一張表表示某個班級(假設該班級爲CS-1)的學生名單,第二張表表示參加了某種考試(假設該考試爲T)的學生的成績,第三張表表示部分學生擁有的書。


join的用法

我們的第一個需求是,找出CS-1班中參加了T考試的學生姓名及其成績。該需求希望做到的是,取表a和表b中均存在的記錄,並使用主鍵id連結起來。代碼如下:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, b.id as id_b, b.score
from hql_jointest_a a
join hql_jointest_b b
on a.id = b.id;


輸出結果爲:





如果關聯的結果有重複記錄,那麼記錄會全部保留。爲了說明這一點,請先看下面一段代碼的輸出結果:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, c.id as id_c, c.book
from hql_jointest_a a
join hql_jointest_c c
on a.id = c.id;


這段代碼的業務場景可以描述爲:找出CS-1班中擁有書的同學姓名及其擁有的書名。輸出結果爲:







雖然id爲2的記錄有多條,但這些記錄彼此並不重複,表現爲最後一個字段book並不相同。

如果我們並不需要書名,只需要找出CS-1班中擁有書的同學姓名,那麼代碼如下:

set mapred.reduce.tasks = 1;
select a.id, a.name
from hql_jointest_a a
join hql_jointest_c c
on a.id = c.id;


這時輸出結果爲:







我們注意到,儘管書名book我們並沒有提取,但結果集還是保留了id爲2的所有三條記錄(當然,還有id爲3的兩條記錄)。在我們看來這些記錄是重複的、只需要保留1條就足夠的,這時可以使用distinct對關聯結果進行去重。

set mapred.reduce.tasks = 1;
select distinct a.id, a.name
from hql_jointest_a a
join hql_jointest_c c
on a.id = c.id;


輸出結果爲:





left outer join的用法

現在,需求方告訴我們,對於第一個需求,他們其實還想知道CS-1班中有哪些同學沒參加T考試(業務方的真實需求通常很坑爹難以捉摸,理解他們的真實意圖十分重要)。這時我們需要把CS-1班的所有學生記錄都取出來,同時,如果某學生參加T考試,則列出其分數。這裏需要以a爲左表,所取結果中應包含a的所有記錄。代碼如下:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, b.id as id_b, b.score
from hql_jointest_a a
left outer join hql_jointest_b b
on a.id = b.id;


輸出結果爲:







在結果集中,如果某學生沒有參加T考試,即其在b表中無相應記錄,那麼結果集的相應字段會被賦予NULL值。由此我們引出第一個需求中join的另外一種實現方式:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, b.id as id_b, b.score
from hql_jointest_a a
left outer join hql_jointest_b b
on a.id = b.id
where b.id is not null;


上述代碼中加上where b.id is not null,把在b表中值爲空的記錄剔除,實現的其實是傳統sql中的exists in操作。輸出結果爲:





現在,需求方又來麻煩你,把CS-1班中未參加T考試的學生取出來(——怎麼,你想做什麼?想處罰他們嗎?——不不,別誤會,只是想通知他們,這個考試必須參加。——哦,這樣。題外話:不能只是機械地被動地響應業務方需求,應該問清楚數據的應用場景,瞭解其想法後必要時予以指導。)。這個需求是,取a中存在但b中不存在的記錄。有了上面的闡述,我們很容易搞定代碼:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, b.id as id_b, b.score
from hql_jointest_a a
left outer join hql_jointest_b b
on a.id = b.id
where b.id is null;


以上代碼實現的其實就是傳統sql中的exists not in操作。輸出結果爲:





同樣的,在left outer join操作中,如果結果集出現重複記錄,可以使用distinct去重。


left semi join的用法

上一節我們其實已經變通地實現了exists in和exists not in操作。這裏我們使用Hive QL提供的另一種解決方案——left semi join來實現傳統sql的exists in操作。使用left semi join,有一個限制條件,即右表的字段只能出現在on子句中,而不能在select和where子句中引用。

回到第一個需求,找出CS-1班中參加T考試的學生。即取a中這樣的記錄,其id存在於b表中。

set mapred.reduce.tasks = 1;
select a.id, a.name
from hql_jointest_a a
left semi join hql_jointest_b b
on a.id = b.id;


輸出結果爲:





以下兩段代碼都是錯誤的:

set mapred.reduce.tasks = 1;
select a.id, a.name, b.id, b.score --b表出現在了select中
from hql_jointest_a a
left semi join hql_jointest_b b
on a.id = b.id;

set mapred.reduce.tasks = 1;
select a.id, a.name
from hql_jointest_a a
left semi join hql_jointest_b b
on a.id = b.id
where b.id >= 3;		--b表出現在了where子句中


full outer join的用法

full outer join可以實現全連結。現在,我們需要取出CS-1班的全體學生,或者參加T考試的學生及其成績。即取a中存在或b中存在的記錄。代碼如下:

set mapred.reduce.tasks = 1;
select a.id as id_a, a.name, b.id as id_b, b.score
from hql_jointest_a a
full outer join hql_jointest_b b
on a.id = b.id;


輸出結果爲:









補充說明

left outer join where is not null與left semi join的聯繫與區別:兩者均可實現exists in操作,不同的是,前者允許右表的字段在select或where子句中引用,而後者不允許。

除了left outer join,Hive QL中還有right outer join,其功能與前者相當,只不過左表和右表的角色剛好相反。

另外,Hive QL中沒有left join、right join、full join以及right semi join等操作。


發佈了26 篇原創文章 · 獲贊 27 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章