另外,LEFT JOIN
、RIGHT JOIN
和 FULL JOIN
如何适应?
假设您要加入没有重复的列,这是一种非常常见的情况:
A 和 B 的内连接给出了 A 相交 B 的结果,即维恩图相交的内部部分。
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
维恩图并没有真正为我做这件事。
例如,它们没有显示交叉连接和内部连接之间的任何区别,或者更一般地显示不同类型的连接谓词之间的任何区别,或者提供了推理它们将如何操作的框架。
理解逻辑处理是无可替代的,而且无论如何都比较容易掌握。
想象一下交叉连接。针对步骤 1 中的所有行评估 on 子句,保持谓词评估为 true 的那些行(仅适用于外部连接)添加回在步骤 2 中丢失的任何外部行。
(注意:在实践中,查询优化器可能会找到比上述纯逻辑描述更有效的查询执行方式,但最终结果必须相同)
我将从完整外部联接的动画版本开始。进一步的解释如下。
https://i.stack.imgur.com/VUkfU.gif
解释
源表
https://i.stack.imgur.com/LVYKx.png
首先从 CROSS JOIN
(又名笛卡尔积)开始。这没有 ON
子句,只是返回两个表中的每个行组合。
从交叉连接 B 中选择 A.Colour、B.Colour
https://i.stack.imgur.com/cv3t6.png
内连接和外连接有一个“ON”子句谓词。
内部联接。评估交叉连接结果中所有行的“ON”子句中的条件。如果为 true,则返回连接的行。否则丢弃它。
左外连接。与内连接相同,然后对于左表中与任何不匹配的任何行输出这些与右表列的 NULL 值。
右外连接。与内连接相同,然后对于右表中与任何不匹配的任何行输出这些与左表列的 NULL 值。
全外连接。与内连接相同,然后保留左外连接中的左不匹配行和右外连接中的右非匹配行。
一些例子
从 A.Colour 上的 A INNER JOIN B 中选择 A.Colour、B.Colour = B.Colour
以上是经典的 equi join。
https://i.stack.imgur.com/a8IHd.png
动画版
https://i.stack.imgur.com/kZcvR.gif
SELECT A.Colour, B.Colour 从 A.Colour NOT IN ('Green','Blue') 上的 A INNER JOIN B
内连接条件不一定是相等条件,也不需要引用两个(甚至任何一个)表中的列。对交叉连接的每一行计算 A.Colour NOT IN ('Green','Blue')
会返回。
https://i.stack.imgur.com/ZwoCi.png
SELECT A.Colour, B.Colour 从 A INNER JOIN B ON 1 =1
对于交叉连接结果中的所有行,连接条件的计算结果为真,因此这与交叉连接相同。 16行的图我就不再赘述了。
从 A.Colour 的左外连接 B 中选择 A.Colour、B.Colour = B.Colour
外部联接的逻辑评估方式与内部联接相同,只是如果左表中的一行(对于左联接)根本不与右侧表中的任何行联接,则它会保留在 NULL
的结果中右侧列的值。
https://i.stack.imgur.com/4bzv2.png
从 A.Colour = B.Colour 的左外连接 B 中选择 A.Colour,B.Colour,其中 B.Colour 为 NULL
这只是将先前的结果限制为仅返回 B.Colour IS NULL
所在的行。在这种特殊情况下,这些将是保留的行,因为它们在右侧表中不匹配,并且查询返回表 B
中不匹配的单个红色行。这称为反半连接。
为 IS NULL
测试选择一个不可为空或连接条件可确保排除任何 NULL
值的列非常重要,以便此模式正常工作并避免只带回发生的行除了不匹配的行之外,该列还有一个 NULL
值。
https://i.stack.imgur.com/d6CVF.png
从 A.Colour = B.Colour 的右外连接 B 中选择 A.Colour、B.Colour
右外连接的作用类似于左外连接,除了它们保留右表中不匹配的行并且空扩展左列。
https://i.stack.imgur.com/LIOW4.png
从 A.Colour = B.Colour 的完整外部连接 B 中选择 A.Colour、B.Colour
全外连接结合了左连接和右连接的行为,并保留左右表中不匹配的行。
https://i.stack.imgur.com/iVoqu.png
从 1 = 0 的完整外部连接 B 中选择 A.Colour、B.Colour
交叉连接中没有与 1=0
谓词匹配的行。两侧的所有行都使用正常的外部连接规则保留,另一侧表的列中为 NULL。
https://i.stack.imgur.com/gtIhf.png
选择 COALESCE(A.Colour, B.Colour) 作为颜色从 FULL OUTER JOIN B ON 1 = 0
对前面的查询稍作修改,就可以模拟两个表中的 UNION ALL
。
https://i.stack.imgur.com/WPu9W.png
从 A.Colour = B.Colour 的左外连接 B 中选择 A.Colour、B.Colour 其中 B.Colour = 'Green'
请注意,WHERE
子句(如果存在)逻辑上在连接之后运行。一个常见的错误是执行左外连接,然后在右表上包含一个 WHERE 子句,该条件最终排除了不匹配的行。以上最终执行了外部连接......
https://i.stack.imgur.com/4bzv2.png
...然后“Where”子句运行。 NULL= 'Green'
不评估为 true,因此外部连接保留的行最终被丢弃(连同蓝色的行),有效地将连接转换回内部连接。
https://i.stack.imgur.com/tRHdf.png
如果打算只包括 B 中颜色为绿色的行以及 A 中的所有行,无论正确的语法是
从 A.Colour = B.Colour 和 B.Colour = 'Green' 的左外连接 B 中选择 A.Colour、B.Colour
https://i.stack.imgur.com/cvJ1s.png
SQL小提琴
请参阅这些示例 run live at SQLFiddle.com。
连接用于组合来自两个表的数据,结果是一个新的临时表。连接是基于称为谓词的东西执行的,谓词指定了用于执行连接的条件。内连接和外连接之间的区别在于,内连接将仅返回基于连接谓词实际匹配的行。例如,让我们考虑 Employee 和 Location 表:
员工
EmpID EmpName 13 杰森 8 亚历克斯 3 拉姆 17 巴布 25 约翰逊
地点
EmpID EmpLoc 13 San Jose 8 Los Angeles 3 Pune, India 17 Chennai, India 39 Bangalore, India
内连接:- 内连接通过基于连接谓词组合两个表(员工和位置)的列值来创建一个新的结果表。该查询将 Employee 的每一行与 Location 的每一行进行比较,以找到满足连接谓词的所有行对。当通过匹配非 NULL 值满足连接谓词时,Employee 和 Location 的每对匹配行的列值将组合成一个结果行。下面是内部连接的 SQL 的样子:
select * from employee inner join location on employee.empID = location.empID
OR
select * from employee, location where employee.empID = location.empID
现在,运行该 SQL 的结果如下所示:
Employee.EmpId Employee.EmpName Location.EmpId Location.EmpLoc 13 Jason 13 San Jose 8 Alex 8 Los Angeles 3 Ram 3 Pune, India 17 Babu 17 Chennai, India
外连接:外连接不需要两个连接表中的每条记录都有匹配的记录。即使不存在其他匹配记录,连接表也会保留每条记录。外连接进一步细分为左外连接和右外连接,具体取决于保留哪个表的行(左或右)。
左外连接:- 员工和位置表的左外连接(或简称为左连接)的结果始终包含“左”表(员工)的所有记录,即使连接条件在“右”表(位置)。下面是左外连接的 SQL 的样子,使用上面的表:
select * from employee left outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
现在,运行此 SQL 的结果如下所示:
Employee.EmpId Employee.EmpName Location.EmpId Location.EmpLoc 13 Jason 13 San Jose 8 Alex 8 Los Angeles 3 Ram 3 Pune, India 17 Babu 17 Chennai, India 25 Johnson NULL NULL
请注意,虽然 Johnson 在员工位置表中没有条目,但他仍然包含在结果中,但位置字段为空。
右外连接:- 右外连接(或右连接)与左外连接非常相似,但表的处理方式相反。 “右”表(位置)中的每一行将至少出现在连接表中一次。如果“左”表 (Employee) 中不存在匹配的行,则 NULL 将出现在 Employee 的列中,用于那些在 Location 中没有匹配的记录。这就是 SQL 的样子:
select * from employee right outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
使用上面的表格,我们可以显示右外连接的结果集是什么样的:
Employee.EmpId Employee.EmpName Location.EmpId Location.EmpLoc 13 Jason 13 San Jose 8 Alex 8 Los Angeles 3 Ram 3 Pune, India 17 Babu 17 Chennai, India NULL NULL 39 印度班加罗尔
请注意,虽然没有列出在班加罗尔工作的员工,但它仍然包含在结果中,员工字段为空。
完全外连接:- 完全外连接或完全连接是通过在连接结果中包含不匹配的行来保留不匹配的信息,使用完全外连接。它包括两个表中的所有行,无论另一个表是否具有匹配值。
Employee.EmpId Employee.EmpName Location.EmpId Location.EmpLoc 13 Jason 13 San Jose 8 Alex 8 Los Angeles 3 Ram 3 Pune, India 17 Babu 17 Chennai, India 25 Johnson NULL NULL NULL NULL 39 印度班加罗尔
MySQL 8.0 Reference Manual - Join Syntax
内部联接
仅检索匹配的行,即 A intersect B
。
https://i.stack.imgur.com/Zkk3I.jpg
SELECT *
FROM dbo.Students S
INNER JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
左外连接
选择第一个表中的所有记录,以及第二个表中与连接键匹配的任何记录。
https://i.stack.imgur.com/Z584b.jpg
SELECT *
FROM dbo.Students S
LEFT JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
全外连接
选择第二个表中的所有记录,以及第一个表中与连接键匹配的任何记录。
https://i.stack.imgur.com/c1QF3.jpg
SELECT *
FROM dbo.Students S
FULL JOIN dbo.Advisors A
ON S.Advisor_ID = A.Advisor_ID
参考
内连接和外连接 SQL 示例和 Join 块
SQL:连接
简单来说:
内连接只检索匹配的行。
而外连接从一个表中检索匹配的行以及其他表中的所有行......结果取决于您使用的是哪一个:
左:右表中的匹配行和左表中的所有行
右:左表中的匹配行和右表中的所有行或
完整:所有表中的所有行。有没有匹配都没关系
内连接仅在连接的另一(右侧)存在匹配记录时才显示行。
(左)外部联接在左侧显示每条记录的行,即使在联接的另一(右侧)侧没有匹配的行。如果没有匹配的行,另一侧(右侧)的列将显示 NULL。
内部联接要求联接表中存在具有相关 ID 的记录。
即使右侧不存在任何内容,外部联接也会返回左侧的记录。
例如,您有一个 Orders 和一个 OrderDetails 表。它们通过“OrderID”相关联。
订单
订单编号
顾客姓名
订单详细信息
订单详情 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 记录。
您可以通过添加类似 WHERE OrderDetails.OrderID IS NULL
的 where 子句来使用它来查找没有任何 OrderDetails 指示可能孤立订单的订单。
SELECT c.id, c.status, cd.name, c.parent_id, cd.description, c.image FROM categories c, categories_description cd WHERE c.id = cd.categories_id AND c.status = 1 AND cd.language_id = 2 ORDER BY c.parent_id ASC
之类的请求更改为 SELECT c.id, c.status, cd.name, c.parent_id, cd.description, c.image FROM categories c INNER JOIN categories_description cd ON c.id = cd.categories_id WHERE c.status = 1 AND cd.language_id = 2 ORDER BY c.parent_id ASC
(MySQL)。我不确定附加条件,它们混合得很好......
简单来说:
内连接 -> 仅从父表和子表中获取公共记录,其中父表的主键与子表中的外键匹配。
左连接->
伪代码
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.
}
}
右连接:与左连接完全相反。将表名放入右连接右侧的左连接中,您将获得与左连接相同的输出。
外部联接:显示两个表 No matter what
中的所有记录。如果左表中的记录根据Primary,Forieign key与右表不匹配,则使用NULL值作为连接结果。
例子 :
https://i.stack.imgur.com/pCErn.png
现在让我们假设 2 个表
1.employees , 2.phone_numbers_employees
employees : id , name
phone_numbers_employees : id , phone_num , emp_id
这里,employees 表是 Master 表,phone_numbers_employees 是子表(它包含 emp_id
作为外键连接 employee.id
所以它的子表。)
内连接
仅当员工表的主键(其 id)与子表 phone_numbers_employees(emp_id)的外键匹配时,才获取 2 个表的记录。
所以查询将是:
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;
从图形上看,它看起来像:
https://i.stack.imgur.com/hMKKt.jpg
您使用 INNER JOIN
从两个表中返回所有匹配的行。即在结果表中,所有行和列都有值。
在 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 / OUTER JOIN :
您可以使用 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
INNER JOIN :
当您想根据 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 [OUTER] JOIN :
当您希望上述结果中的一个表的所有行具有相同的关系时,您可以使用 LEFT JOIN
:
(对于 RIGHT 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 :
如果您还想在结果中包含另一个表的所有行,您可以使用 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
好吧,根据您的需要,您可以选择满足您需要的每一个;)。
full outer join
。
内部联接。
连接是组合两个表中的行。内连接尝试根据您在查询中指定的条件匹配两个表,并且只返回匹配的行。如果连接中第一个表中的一行与第二个表中的两行匹配,则结果中将返回两行。如果第一个表中有一行与第二个表中的行不匹配,则不返回;同样,如果第二个表中有一行与第一个表中的行不匹配,则不会返回。
外连接。
左连接尝试将第一个表中的行与第二个表中的行匹配。如果找不到匹配项,它将返回第一个表中的列,并将第二个表中的列留空(null)。
https://i.stack.imgur.com/TBMzF.jpg
INNER JOIN 最典型的两个或多个表的连接。它返回表 ON primarykey 和 forignkey 关系上的数据匹配。
OUTER JOIN 与 INNER JOIN 相同,但它还包括 ResultSet 上的 NULL 数据。 LEFT JOIN = INNER JOIN + 左表不匹配数据与右表空匹配。 RIGHT JOIN = INNER JOIN + 右表的不匹配数据与左表的空匹配。 FULL JOIN = INNER JOIN + 左右表上的不匹配数据与 Null 匹配。
LEFT JOIN = INNER JOIN + 左表不匹配数据与右表空匹配。
RIGHT JOIN = INNER JOIN + 右表的不匹配数据与左表的空匹配。
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
我在其他答案中看不到有关性能和优化器的太多细节。
有时很高兴知道只有 INNER JOIN
是关联的,这意味着优化器拥有最多的选项来使用它。它可以重新排序连接顺序,以使其更快地保持相同的结果。优化器可以使用最多的连接模式。
通常,尝试使用 INNER JOIN
代替不同类型的连接是一种很好的做法。 (当然,如果可以考虑预期的结果集。)
关于这种奇怪的关联行为,这里有几个很好的例子和解释:
左外连接是关联的吗?
连接顺序在 SQL 中重要吗?
INNER JOIN
在大多数情况下都比 LEFT JOIN
慢,人们可以通过添加 WHERE
来使用 LEFT JOIN
而不是 INNER JOIN
来删除意外的 NULL
结果;)。
INNER
较慢?
在批评了广受欢迎的红色阴影维恩图之后,我认为发布我自己的尝试是公平的。
尽管@Martin Smith 的回答是这群人中最好的,但他只显示每个表的关键列,而我认为理想情况下也应该显示非关键列。
在允许的半小时内我能做的最好的事情,我仍然认为它没有充分表明由于 TableB
中没有键值或者 OUTER JOIN
实际上是联合而不是连接而存在空值:
https://i.stack.imgur.com/fzwkg.png
TableB B RIGHT OUTER JOIN TableA a
替换 TableA a LEFT OUTER JOIN TableB b
INNER JOIN
、LEFT/RIGHT OUTER JOIN
的精确算法如下:
从第一个表中取出每一行:a 考虑旁边第二个表中的所有行:(a, b[i]) 针对每一对评估 ON ... 子句:ON(a, b[i] ) = true/false ?当条件计算为真时,返回该组合行 (a, b[i])。当到达第二个表的末尾而没有任何匹配时,这是一个外连接,然后使用 Null 为其他表的所有列返回一个(虚拟)对:(a, Null) 用于 LEFT 外连接或 (Null, b) 用于 RIGHT 外连接加入。这是为了确保第一个表的所有行都存在于最终结果中。
注意: ON
子句中指定的条件可以是任何内容,不需要使用主键(并且您不需要总是引用两者中的列表)!例如:
... ON T1.title = T2.title AND T1.version < T2.version (=> 将此帖子视为示例用法:仅选择列上具有最大值的行)
... ON T1.y 为空
... ON 1 = 0(就像示例一样)
https://i.stack.imgur.com/0dWzY.png
https://i.stack.imgur.com/Bsocp.png
注意:左连接 = 左外连接,右连接 = 右外连接。
最简单的定义
内连接:从两个表中返回匹配的记录。
完全外连接:返回两个表中匹配和不匹配的记录,对于两个表中的不匹配记录返回 null。
左外连接:仅从左侧的表中返回匹配和不匹配的记录。
右外连接:仅从右侧的表中返回匹配和不匹配的记录。
简而言之
匹配 + 左不匹配 + 右不匹配 = 完全外连接
匹配 + 左不匹配 = 左外连接
匹配 + 右不匹配 = 右外连接
匹配 = 内连接
总体思路
请参阅 Martin Smith 的 answer,以更好地说明和解释不同的连接,尤其是 FULL OUTER JOIN
、RIGHT OUTER JOIN
和 LEFT OUTER JOIN
之间的区别。
这两个表构成了以下 JOIN
表示的基础:
https://i.stack.imgur.com/DEAAy.png
交叉连接
https://i.stack.imgur.com/EqnB9.png
SELECT *
FROM citizen
CROSS JOIN postalcode
结果将是所有组合的笛卡尔积。不需要 JOIN
条件:
https://i.stack.imgur.com/eeFIB.png
内部联接
INNER JOIN
与简单的相同:JOIN
https://i.stack.imgur.com/oXqs0.png
SELECT *
FROM citizen c
JOIN postalcode p ON c.postal = p.postal
结果将是满足所需 JOIN
条件的组合:
https://i.stack.imgur.com/aaNvi.png
左外连接
LEFT OUTER JOIN
与 LEFT JOIN
相同
https://i.stack.imgur.com/qmy29.png
SELECT *
FROM citizen c
LEFT JOIN postalcode p ON c.postal = p.postal
结果将是 citizen
中的所有内容,即使 postalcode
中没有匹配项。再次需要一个 JOIN
条件:
https://i.stack.imgur.com/mbaqJ.png
播放数据
所有示例都在 Oracle 18c 上运行。它们可在 dbfiddle.uk 获得,这也是表格屏幕截图的来源。
CREATE TABLE citizen (id NUMBER,
name VARCHAR2(20),
postal NUMBER, -- <-- could do with a redesign to postalcode.id instead.
leader NUMBER);
CREATE TABLE postalcode (id NUMBER,
postal NUMBER,
city VARCHAR2(20),
area VARCHAR2(20));
INSERT INTO citizen (id, name, postal, leader)
SELECT 1, 'Smith', 2200, null FROM DUAL
UNION SELECT 2, 'Green', 31006, 1 FROM DUAL
UNION SELECT 3, 'Jensen', 623, 1 FROM DUAL;
INSERT INTO postalcode (id, postal, city, area)
SELECT 1, 2200, 'BigCity', 'Geancy' FROM DUAL
UNION SELECT 2, 31006, 'SmallTown', 'Snizkim' FROM DUAL
UNION SELECT 3, 31006, 'Settlement', 'Moon' FROM DUAL -- <-- Uuh-uhh.
UNION SELECT 4, 78567390, 'LookoutTowerX89', 'Space' FROM DUAL;
使用 JOIN 和 WHERE 时边界模糊
交叉连接
CROSS JOIN
产生作为 The General Idea/INNER JOIN
的行:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.postal = p.postal -- < -- The WHERE condition is limiting the resulting rows
使用 CROSS JOIN
获取 LEFT OUTER JOIN
的结果需要一些技巧,例如添加 NULL
行。它被省略了。
内部联接
INNER JOIN
成为笛卡尔积。与 The General Idea/CROSS JOIN
相同:
SELECT *
FROM citizen c
JOIN postalcode p ON 1 = 1 -- < -- The ON condition makes it a CROSS JOIN
这就是内部连接可以真正被视为交叉连接的地方,其结果与删除的条件不匹配。这里没有任何结果行被删除。
使用 INNER JOIN
获得 LEFT OUTER JOIN
的结果也需要技巧。它被省略了。
左外连接
LEFT JOIN
产生的行为 The General Idea/CROSS JOIN
:
SELECT *
FROM citizen c
LEFT JOIN postalcode p ON 1 = 1 -- < -- The ON condition makes it a CROSS JOIN
LEFT JOIN
产生的行为 The General Idea/INNER JOIN
:
SELECT *
FROM citizen c
LEFT JOIN postalcode p ON c.postal = p.postal
WHERE p.postal IS NOT NULL -- < -- removed the row where there's no mathcing result from postalcode
维恩图的问题
在“sql join cross inner external”上的图像互联网搜索将显示大量维恩图。我曾经在我的桌子上放了一份印刷版。但表示存在问题。
维恩图非常适合集合论,其中一个元素可以在一个或两个集合中。但是对于数据库,在我看来,一个“集合”中的元素似乎是表中的一行,因此也不存在于任何其他表中。多个表中不存在一行。一行对表来说是唯一的。
自联接是一种极端情况,其中每个元素实际上在两组中都是相同的。但它仍然没有解决以下任何问题。
在下面的讨论中,集合 A
表示左侧的集合(citizen
表),集合 B
是右侧的集合(postalcode
表)。
交叉连接
两个集合中的每个元素都与另一个集合中的每个元素匹配,这意味着我们需要每个 B
个元素的 A
个数量和每个 A
个元素的 B
个数量来正确表示这个笛卡尔积。集合论不适用于集合中的多个相同元素,因此我发现维恩图正确表示它是不切实际/不可能的。 UNION
似乎根本不适合。
行是不同的。 UNION
共有 7 行。但它们与常见的 SQL
结果集不兼容。这根本不是 CROSS JOIN
的工作方式:
https://i.stack.imgur.com/MqN6E.png
试图像这样表示它:
https://i.stack.imgur.com/1BhEE.png
..但现在它看起来就像一个 INTERSECTION
,它肯定 不是。此外,INTERSECTION
中没有任何元素实际上位于两个不同集合中的任何一个中。但是,它看起来很像类似这样的可搜索结果:
https://i.stack.imgur.com/syxYr.png
作为参考,可以在 Tutorialgateway 中看到 CROSS JOIN
的一个可搜索结果。 INTERSECTION
,就像这个一样,是空的。
内部联接
元素的值取决于 JOIN
条件。在每一行都对该条件唯一的条件下表示这一点是可能的。含义 id=x
仅适用于 one 行。一旦表 A
(citizen
) 中的一行在 JOIN
条件下匹配表 B
(postalcode
) 中的多行,结果与 CROSS JOIN
存在相同的问题:该行需要多次表示,而集合论并不是为此而生的。在唯一性条件下,图表可以工作,但请记住,JOIN
条件决定了元素在图表中的位置。仅查看 JOIN
条件的值以及该行的其余部分以进行乘车:
https://i.stack.imgur.com/djpSE.png
当使用带有 ON 1 = 1
条件的 INNER JOIN
使其成为 CROSS JOIN
时,此表示完全崩溃。
使用 self-JOIN
,行实际上是两个表中的相同元素,但将表同时表示为 A
和 B
不是很合适。例如,使 A
中的元素与 B 中的 不同 元素匹配的常见 self-JOIN
条件是 ON A.parent = B.child
,使得从 A
匹配到 B
单独的元素。从像这样的 SQL
示例中:
SELECT *
FROM citizen c1
JOIN citizen c2 ON c1.id = c2.leader
https://i.stack.imgur.com/p5r7D.png
这意味着史密斯是格林和詹森的领袖。
外连接
当一行与另一表中的行有多个匹配时,麻烦再次开始。这更加复杂,因为 OUTER JOIN
可以被认为是匹配空集。但是在集合论中,任何集合 C
和一个空集的并集总是只是 C
。空集什么也没增加。这个 LEFT OUTER JOIN
的表示通常只是显示所有 A
来说明无论是否有来自 B
的匹配,都会选择 A
中的行。然而,“匹配元素”具有与上图相同的问题。它们取决于条件。空集似乎已经徘徊到 A
:
https://i.stack.imgur.com/Q2yFW.png
WHERE 子句 - 有意义
在月球上查找带有 Smith 和邮政编码的 CROSS JOIN
中的所有行:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
AND p.area = 'Moon';
https://i.stack.imgur.com/Iy4Z3.png
现在不使用维恩图来反映 JOIN
。它仅用于 WHERE
子句:
https://i.stack.imgur.com/fmxEr.png
..这是有道理的。
当 INTERSECT 和 UNION 有意义时
相交
如前所述,INNER JOIN
并不是真正的 INTERSECT
。但是 INTERSECT
可以用于单独查询的结果。这里的维恩图很有意义,因为来自单独查询的元素实际上是属于一个结果或两者的行。相交显然只会返回两个查询中都存在该行的结果。此 SQL
将产生与上述 WHERE
相同的行,维恩图也将相同:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
INTERSECT
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE p.area = 'Moon';
联盟
OUTER JOIN
不是 UNION
。但是 UNION
在与 INTERSECT
相同的条件下工作,导致返回结合两个 SELECT
的所有结果:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
UNION
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE p.area = 'Moon';
这相当于:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
OR p.area = 'Moon';
..并给出结果:
https://i.stack.imgur.com/FTPRr.png
这里的维恩图也很有意义:
https://i.stack.imgur.com/GW69a.png
不适用时
一个重要的注意事项是,这些仅在两个 SELECT 的结果结构相同时才有效,从而可以进行比较或联合。这两个结果将无法实现:
SELECT *
FROM citizen
WHERE name = 'Smith'
SELECT *
FROM postalcode
WHERE area = 'Moon';
..尝试将结果与 UNION
结合起来
ORA-01790: expression must have same datatype as corresponding expression
如需更多兴趣,请阅读 Say NO to Venn Diagrams When Explaining JOINs 和 sql joins as venn diagram。两者都涵盖EXCEPT
。
简单来说,
1.INNER JOIN OR EQUI JOIN :返回只匹配两个表中的条件的结果集。
2.OUTER JOIN:返回两个表中所有值的结果集,即使条件匹配与否。
3.LEFT JOIN :返回左表中所有值的结果集,并且只返回与右表中的条件匹配的行。
4.RIGHT JOIN :返回右表中所有值的结果集,并且只返回与左表中的条件匹配的行。
5.FULL JOIN:完全连接和完全外连接是一样的。
left join on
返回由 null
扩展的 inner join on
行 union all
不匹配的左表行。
right join on
返回由 null
扩展的 inner join on
行 union all
不匹配的右表行。
full join on
返回 inner join on
行union all
由 null
s 扩展的不匹配的左表行 union all
由 null
s 扩展的不匹配的右表行。
outer
是可选的没有效果。
(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1、一般规则 1 b、3 c & d、5 b。)
因此,在您知道所涉及的基础 inner join on
之前,不要outer join on
。
找出行 inner join on
返回的内容:CROSS JOIN vs INNER JOIN in SQL
这也解释了为什么维恩(类)图对内连接和外连接没有帮助。要详细了解为什么它们通常对连接没有帮助:Venn Diagram for Natural Join
内连接 - 使用任一等效查询的内连接给出了两个表的交集,即它们共有的两行。
左外连接 - 左外连接将给出 A 中的所有行,以及 B 中的任何公共行。
全外连接 - 全外连接将为您提供 A 和 B 的并集,即 A 中的所有行和 B 中的所有行。如果 A 中的某些内容在 B 中没有对应的数据,则 B 部分是为空,反之亦然
Join is not an intersection unless the tables have the same columns
不。您可以加入任何您想要的列,如果值匹配,它们将连接在一起。
1.Inner Join:也称为Join。它仅在匹配时返回左表和右表中存在的行。否则,它返回零记录。
例子:
SELECT
e1.emp_name,
e2.emp_salary
FROM emp1 e1
INNER JOIN emp2 e2
ON e1.emp_id = e2.emp_id
https://i.stack.imgur.com/lWzlz.jpg
2.完全外连接:也称为完全连接。它返回左表和右表中存在的所有行。
例子:
SELECT
e1.emp_name,
e2.emp_salary
FROM emp1 e1
FULL OUTER JOIN emp2 e2
ON e1.emp_id = e2.emp_id
https://i.stack.imgur.com/3yY0Y.jpg
3.Left Outer join:或者简称为Left Join。它返回左表中存在的所有行以及右表中的匹配行(如果有)。
4.右外连接:也称为右连接。它从左表(如果有)返回匹配的行,以及右表中存在的所有行。
https://i.stack.imgur.com/UIVk7.png
连接的优点
执行速度更快。
通过一个示例可以更轻松地解释连接:
https://i.stack.imgur.com/DxeqP.png
要模拟存储在不同表中的人员和电子邮件,
表 A 和表 B 由 Table_A.id = Table_B.name_id 连接
内部联接
https://i.stack.imgur.com/l4sbv.png
仅显示匹配的 ID 行。
外连接
https://i.stack.imgur.com/gL8gT.png
显示了表 A 的匹配 ID 和不匹配行。
https://i.stack.imgur.com/kXk4J.png
显示了表 B 的匹配 ID 和不匹配行。
https://i.stack.imgur.com/736nT.png
注意:完全外连接在 MySQL 上不可用
考虑以下 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 查询中只写为 JOIN。它只返回表之间的匹配记录。
找出所有员工及其部门名称:
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
不是从输出中的 EMP 打印的,因为它的 dept_id 6
在 Department 表中找不到匹配项。同样,HR
和 R&D
行不会从 Department 表中打印出来,因为它们在 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
因此,如果您观察上述输出,则 LEFT 表 (Emp) 中的所有记录都将与 RIGHT 表中的匹配记录一起打印。
HR
和 R&D
行未从 Department 表中打印,因为它们未在 dept_id 的 Emp 表中找到匹配项。
因此,LEFT JOIN 返回左表中的所有行,并且只返回右表中的匹配行。
也可以查看 DEMO here。
inner join
和 outer join
的区别如下:
内连接是基于匹配元组组合表的连接,而外连接是基于匹配和不匹配元组组合表的连接。内连接合并两个表中匹配的行,其中不匹配的行被省略,而外连接合并两个表的行,不匹配的行用空值填充。内连接类似于交集操作,而外连接类似于联合操作。内连接是两种类型,而外连接是三种类型。外连接比内连接快。
这里有很多很好的答案以及非常准确的 relational algebra 示例。这是一个非常简化的答案,可能对遇到 SQL 编码困境的业余或新手编码员有所帮助。
基本上,JOIN
查询通常归结为两种情况:
对于 A
数据子集的 SELECT
:
当您要查找的相关 B 数据必须在每个数据库设计中存在时,请使用 INNER JOIN;
当您正在寻找的相关 B 数据可能或可能不存在每个数据库设计时,请使用 LEFT JOIN。
一个示范
设置
跳到 psql
并创建一个包含猫和人类的小型数据库。您可以复制粘贴整个部分。
CREATE DATABASE catdb;
\c catdb;
\pset null '[NULL]' -- how to display null values
CREATE TABLE humans (
name text primary key
);
CREATE TABLE cats (
human_name text references humans(name),
name text
);
INSERT INTO humans (name)
VALUES ('Abe'), ('Ann'), ('Ben'), ('Jen');
INSERT INTO cats (human_name, name)
VALUES
('Abe', 'Axel'),
(NULL, 'Bitty'),
('Jen', 'Jellybean'),
('Jen', 'Juniper');
查询
这是我们将多次运行的查询,将 [SOMETHING JOIN]
更改为各种类型以查看结果。
SELECT
humans.name AS human_name,
cats.name AS cat_name
FROM humans
[SOMETHING JOIN] cats ON humans.name = cats.human_name
ORDER BY humans.name;
INNER JOIN
返回所有人猫对。任何没有猫的人或没有人的猫都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Jen | Jellybean
Jen | Juniper
FULL OUTER JOIN
返回所有人类和所有猫,如果两边都没有匹配项,则返回 NULL
。
human_name | cat_name
------------+-----------
Abe | Axel
Ann | [NULL]
Ben | [NULL]
Jen | Jellybean
Jen | Juniper
[NULL] | Bitty
LEFT OUTER JOIN
返回所有人类(左表)。任何没有猫的人都会在 cat_name
列中获得 NULL
。任何没有人的猫都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Ann | [NULL]
Ben | [NULL]
Jen | Jellybean
Jen | Juniper
RIGHT OUTER JOIN
返回所有猫(右表)。任何没有人类的猫都会在 human_name
列中获得 NULL
。任何没有猫的人都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Jen | Jellybean
Jen | Juniper
[NULL] | Bitty
内部与外部
您可以看到,虽然 INNER JOIN
只获得匹配对,但每种 OUTER
连接都包含一些没有匹配项的项目。
但是,实际的词 INNER
和 OUTER
不需要出现在查询中:
JOIN 本身意味着 INNER
LEFT JOIN、RIGHT JOIN 和 OUTER JOIN 都暗示 OUTER
内连接VS。外连接
在 SQL 中,join 用于比较和组合——字面意思是 join——并从数据库中的两个或多个表中返回特定的数据行。 INNER JOIN
从表中查找并返回匹配数据,而 OUTER JOIN
从表中查找并返回匹配数据和一些不同的数据。
内部联接
INNER JOIN
侧重于 2 个表之间的相似性。使用 INNER JOIN
时,要比较的两个(或多个)表之间必须至少有一些匹配的数据。 INNER JOIN
搜索表以查找匹配或重叠的数据。如果找到,INNER JOIN
会组合信息并将其返回到新表中。
例子
让我们考虑一个有两个表的常见场景:产品价格和数量。两个表中的共同信息是产品名称,所以这是连接表的逻辑列。有些产品在两个表中是相同的;其他人对一个人来说是独一无二的,并且在另一个人中没有匹配项。
产品 INNER JOIN
仅返回两个表共有的产品信息。
我整理了一些 HTML 和 CSS 的交互,以便您可以直观地想象整个事情。
/* CSS 样式 */ .circles { display: flex; } .circle { 高度:100px;宽度:100px;边界半径:50%;背景:#99AEBA; } .circle:first-child { 背景:#FF0000; } .circle:nth-child(2) { 背景:#05E156;变换:translateX(-45px); z指数:2;混合混合模式:乘法; }
此处红色圆圈描述您的 PRICES
,绿色圆圈描述您的 QUANTITIES
。
价格 数量
P.产品 P.价格 Q.产品 Q.数量 A 6€ A 92 B 5€ B 27 C 5€ D 66 D 1€ E 20
如果您现在想要一个确定交集的 INNER JOIN
,您可以编写以下查询:
SELECT Prices.*, Quantities.Quantity
FROM Prices INNER JOIN Quantities
ON Prices.Product = Quantities.Product;
作为交叉点,您现在从图中获得黑色区域,并得出以下查询结果:
产品 价格 数量 A 6€ 92 B 5€ 27 D 1€ 66
外连接
OUTER JOIN
返回一组记录(或行),其中包含 INNER JOIN
将返回的内容,但还包含在另一个表中未找到对应匹配项的其他行。
外连接分为三种类型:
左外连接(或左连接)
右外连接(或右连接)
FULL OUTER JOIN(或完全连接)
这些 OUTER JOIN
中的每一个都指的是被比较、组合和返回的数据部分。有时会在此过程中生成零,因为某些数据是共享的,而另一些则不是。
但是,这种描述现在超出了问题的范围。
我的资料来源:https://www.diffen.com/difference/Inner_Join_vs_Outer_Join https://www.freecodecamp.org/news/sql-join-types-inner-join-vs-outer-join-example/#:~: text=%20最大%20差异%20%20an,table%20in%20the%20resulting%20table。 https://towardsdatascience.com/what-is-the-difference-between-an-inner-and-an-outer-join-in-sql-5b5ec8277377