create table user
(
user_id bigint COMMENT '用户id'
,name STRING COMMENT '姓名'
,gender STRING COMMENT '性别'
) COMMENT '用户表';
create table class
(
class_id bigint COMMENT '班级id'
,class_name STRING COMMENT '班级名'
,user_id BIGINT COMMENT '用户id'
) COMMENT '班级表';
INSERT INTO user values(1,'李四','男');
INSERT INTO user values(2,'张三','男');
INSERT INTO user values(3,'翠花','女');
INSERT INTO user values(4,'武则天','女');
INSERT INTO class values(1001,'物理',1);
INSERT INTO class values(1001,'物理',3);
INSERT INTO class values(1002,'物理',2);
INSERT INTO class values(1003,'化学',null);
INSERT INTO class values(1003,'化学',5);
select * from user ;
user_id name gender
4 武则天 女
2 张三 男
3 翠花 女
1 李四 男
select * from class ;
class_id class_name user_id
1001 物理 1
1001 物理 3
1002 英语 2
1003 化学 \N
1003 化学 5
返回左表的全部数据,和右表中满足on条件的行,如果左表的行在右表中没有匹配的数据,那么这一行中右表对应的数据为null
--SQL
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
)
LEFT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)
ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
1 李四 男 1001 物理
2 张三 男 1002 英语
3 翠花 女 1001 物理
4 武则天 女 \N \N
HIVE中没有IN与NOT IN操作,可使用LEFT JOIN方式代替
(1) 获取【a在b中不存在】的数据
--获取【a在b中不存在】的数据
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
LEFT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id
where b.user_id is NULL ;
--结果
user_id name gender class_id class_name
4 武则天 女 \N \N
(2) 获取【a在b中存在】的数据
--获取【a在b中存在】的数据
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
LEFT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id
where b.user_id is NOT NULL ;
--结果
user_id name gender class_id class_name
1 李四 男 1001 物理
2 张三 男 1002 英语
3 翠花 女 1001 物理
返回左右表同时存在的数据,其实INNER JOIN 等价于上述获取【a在b中存在】数据的写法。
--SQL
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
INNER JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
1 李四 男 1001 物理
2 张三 男 1002 英语
3 翠花 女 1001 物理
返回右表中所有的行,和左表中满足on条件的行,如果右表的行在左表中没有匹配,那么这一行中左表的对应数据为NULL。
可以看作是LEFT JOIN的反面,一般用的也较少
--SQL1
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
RIGHT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
1 李四 男 1001 物理
3 翠花 女 1001 物理
2 张三 男 1002 英语
\N \N \N 1003 化学
\N \N \N 1003 化学
一般将主表的关联键保留下来
--SQL2
SELECT
b.user_id --保留主表的关联键
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
RIGHT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
1 李四 男 1001 物理
3 翠花 女 1001 物理
2 张三 男 1002 英语
\N \N \N 1003 化学
5 \N \N 1003 化学
会返回左表,右表所有的行,对应表中没有数据以NULL填充。
(1)SQL1及结果
--SQL1
SELECT a.*,b.*
FROM (
SELECT user_id
,name
,gender
FROM USER
) a
FULL JOIN (
SELECT class_id
,class_name
,user_id
FROM class
) b
ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name user_id2
2 张三 男 1002 英语 2
4 武则天 女 \N \N \N
3 翠花 女 1001 物理 3
1 李四 男 1001 物理 1
\N \N \N 1003 化学 5
\N \N \N 1003 化学 \N
(2)SQL2及结果
FULL JOIN
可以理解为
LEFT JOIN
UNION
RIGHT JOIN
--SQL2
SELECT
a.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
LEFT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id
UNION
SELECT
b.user_id
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT
user_id
,name
,gender
FROM USER
) a
RIGHT JOIN (
SELECT
class_id
,class_name
,user_id
FROM class
)b ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
\N \N \N 1003 化学
1 李四 男 1001 物理
2 张三 男 1002 英语
3 翠花 女 1001 物理
4 武则天 女 \N \N
5 \N \N 1003 化学
(3)在取两个表中的共同值的时候,可以使用coalesce
--SQL3
SELECT coalesce(a.user_id,b.user_id)
,a.name
,a.gender
,b.class_id
,b.class_name
FROM (
SELECT user_id
,name
,gender
FROM USER
) a
FULL JOIN (
SELECT class_id
,class_name
,user_id
FROM class
) b
ON a.user_id = b.user_id;
--结果
user_id name gender class_id class_name
2 张三 男 1002 英语
4 武则天 女 \N \N
3 翠花 女 1001 物理
1 李四 男 1001 物理
5 \N \N 1003 化学
\N \N \N 1003 化学
注意:以上这种FULL JOIN的使用,在JOIN的字段无NULL值的情况下使用,才能保证数据的准确性。
把表A和表B的数据进行一个N*M的组合,即笛卡尔积
即是4x5=20条记录
--SQL
SELECT
/*+MAPJOIN(b)*/a.*,b.*
FROM (
SELECT user_id
,name
,gender
FROM USER
) a
CROSS JOIN (
SELECT class_id
,class_name
,user_id
FROM class
) b;
--结果
user_id name gender class_id class_name user_id2
1 李四 男 1001 物理 1
1 李四 男 1001 物理 3
1 李四 男 1002 英语 2
1 李四 男 1003 化学 \N
1 李四 男 1003 化学 5
2 张三 男 1001 物理 1
2 张三 男 1001 物理 3
2 张三 男 1002 英语 2
2 张三 男 1003 化学 \N
2 张三 男 1003 化学 5
3 翠花 女 1001 物理 1
3 翠花 女 1001 物理 3
3 翠花 女 1002 英语 2
3 翠花 女 1003 化学 \N
3 翠花 女 1003 化学 5
4 武则天 女 1001 物理 1
4 武则天 女 1001 物理 3
4 武则天 女 1002 英语 2
4 武则天 女 1003 化学 \N
4 武则天 女 1003 化学 5
JOIN没有写ON条件,等同于CROSS JOIN
--SQL2
SELECT
/*+MAPJOIN(b)*/a.*,b.*
FROM (
SELECT user_id
,name
,gender
FROM USER
) a
JOIN (
SELECT class_id
,class_name
,user_id
FROM class
) b;
--结果
user_id name gender class_id class_name user_id2
1 李四 男 1001 物理 1
1 李四 男 1001 物理 3
1 李四 男 1002 英语 2
1 李四 男 1003 化学 \N
1 李四 男 1003 化学 5
2 张三 男 1001 物理 1
2 张三 男 1001 物理 3
2 张三 男 1002 英语 2
2 张三 男 1003 化学 \N
2 张三 男 1003 化学 5
3 翠花 女 1001 物理 1
3 翠花 女 1001 物理 3
3 翠花 女 1002 英语 2
3 翠花 女 1003 化学 \N
3 翠花 女 1003 化学 5
4 武则天 女 1001 物理 1
4 武则天 女 1001 物理 3
4 武则天 女 1002 英语 2
4 武则天 女 1003 化学 \N
4 武则天 女 1003 化学 5
参考文章
数据类型 | 判断空 | 备注 |
---|---|---|
STRING | is not null and !=‘’ | |
DOUBLE/BIGINT | is not null | 如果使用DOUBLE/BIGINT != ‘’,会出现返回\N,结果不正确。 |
例如:
select 8.0 != ''; --返回\N
select true and 8.0 != ''; --返回\N
例如:
在hive或者impala中,null值表示这个字段值为空值(没有值),一般情况下我们把数据从原始系统抽取到数仓ODS层的时候,都要做空值处理,比方说用nvl()函数把空值置为0或者空字符串等等,如果不处理,后续可能给开发带来坑。下面以一个具体的例子说明笔者在开发的时候,由于疏忽大意,没对空值进行处理,导致的问题。
具体就是null值在和任何值作比较的时候,都认为不成立。
比方说,有个student表,有个字段为选修科目,为string类型,正常情况,有些学生,有选修课,有些学生没有选选修课,所有这个字段存在null值。
现在我们要把选修课不是为管理学的数据筛选出来。
笔者的第一反应就是
select * from student where subject !='管理学';
或者
select * from student where subject not in ('管理学')
这种情况查询出来的结果会把选修科目为管理学的数据筛选掉,
但是同时也会把选修科目为NULL的数据筛选掉。原因是因为NULL值和任何值比较时默认认为不成立。
但是我们的本意是要保留这一部分数据的。
所以显然这种查询出来的结果是错的,这个时候我们就要对NULL值做特殊处理,比方说全部置为空字符串。或者置为0
select * from student where nvl(subject,'') !='管理学'
排序字段(以下a,b,c)为null会导致结果不正确
row_number() over(partition by a,b,c by gmtModified desc) as rk
group by字段(以下a,b,c)为null,会导致结果不正确
group by a ,b ,c
join字段为null会导致结果不正确
t1 left join t2 on t1.a=t2.a and t1.b=t2.b and t1.c=t2.c
经验:JOIN的字段不能有null,否则会导致结果不正确。
例如
select
* from a
left join b
on a.id=b.id
如果在主表a的id唯一情况下,副表b的id不唯一,就会导致数据膨胀(1条数据变多条)。
经验:在JOIN的时候最好保证JOIN的字段唯一,不然会出现数据膨胀,在计算聚合值的时候出现问题。
Hive 中的join和谓词下推
Hive的谓词下推
Hive中Join的原理和机制
Hive中的Join可分为Common Join(Reduce阶段完成join)和Map Join(Map阶段完成join)。
往往数据倾斜在Common Join中,Map输出时候以Join on条件中的列为key,根据key的值进行hash,并将key/value按照hash值推送至不同的reduce中,这样确保两个表中相同的key位于同一个reduce中。此时如果join的key是user_id,很可能导致发送到reduce的数据倾斜,导致join的热点问题。