“内连接”和“外连接”有什么区别?
-
09-06-2019 - |
题
还有怎么办 LEFT JOIN
, RIGHT JOIN
和 FULL JOIN
适合?
解决方案
假设您要加入没有重复项的列,这是一种非常常见的情况:
A 和 B 的内连接给出 A 与 B 相交的结果,即a 的内部部分 维恩图 路口。
A 和 B 的外连接给出 A 并 B 的结果,即维恩图并集的外部部分。
例子
假设您有两个表,每个表有一列,数据如下:
A B
- -
1 3
2 4
3 5
4 6
请注意,(1,2) 是 A 所独有的,(3,4) 是通用的,(5,6) 是 B 所独有的。
内部联接
使用任一等效查询的内部联接给出两个表的交集,即他们有共同的两行。
select * from a INNER JOIN b on a.a = b.b;
select a.*, b.* from a,b where a.a = b.b;
a | b
--+--
3 | 3
4 | 4
左外连接
左外连接将给出 A 中的所有行以及 B 中的所有公共行。
select * from a LEFT OUTER JOIN b on a.a = b.b;
select a.*, b.* from a,b where a.a = b.b(+);
a | b
--+-----
1 | null
2 | null
3 | 3
4 | 4
右外连接
右外连接将给出 B 中的所有行以及 A 中的所有公共行。
select * from a RIGHT OUTER JOIN b on a.a = b.b;
select a.*, b.* from a,b where a.a(+) = b.b;
a | b
-----+----
3 | 3
4 | 4
null | 5
null | 6
全外连接
完整的外连接将为您提供 A 和 B 的并集,即A 中的所有行和 B 中的所有行。如果 A 中的某些内容在 B 中没有对应的数据,则 B 部分为空,反之亦然。
select * from a FULL OUTER JOIN b on a.a = b.b;
a | b
-----+-----
1 | null
2 | null
3 | 3
4 | 4
null | 6
null | 5
其他提示
维恩图并不真正适合我。
例如,它们不显示交叉连接和内部连接之间的任何区别,或者更一般地显示不同类型的连接谓词之间的任何区别,或提供用于推理它们如何操作的框架。
理解逻辑处理是无可替代的,而且无论如何掌握起来相对简单。
- 想象一下交叉连接。
- 评估
on
针对步骤 1 中的所有行的子句保留谓词计算结果的行true
- (仅适用于外部联接)重新添加在步骤 2 中丢失的任何外部行。
(注意:在实践中,查询优化器可能会找到比上面纯逻辑描述更有效的执行查询的方法,但最终结果必须相同)
我将从动画版本开始 完全外连接. 。进一步的解释如下。
解释
源表
首先从一个 CROSS JOIN
(又名笛卡尔积)。这没有一个 ON
子句并简单地返回两个表中行的每个组合。
从 A CROSS JOIN B 中选择 A.Colour、B.Colour
内连接和外连接都有一个“ON”子句谓词。
- 内部联接。 评估交叉连接结果中所有行的“ON”子句中的条件。如果 true 返回连接的行。否则丢弃它。
- 左外连接。 与内连接相同,然后对于左表中与任何内容不匹配的任何行,将这些行输出为右表列的 NULL 值。
- 右外连接。 与内连接相同,然后对于右表中与任何内容不匹配的任何行,输出这些行并为左表列输出 NULL 值。
- 全外连接。 与内连接相同,然后保留左外连接中的左非匹配行,并根据右外连接保留右非匹配行。
一些例子
从 A.Colour = B.Colour 上的内连接 B 选择 A.Colour、B.Colour
上面是经典的等值连接。
动画版
从 A.Colour NOT IN 上的内连接 B 中选择 A.Colour、B.Colour('绿色'、'蓝色')
内连接条件不一定是相等条件,并且它不需要引用两个表(甚至任一表)中的列。评估 A.Colour NOT IN ('Green','Blue')
在交叉连接的每一行上返回。
从 A INNER JOIN B ON 1 =1 中选择 A.Colour、B.Colour
对于交叉连接结果中的所有行,连接条件的计算结果为 true,因此这与交叉连接相同。16行的图我就不再重复了。
从 A.Colour = B.Colour 上的左外连接 B 选择 A.Colour、B.Colour
外连接在逻辑上的计算方式与内连接相同,除了如果左表中的一行(对于左连接)根本不与右表中的任何行连接,它将保留在结果中 NULL
右侧列的值。
从 A.Colour = B.Colour 上的左外连接 B 选择 A.Colour、B.Colour,其中 B.Colour 为 NULL
这只是将先前的结果限制为仅返回其中的行 B.Colour IS NULL
. 。在这种特殊情况下,这些行将被保留,因为它们在右侧表中没有匹配项,并且查询返回表中不匹配的单个红色行 B
. 。这称为反半连接。
选择一个列很重要 IS NULL
测试不可为空或者连接条件确保任何 NULL
值将被排除,以便此模式正常工作并避免仅仅带回恰好具有 NULL
除了不匹配的行之外,该列的值。
从 A.Colour = B.Colour 上的右外连接 B 选择 A.Colour、B.Colour
右外连接的作用与左外连接类似,只是它们保留右表中的非匹配行,并且以 null 值扩展左手列。
从 A.Colour = B.Colour 上的完整外部连接 B 选择 A.Colour、B.Colour
完全外连接结合了左连接和右连接的行为,并保留左表和右表中的不匹配行。
从完整外部连接 B ON 1 = 0 中选择 A.Colour、B.Colour
交叉连接中没有行与 1=0
谓词。使用正常的外连接规则保留两侧的所有行,并在另一侧的表的列中使用 NULL。
SELECT COALESCE(A.Colour, B.Colour) AS Color FROM A FULL OUTER JOIN B ON 1 = 0
通过对前面的查询稍加修改,我们可以模拟 UNION ALL
两个表的。
从 A.Colour = B.Colour 上的左外连接 B 选择 A.Colour、B.Colour,其中 B.Colour = '绿色'
请注意, WHERE
子句(如果存在)逻辑上在连接之后运行。一个常见的错误是执行左外连接,然后在右表中包含一个带有条件的 WHERE 子句,最终排除不匹配的行。上面最终执行了外连接......
...然后运行“Where”子句。 NULL= 'Green'
不计算为 true,因此外连接保留的行最终被丢弃(连同蓝色的行),有效地将连接转换回内连接。
如果目的是仅包含 B 中颜色为绿色的行以及 A 中的所有行,无论正确的语法将是
从 A.Colour = B.Colour AND B.Colour = 'Green' 上的左外连接 B 选择 A.Colour、B.Colour
SQL小提琴
请参阅这些示例 在 SQLFiddle.com 上实时运行.
加入 用于组合两个表中的数据,结果是一个新的临时表。连接是基于称为谓词的东西来执行的,它指定了执行连接时要使用的条件。内连接和外连接之间的区别在于,内连接将仅返回基于连接谓词实际匹配的行。让我们考虑一下员工和位置表:
内部联接:-内连接通过组合两个表的列值创建一个新的结果表(员工 和 地点) 基于连接谓词。该查询比较每一行 员工 每行 地点 查找满足连接谓词的所有行对。当通过匹配非 NULL 值来满足连接谓词时,每个匹配的行对的列值 员工 和 地点 合并成一个结果行。内连接的 SQL 如下所示:
select * from employee inner join location on employee.empID = location.empID
OR
select * from employee, location where employee.empID = location.empID
外连接:-外连接并不要求两个连接表中的每条记录都有匹配的记录。连接表保留每条记录 — 即使不存在其他匹配记录。外连接进一步细分为左外连接和右外连接,具体取决于保留哪个表的行(左或右)。
左外连接:-表的左外连接(或简称左连接)的结果 员工 和 地点 始终包含“左”表的所有记录(员工),即使连接条件在“右”表中没有找到任何匹配的记录(地点)。使用上表,左外连接的 SQL 如下所示:
select * from employee left outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
右外连接:-右外部联接(或右联接)与左外部联接非常相似,只是表的处理相反。“右”表中的每一行(地点) 将在连接表中至少出现一次。如果“左”表中没有匹配行(员工) 存在,NULL 将出现在来自 员工 对于那些没有匹配的记录 地点。SQL 如下所示:
select * from employee right outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
使用上面的表格,我们可以显示右外连接的结果集是什么样子的:
完整外部连接:-Full Outer Join或Full Join是通过在连接结果中包含不匹配的行来保留不匹配的信息,使用完全外连接。它包括两个表中的所有行,无论另一个表是否具有匹配的值。
内部联接
只检索匹配的行,即 A intersect B
.
SELECT *
FROM dbo.Students S
INNER JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
左外连接
从第一个表中选择所有记录,以及匹配加入键的第二个表中的任何记录。
SELECT *
FROM dbo.Students S
LEFT JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
全外连接
从第二个表中选择所有记录,以及匹配加入键的第一个表中的任何记录。
SELECT *
FROM dbo.Students S
FULL JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
参考
简单来说:
一个 内部联接 仅检索匹配的行。
而 外连接 从一个表中检索匹配的行以及另一表中的所有行...结果取决于您使用的是哪一个:
左边: :右表中的匹配行和左表中的所有行
正确的: :左表中的匹配行和右表中的所有行或
满的: :所有表中的所有行。匹配与否并不重要
如果联接的另一(右侧)侧有匹配的记录,则内部联接仅显示行。
(左)外部联接在左侧显示每条记录的行,即使联接的另一(右侧)侧没有匹配的行。如果没有匹配的行,另一侧(右侧)的列将显示 NULL。
内联接要求联接表中存在具有相关 ID 的记录。
即使右侧不存在任何记录,外连接也会返回左侧的记录。
例如,您有一个 Orders 和一个 OrderDetails 表。它们通过“OrderID”相关。
命令
- 订单ID
- 顾客姓名
订单详细信息
- 订单明细ID
- 订单ID
- 产品名称
- 数量
- 价格
请求
SELECT Orders.OrderID, Orders.CustomerName
FROM Orders
INNER JOIN OrderDetails
ON Orders.OrderID = OrderDetails.OrderID
只会返回在 OrderDetails 表中也有某些内容的订单。
如果将其更改为 OUTER LEFT JOIN
SELECT Orders.OrderID, Orders.CustomerName
FROM Orders
LEFT JOIN OrderDetails
ON Orders.OrderID = OrderDetails.OrderID
那么它将返回 Orders 表中的记录,即使它们没有 OrderDetails 记录。
您可以使用它来查找没有任何 OrderDetails 的订单,通过添加像这样的 where 子句来指示可能的孤立订单 WHERE OrderDetails.OrderID IS NULL
.
简而言之:
内部联接 -> 仅从父表和子表中获取公共记录,其中父表的主键与子表中的外键匹配。
左连接 ->
伪代码
1.Take All records from left Table
2.for(each record in right table,) {
if(Records from left & right table matching on primary & foreign key){
use their values as it is as result of join at the right side for 2nd table.
} else {
put value NULL values in that particular record as result of join at the right side for 2nd table.
}
}
右连接 :与 left join 完全相反。将 LEFT JOIN 中的表名称放在 Right join 的右侧,您将获得与 LEFT JOIN 相同的输出。
外连接 :显示两个表中的所有记录 No matter what
. 。如果左表中的记录与基于主键、外键的右表不匹配,则使用 NULL 值作为连接结果。
例子 :
现在假设有 2 个表
1.employees , 2.phone_numbers_employees
employees : id , name
phone_numbers_employees : id , phone_num , emp_id
这里,employees表是主表,phone_numbers_employees是子表(它包含 emp_id
作为连接的外键 employee.id
所以它的子表。)
内连接
取2张表的记录 仅当员工表的主键(其ID)与子表phone_numbers_employees(emp_id)的外键匹配时.
所以查询将是:
SELECT e.id , e.name , p.phone_num FROM employees AS e INNER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;
这里仅采用主键 = 外键上的匹配行,如上所述。这里主键 = 外键上的非匹配行将作为连接结果被跳过。
左连接 :
左连接保留左表的所有行,无论右表是否有匹配的行。
SELECT e.id , e.name , p.phone_num FROM employees AS e LEFT JOIN phone_numbers_employees AS p ON e.id = p.emp_id;
外连接 :
SELECT e.id , e.name , p.phone_num FROM employees AS e OUTER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;
从图表上看,它看起来像:
你用 INNER JOIN
返回两个表中存在匹配的所有行。IE。在结果表中,所有行和列都将具有值。
在 OUTER JOIN
结果表可能有空列。外连接可以是 LEFT
或者 RIGHT
.
LEFT OUTER JOIN
返回第一个表中的所有行,即使第二个表中没有匹配项。
RIGHT OUTER JOIN
返回第二个表中的所有行,即使第一个表中没有匹配项。
INNER JOIN
要求比较两个表时至少存在匹配项。例如,表A和表B意味着A ٨ B(A交集B)。
LEFT OUTER JOIN
和 LEFT JOIN
是相同的。它给出了两个表中匹配的所有记录以及左表的所有可能性。
相似地, RIGHT OUTER JOIN
和 RIGHT JOIN
是相同的。它给出了两个表中匹配的所有记录以及右表的所有可能性。
FULL JOIN
是以下组合 LEFT OUTER JOIN
和 RIGHT OUTER JOIN
没有重复。
答案在于每一个的含义,因此也在于结果。
笔记 :
在SQLite
没有RIGHT OUTER JOIN
或者FULL OUTER JOIN
.
还有在MySQL
没有FULL OUTER JOIN
.
我的回答是基于上面的 笔记.
当你有两个像这样的表时:
--[table1] --[table2]
id | name id | name
---+------- ---+-------
1 | a1 1 | a2
2 | b1 3 | b2
交叉连接/外连接:
您可以使用以下命令获取所有这些表数据 CROSS JOIN
或者只是与 ,
像这样:
SELECT * FROM table1, table2
--[OR]
SELECT * FROM table1 CROSS JOIN table2
--[Results:]
id | name | id | name
---+------+----+------
1 | a1 | 1 | a2
1 | a1 | 3 | b2
2 | b1 | 1 | a2
2 | b1 | 3 | b2
内部联接 :
当您想根据类似关系向上述结果添加过滤器时 table1.id = table2.id
您可以使用 INNER JOIN
:
SELECT * FROM table1, table2 WHERE table1.id = table2.id
--[OR]
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id
--[Results:]
id | name | id | name
---+------+----+------
1 | a1 | 1 | a2
左[外]连接:
当您想要在上述结果中获得其中一个表的所有行 - 具有相同的关系 - 您可以使用 LEFT JOIN
:
(为了 右连接 只需改变桌子的位置)
SELECT * FROM table1, table2 WHERE table1.id = table2.id
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
--[OR]
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
--[Results:]
id | name | id | name
---+------+------+------
1 | a1 | 1 | a2
2 | b1 | Null | Null
全外连接:
当您还想在结果中包含另一个表的所有行时,您可以使用 FULL OUTER JOIN
:
SELECT * FROM table1, table2 WHERE table1.id = table2.id
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
UNION ALL
SELECT Null, Null, * FROM table2 WHERE Not table2.id In (SELECT id FROM table1)
--[OR] (recommended for SQLite)
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
UNION ALL
SELECT * FROM table2 LEFT JOIN table1 ON table2.id = table1.id
WHERE table1.id IS NULL
--[OR]
SELECT * FROM table1 FULL OUTER JOIN table2 On table1.id = table2.id
--[Results:]
id | name | id | name
-----+------+------+------
1 | a1 | 1 | a2
2 | b1 | Null | Null
Null | Null | 3 | b2
好吧,根据您的需要,您可以选择满足您需要的每一项;)。
内部联接。
连接是将两个表中的行组合在一起。一个 内部联接 尝试根据您在查询中指定的条件来匹配两个表,并且仅返回匹配的行。如果连接中第一个表中的一行与第二个表中的两行匹配,则结果中将返回两行。如果第一个表中的行与第二个表中的行不匹配,则不会返回;同样,如果第二个表中的行与第一个表中的行不匹配,则不会返回。
外连接。
A 左连接 尝试查找第一个表中的行与第二个表中的行的匹配。如果找不到匹配项,它将返回第一个表中的列,并将第二个表中的列留空 (null)。
我在其他答案中没有看到有关性能和优化器的太多细节。
有时,知道只有 INNER JOIN
是关联的,这意味着优化器有最多的选择来使用它。它可以重新排序连接顺序,使其更快地保持相同的结果。优化器可以使用最多的连接模式。
一般来说,尝试使用是一个很好的做法 INNER JOIN
而不是不同类型的连接。(当然,如果可以考虑预期的结果集的话。)
关于这种奇怪的联想行为,这里有几个很好的例子和解释:
精确的算法为 INNER JOIN
, LEFT/RIGHT OUTER JOIN
如下:
- 从第一个表中取出每一行:
a
- 考虑旁边第二个表中的所有行:
(a, b[i])
- 评估
ON ...
针对每对子句:ON( a, b[i] ) = true/false?
- 当条件评估为
true
, ,返回合并后的行(a, b[i])
. - 当到达第二个表的末尾时没有任何匹配,这是一个
Outer Join
然后返回一个 (虚拟的) 配对使用Null
对于其他表的所有列:(a, Null)
对于 LEFT 外连接或(Null, b)
对于右外连接。这是为了确保第一个表的所有行都存在于最终结果中。
- 当条件评估为
笔记: 中指定的条件 ON
子句可以是任何内容,不需要使用 主键 (并且您不需要总是引用两个表中的列)!例如:
... ON T1.title = T2.title AND T1.version < T2.version
( => 请参阅这篇文章作为示例用法: 仅选择列上具有最大值的行)... ON T1.y IS NULL
... ON 1 = 0
(就像样本一样)
笔记: 左连接=左外连接,右连接=右外连接。
INNER JOIN
最典型的两个或多个表的连接。它返回表 ON 主键和外键关系上的数据匹配。OUTER JOIN
与INNER JOIN
, ,但它还包括NULL
结果集上的数据。LEFT JOIN
=INNER JOIN
+ 不匹配的数据 左边 表与Null
匹配右表。RIGHT JOIN
=INNER JOIN
+ 不匹配的数据 正确的 表与Null
匹配左表。FULL JOIN
=INNER JOIN
+ 无与伦比的数据 左右 桌子与Null
火柴。
- 自连接不是 SQL 中的关键字,当表本身引用数据时称为自连接。使用
INNER JOIN
和OUTER JOIN
我们可以编写自连接查询。
例如:
SELECT *
FROM tablea a
INNER JOIN tableb b
ON a.primary_key = b.foreign_key
INNER JOIN tablec c
ON b.primary_key = c.foreign_key
最简单的定义
内部联接:退货 匹配记录 来自两个表。
完全外部连接:返回匹配的和 不匹配的记录 来自两个表,其中不匹配的记录为空 两张桌子.
左外连接:仅从表中返回匹配和不匹配的记录 左边.
右外连接:仅从表中返回匹配和不匹配的记录 右边.
简而言之
匹配 + 左侧不匹配 + 右侧不匹配 = 全外连接
匹配 + 左侧不匹配 = 左外连接
匹配 + 右侧不匹配 = 右外连接
匹配 = 内部联接
简单来说,
1.内连接或等连接: 返回仅与两个表中的条件匹配的结果集。
2.外部连接: 即使条件匹配或不匹配,也会返回两个表中所有值的结果集。
3.左连接: 返回左表中所有值以及右表中符合条件的行的结果集。
4.右连接: 返回右表中所有值以及左表中符合条件的行的结果集。
5.完全加入: 全连接和全外连接是相同的。
1.内部联接: 也称为加入。它仅返回左表和右表中存在的行 如果有匹配项. 。否则,它返回零记录。
例子:
SELECT
e1.emp_name,
e2.emp_salary
FROM emp1 e1
INNER JOIN emp2 e2
ON e1.emp_id = e2.emp_id
2.完全外部连接: 也称为完全连接。它返回 所有行 存在于左表和右表中。
例子:
SELECT
e1.emp_name,
e2.emp_salary
FROM emp1 e1
FULL OUTER JOIN emp2 e2
ON e1.emp_id = e2.emp_id
3.左外连接: 或者简称为左连接。它返回左表中存在的所有行以及右表中的匹配行(如果有)。
4.右外连接: 也称为右连接。它返回左表(如果有)中的匹配行以及右表中存在的所有行。
连接的优点
- 执行速度更快。
内部联接 - 一个 内部联接 使用任一等效查询给出两者的交集 桌子, , IE。他们有共同的两行。
左外连接 - A 左外连接 将给出 A 中的所有行,以及 B 中的所有公共行。
全外连接 - A 完全外连接 会给你 A 和 B 的并集,即A 中的所有行和 B 中的所有行。如果 A 中的某些内容在 B 中没有对应的数据,则 B 部分为空,反之亦然
left join on
(又名 left outer join on
) 返回 inner join on
行 union all
由空值扩展的不匹配的左表行。
right join (on
又名 right outer join on
) 返回 inner join on
行 union all
由空值扩展的不匹配的右表行。
full join on
(又名 full outer join on
) 返回 inner join on
行union all
由空值扩展的不匹配的左表行 union all
由空值扩展的不匹配的右表行。
(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1、一般规则 1 b、3 c 和 d、5 b。)
所以不要 outer join
直到你知道底层是什么 inner join
参与。
阅读我的评论,其中有许多令人困惑和糟糕的答案。
然后阅读我的评论,了解许多困惑和糟糕的答案。
内连接和外连接的区别如下:
- 内连接是基于匹配元组组合表的连接,而外连接是基于匹配和不匹配元组组合表的连接。
- 内连接合并两个表中的匹配行,其中省略不匹配的行,而外连接合并两个表中的行,不匹配的行填充空值。
- 内连接类似于交集运算,而外连接类似于并集运算。
- 内连接有两种类型,外连接有三种类型。
- 内连接速度较慢,而外连接速度比内连接速度快。
考虑下面 2 个表:
电磁脉冲
empid name dept_id salary
1 Rob 1 100
2 Mark 1 300
3 John 2 100
4 Mary 2 300
5 Bill 3 700
6 Jose 6 400
部门
deptid name
1 IT
2 Accounts
3 Security
4 HR
5 R&D
内部联接:
大部分写成 加入 在sql查询中。它仅返回表之间的匹配记录。
找出所有员工及其部门名称:
Select a.empid, a.name, b.name as dept_name
FROM emp a
JOIN department b
ON a.dept_id = b.deptid
;
empid name dept_name
1 Rob IT
2 Mark IT
3 John Accounts
4 Mary Accounts
5 Bill Security
正如你在上面看到的, Jose
不是打印自 电磁脉冲 在输出中,因为它是 dept_id 6
在部门表中找不到匹配项。相似地, HR
和 R&D
行不打印自 部门 表,因为他们在 Emp 表中没有找到匹配项。
因此,INNER JOIN 或只是 JOIN,仅返回匹配的行。
左连接:
这将返回 LEFT 表中的所有记录,并且仅返回 RIGHT 表中的匹配记录。
Select a.empid, a.name, b.name as dept_name
FROM emp a
LEFT JOIN department b
ON a.dept_id = b.deptid
;
empid name dept_name
1 Rob IT
2 Mark IT
3 John Accounts
4 Mary Accounts
5 Bill Security
6 Jose
因此,如果您观察上面的输出,左表(Emp)中的所有记录都将与右表中的匹配记录一起打印。
HR
和 R&D
行不打印自 部门 表,因为他们在 Dept_id 的 Emp 表中没有找到匹配项。
因此,LEFT JOIN 返回左表中的所有行,并且仅返回右表中的匹配行。
也可以查看DEMO 这里.