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班中拥有书的同学姓名及其拥有的书名。输出结果为:

Hive join操作小结_第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;


这时输出结果为:

Hive join操作小结_第2张图片






我们注意到,尽管书名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;


输出结果为:

Hive join操作小结_第3张图片





在结果集中,如果某学生没有参加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等操作。


你可能感兴趣的:(Hive join操作小结)