Greenplum的Hash Join
在介绍HashJoin实现之前,首先了解下什么是 JOIN。根据维基百科(WIKIPedia),JOIN是关系数据库中组合一个或者多个表中的columns的算子。
而JOIN 有多种类型, SQL 标准中定义了 INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL OUTER JOIN 等四种类型,用集合论里的操作非常容易理解。 我们在下图直观的解释了这四种JOIN类型的效用。
此外还有其中JOIN类型,譬如 SEMI JOIN和 ANTI JOIN。 这两种JOIN不是 SQL 语法,然而通常用来实现某些 SQL 功能,后面会详细介绍他们。
本文将使用这样一个例子。 例子里面包含两张表:student表和score表,每张表都有几条记录。
testdb=# create table student(id int, name text, age int) distributed by(id);
CREATE TABLE
testdb=# insert into student values (10,'Jack',25),(12,'Tom',26),(13,'Ariel',25);
INSERT 0 3
testdb=# create table score(id int, stu_id int, subject text, score int) distributed by(stu_id);
CREATE TABLE
testdb=# insert into score values (1,10,'math',95),(2,10,'history',98),(3,12,'math',97),(4,15,'history',92);
INSERT 0 4
首先我们看下图中JOIN对应的 SQL语句。 通过 explain 可以查看这6个SQL例子的JOIN类型。 (需要设置 enable_mergejoin, enable_hashagg 为 OFF,否则优化器可能会选择其他查询计划)
testdb=# set enable_mergejoin = off;
SET
testdb=# set enable_hashagg = off;
SET
下图展示了查询结果,让我们对图中JOIN的作用有个直观的认知。
testdb=# select * from student;
id | name | age
----+-------+-----
10 | Jack | 25
12 | Tom | 26
13 | Ariel | 25
(3 rows)
testdb=# select * from score;
id | stu_id | subject | score
----+--------+---------+-------
4 | 15 | history | 92
1 | 10 | math | 95
2 | 10 | history | 98
3 | 12 | math | 97
(4 rows)
testdb=# select name, score from student st inner join score s on st.id = s.stu_id;
name | score
------+-------
Jack | 98
Jack | 95
Tom | 97
(3 rows)
testdb=# select name, score from student st left join score s on st.id = s.stu_id;
name | score
-------+-------
Jack | 98
Jack | 95
Ariel |
Tom | 97
(4 rows)
testdb=# select name, score from student st right join score s on st.id = s.stu_id;
name | score
------+-------
Tom | 97
| 92
Jack | 95
Jack | 98
(4 rows)
testdb=# select name, score from student st full join score s on st.id = s.stu_id;
name | score
-------+-------
| 92
Jack | 95
Jack | 98
Ariel |
Tom | 97
(5 rows)
testdb=# select id, name from student st where exists(select id from score s where st.id = s.stu_id);
id | name
----+------
10 | Jack
12 | Tom
(2 rows)
testdb=# select name from student st where not exists(select stu_id from score s where st.id = s.stu_id);
name
-------
Ariel
(1 row)