数据库中的连接语句用于将数据库中的两个或多个表根据连接条件组合起来。由”连接”生成的集合, 可以被保存为表, 或者当成表来使用。连接语句的含义是把两张表的属性通过它们的值组合在一起。数据库中的连接类型一般包括inner join,outer join,semi-join和anti-join。其中Semi-join和Anti-join都是通过子查询改写得到,SQL本身并没有表述Anti-join和Semi-join的语法
连接条件
连接条件可以分为等值连接(比如t1.a = t2.b)和非等值连接(t1.a < t2.b)。相比于非等值连接条件, 等值连接条件的一个好处是允许数据库中使用高效的连接算法,比如Hash Join和Merge-Sort join。
Self-join
Self-Join是指跟跟自己表做连接的join。下图展示了一个self join的例子。
OceanBase (root@test)> create table t1(a int primary key, b int, c int);
Query OK, 0 rows affected (0.70 sec)
---一个self join的例子
OceanBase (root@test)> select * from t1 as ta, t1 as tb where ta.b = tb.b
内连接(inner join)
Inner Join(内连接)是数据库中最基本的连接操作。内连接基于连接条件将两张表(如 A 和 B)的列组合在一起,产生新的结果表。查询会将 A 表的每一行和 B 表的每一行进行比较,并找出满足连接条件的组合。当连接条件被满足,A 和 B 中匹配的行会按列组合(并排组合)成结果集中的一行。连接产生的结果集,可以定义为首先对两张表做笛卡尔积(交叉连接) — 将 A 中的每一行和 B 中的每一行组合,然后返回满足连接条件的记录。
外连接(outer join)
Outer Join(外连接)并不要求连接的两表的每一条记录在对方表中都一条匹配的记录。要保留所有记录(甚至这条记录没有匹配的记录也要保留)的表称为保留表。外连接可依据连接表保留左表, 右表或全部表的行而进一步分为左外连接, 右外连接和全连接。其中左外连接中左表的一行未在右表中找到的时候,就在右表自动填充NULL。右外连接中右表的一行未在左表中找到的时候,就在左表自动填充NULL。全连接就是左表或者右表找不匹配行的时候都会自动填充。
Semi连接 (semi-join)
当A表和B表进行left/right semi-join的时候,它只返回A/B表中所有能够在B/A中找到匹配的行。Semi-join只能通过子查询展开得到,如下所示。
OceanBase (root@test)> create table t1(a int primary key, b int, c int);
Query OK, 0 rows affected (0.70 sec)
OceanBase (root@test)> create table t2(a int primary key, b int, c int);
Query OK, 0 rows affected (0.92 sec)
--- 有依赖关系的子查询被展开改写成Semi-join
OceanBase (root@test)> explain select * from t1 where t1.a in (select t2.b from t2 where t2.c = t1.c);
| =======================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
---------------------------------------
|0 |HASH SEMI JOIN| |1 |2924|
|1 | TABLE SCAN |t1 |1000 |455 |
|2 | TABLE SCAN |t2 |1000 |455 |
=======================================
Outputs & filters:
-------------------------------------
0 - output([t1.a], [t1.b], [t1.c]), filter(nil),
equal_conds([t1.a = t2.b], [t2.c = t1.c]), other_conds(nil)
1 - output([t1.c], [t1.a], [t1.b]), filter(nil),
access([t1.c], [t1.a], [t1.b]), partitions(p0)
2 - output([t2.c], [t2.b]), filter(nil),
access([t2.c], [t2.b]), partitions(p0)
Anti-连接(anti-join)
当A表和B表进行left/right anti-join的时候,它只返回A/B中所有不能再B/A中找到匹配的行。类似于Semi-join, anti-join也只能通过子查询展开得到,如下所示。
OceanBase (root@test)> create table t1(a int primary key, b int, c int);
Query OK, 0 rows affected (0.70 sec)
OceanBase (root@test)> create table t2(a int primary key, b int, c int);
Query OK, 0 rows affected (0.92 sec)
---有依赖关系的子查询被改写成Anti-join
OceanBase (root@test)> explain select * from t1 where t1.a not in (select t2.b from t2 where t2.c = t1.c);
| =======================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
---------------------------------------
|0 |HASH ANTI JOIN| |995 |3262|
|1 | TABLE SCAN |t1 |1000 |455 |
|2 | TABLE SCAN |t2 |1000 |455 |
=======================================
Outputs & filters:
-------------------------------------
0 - output([t1.a], [t1.b], [t1.c]), filter(nil),
equal_conds([t2.c = t1.c]), other_conds([t1.a = t2.b OR (T_OP_IS, t2.b, NULL, 0)])
1 - output([t1.c], [t1.a], [t1.b]), filter(nil),
access([t1.c], [t1.a], [t1.b]), partitions(p0)
2 - output([t2.c], [t2.b]), filter(nil),
access([t2.c], [t2.b]), partitions(p0)