在 SQL 中连接表






4.86/5 (27投票s)
解释不同的连接表的方法
引言
本文档将尝试解释两种表连接的工作方式。为了更好地解释这一点,我创建了两个包含特定行的临时表来证明这一点。请参阅附录 A 中的 SQL 语句来创建示例临时表。我们有一个名为 TEMP_A 的表,其中包含四行,这四行的 ID 分别是唯一的,编号为 1、2、3、4。然后我们还有一个名为 TEMP_B 的表,其中包含五行。它的行是 1、2、3、3、5。请注意,表 A 中的行 1 和 2 在表 B 中各有一个引用。
行 3 在表 B 中有两个引用,行 4 在表 B 中没有任何引用,而表 B 中有一行孤立行(行 5),在表 A 中没有父行。
另外请注意,保留字 inner 和 outer 是可选的。left outer join 和 left join 的意思完全相同。
好了,现在开始有趣的部分。
普通连接(或内部连接)
在 ID 字段上连接(或内部连接)这两个表,您将获得两个集合的交集中的所有行,这意味着它们具有相同的值。
使用附录 A 中创建的数据样本,我们将得到以下结果:
select * from TEMP_A INNER JOIN TEMP_B ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
请注意,表 A 中的第 3 行会根据表 B 中的每个对应行进行一次重复。
左连接(或左外连接)
左连接将返回两个表的交集,此外,它还将返回左表中没有对应行的行。什么是左,什么是右?嗯,左表是指定的第一个表,右表是指定的第二个表。或者右表是 Join 语句之后的表。左边是 SQL 正在处理的其余数据。
使用附录 A 中创建的数据样本,我们将得到以下结果:
select *
from TEMP_A
LEFT OUTER JOIN TEMP_B
ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
4 | 表 A 第 4 行 | NULL | NULL |
请注意,表 A 中的第 4 行现在已包含在内,但由于表 B 中没有对应的行,因此表 B 的所有字段都包含 NULL。
右连接(或右外连接)
右连接非常类似于左连接,但它返回右表中没有对应左表行的行。
使用附录 A 中创建的数据样本,我们将得到以下结果:
select *
from TEMP_A
RIGHT OUTER JOIN TEMP_B
ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
NULL | NULL | 5 | 表 B 第 5 行 |
此集合也包含 5 行。但这次最后一行包含来自表 B 的数据,而来自表 A 的所有数据均为 NULL。
全连接(或全外连接)
这就像左连接和右连接的组合。它将返回两个表的交集,以及表 A 中所有没有对应表 B 行的行,以及表 B 中所有没有对应表 A 行的行。
使用附录 A 中创建的数据样本,我们将得到以下结果:
select *
from TEMP_A
FULL OUTER JOIN TEMP_B
ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
4 | 表 A 第 4 行 | NULL | NULL |
NULL | NULL | 5 | 表 B 第 5 行 |
交叉连接
嗯,交叉连接(Cross Join)并不是真正的连接,你不需要指定连接的字段,只需要指定表的名称。它会将表 A 中的每一行与表 B 中的每一行匹配起来,所以最终结果会有很多行。
使用附录 A 中创建的数据样本,我们将得到以下结果:
select * from TEMP_A, TEMP_B
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
1 | 表 A 第 1 行 | 2 | 表 B 第 2 行 |
1 | 表 A 第 1 行 | 3 | 表 B 第 3a 行 |
1 | 表 A 第 1 行 | 3 | 表 B 第 3b 行 |
1 | 表 A 第 1 行 | 5 | 表 B 第 5 行 |
2 | 表 A 第 2 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
2 | 表 A 第 2 行 | 3 | 表 B 第 3a 行 |
2 | 表 A 第 2 行 | 3 | 表 B 第 3b 行 |
2 | 表 A 第 2 行 | 5 | 表 B 第 5 行 |
3 | 表 A 第 3 行 | 1 | 表 B 第 1 行 |
3 | 表 A 第 3 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
3 | 表 A 第 3 行 | 5 | 表 B 第 5 行 |
4 | 表 A 第 4 行 | 1 | 表 B 第 1 行 |
4 | 表 A 第 4 行 | 2 | 表 B 第 2 行 |
4 | 表 A 第 4 行 | 3 | 表 B 第 3a 行 |
4 | 表 A 第 4 行 | 3 | 表 B 第 3b 行 |
4 | 表 A 第 4 行 | 5 | 表 B 第 5 行 |
为什么不使用唯一键是不好的
在本次解释中,我使用了 TBL_ID 字段具有唯一值的表 A。不建议使用连接键在两个表中的至少一个表中不唯一的表。如果是这种情况,您会发现非唯一键会执行交叉连接。让我们向表 A 添加一行来演示这一点。
insert into TEMP_A values (3, 'Tbl A Row 3 dup')
现在像以前一样选择一个普通连接,并观察结果。
select *
from TEMP_A
INNER JOIN TEMP_B
ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
1 | 表 A 第 1 行 | 1 | 表 B 第 1 行 |
2 | 表 A 第 2 行 | 2 | 表 B 第 2 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行 | 3 | 表 B 第 3b 行 |
3 | 表 A 第 3 行(重复) | 3 | 表 B 第 3a 行 |
3 | 表 A 第 3 行(重复) | 3 | 表 B 第 3b 行 |
看看我们现在返回了 6 行。
使用连接选择孤立行
那么,如何获取一个表中所有没有对应另一个表行的行呢?很简单,使用左连接或右连接,并加上一个 WHERE 子句移除不需要的行。
使用样本数据
select *
from TEMP_A
LEFT OUTER JOIN TEMP_B
ON TEMP_A.Tbl_ID = TEMP_B.Tbl_ID
where TEMP_B.Tbl_ID is null
Tbl_ID | Tbl_Data | Tbl_ID | Tbl_Data |
4 | 表 A 第 4 行 | NULL | NULL |
附录 A(样本数据)
-- Create the first temp table
create table TEMP_A (
Tbl_ID int not null,
Tbl_Data varchar(50) not null
)
-- Insert sample data in to first temp table
insert into TEMP_A values (1, 'Tbl A Row 1')
insert into TEMP_A values (2, 'Tbl A Row 2')
insert into TEMP_A values (3, 'Tbl A Row 3')
insert into TEMP_A values (4, 'Tbl A Row 4')
-- Create the second temp table
create table TEMP_B (
Tbl_ID int not null,
Tbl_Data varchar(50) not null
)
-- Inset sample data into the second temp table
insert into TEMP_B values (1, 'Tbl B Row 1')
insert into TEMP_B values (2, 'Tbl B Row 2')
insert into TEMP_B values (3, 'Tbl B Row 3a')
insert into TEMP_B values (3, 'Tbl B Row 3b')
insert into TEMP_B values (5, 'Tbl B Row 5')