又如何LEFT JOIN
,RIGHT JOIN
又如何FULL JOIN
适应?
27 回答
假设您要加入没有重复的列,这是一种非常常见的情况:
例子
假设您有两个表,每个表都有一个列,数据如下:
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
(AKA 笛卡尔积)开始。这没有ON
子句,只返回两个表中的每个行组合。
从交叉连接 B 中选择 A.Colour、B.Colour
内连接和外连接有一个“ON”子句谓词。
- 内部联接。评估交叉连接结果中所有行的“ON”子句中的条件。如果为 true,则返回连接的行。否则丢弃它。
- 左外连接。与内连接相同,然后对于左表中与任何不匹配的任何行输出这些与右表列的 NULL 值。
- 右外连接。与内连接相同,然后对于右表中与任何不匹配的任何行输出这些与左表列的 NULL 值。
- 全外连接。与内连接相同,然后保留左外连接中的左不匹配行和右外连接中的右非匹配行。
一些例子
从 A.Colour 上的 A INNER JOIN B 中选择 A.Colour、B.Colour = B.Colour
以上是经典的 equi join。
动画版
SELECT A.Colour, B.Colour 从 A.Colour NOT IN ('Green','Blue') 上的 A INNER JOIN B
内连接条件不一定是相等条件,也不需要引用两个(甚至任何一个)表中的列。评估A.Colour NOT IN ('Green','Blue')
交叉连接返回的每一行。
SELECT A.Colour, B.Colour 从 A INNER JOIN B ON 1 =1
对于交叉连接结果中的所有行,连接条件的计算结果为真,因此这与交叉连接相同。16行的图我就不再赘述了。
从 A.Colour 的左外连接 B 中选择 A.Colour、B.Colour = B.Colour
外部联接的逻辑评估方式与内部联接相同,只是如果左表中的一行(用于左联接)根本不与右侧表中的任何行联接,则它会保留在结果中,并NULL
带有右手列。
从 A.Colour = B.Colour 的左外连接 B 中选择 A.Colour,B.Colour,其中 B.Colour 为 NULL
这只是将先前的结果限制为仅返回B.Colour IS NULL
. 在这种特殊情况下,这些将是保留的行,因为它们在右侧 table 中没有匹配,并且查询返回 table 中不匹配的单个红色行B
。这称为反半连接。
为IS NULL
测试选择一个不可为空的列或连接条件可确保NULL
排除任何值以使此模式正常工作并避免仅带回恰好具有该NULL
值的行的测试非常重要除了不匹配的行之外的列。
从 A.Colour = B.Colour 的右外连接 B 中选择 A.Colour、B.Colour
右外连接的作用类似于左外连接,除了它们保留右表中不匹配的行并且空扩展左列。
从 A.Colour = B.Colour 的完整外部连接 B 中选择 A.Colour、B.Colour
全外连接结合了左连接和右连接的行为,并保留左右表中不匹配的行。
从 1 = 0 的完整外部连接 B 中选择 A.Colour、B.Colour
交叉连接中没有与1=0
谓词匹配的行。两侧的所有行都使用正常的外部连接规则保留,另一侧表的列中为 NULL。
选择 COALESCE(A.Colour, B.Colour) 作为颜色从 FULL OUTER JOIN B ON 1 = 0
对前面的查询稍作修改,就可以模拟UNION ALL
两个表中的一个。
从 A.Colour = B.Colour 的左外连接 B 中选择 A.Colour、B.Colour 其中 B.Colour = 'Green'
请注意,WHERE
子句(如果存在)逻辑上在连接之后运行。一个常见的错误是执行左外连接,然后在右表上包含一个 WHERE 子句,该条件最终排除了不匹配的行。以上最终执行了外部连接......
...然后“Where”子句运行。NULL= 'Green'
不评估为真,因此由外部连接保留的行最终被丢弃(连同蓝色的)有效地将连接转换回内部连接。
如果打算只包括 B 中颜色为绿色的行以及 A 中的所有行,无论正确的语法是
从 A.Colour = B.Colour 和 B.Colour = 'Green' 的左外连接 B 中选择 A.Colour、B.Colour
SQL小提琴
查看这些示例在 SQLFiddle.com 上实时运行。
连接用于组合来自两个表的数据,结果是一个新的临时表。连接是基于称为谓词的东西执行的,谓词指定了用于执行连接的条件。内连接和外连接之间的区别在于,内连接将仅返回基于连接谓词实际匹配的行。例如,让我们考虑 Employee 和 Location 表:
员工
企业标识 | 企业名称 |
---|---|
13 | 杰森 |
8 | 亚历克斯 |
3 | 内存 |
17 | 巴布 |
25 | 约翰逊 |
地点
企业标识 | EmpLoc |
---|---|
13 | 圣荷西 |
8 | 洛杉矶 |
3 | 印度浦那 |
17 | 印度钦奈 |
39 | 印度班加罗尔 |
内连接:-内连接通过基于连接谓词 组合两个表(员工和位置)的列值来创建一个新的结果表。该查询将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 的结果如下所示:
员工.EmpId | 员工.EmpName | 位置.EmpId | 位置.EmpLoc |
---|---|---|---|
13 | 杰森 | 13 | 圣荷西 |
8 | 亚历克斯 | 8 | 洛杉矶 |
3 | 内存 | 3 | 印度浦那 |
17 | 巴布 | 17 | 印度钦奈 |
外连接: 外连接不需要两个连接表中的每条记录都有匹配的记录。即使不存在其他匹配记录,连接表也会保留每条记录。外连接进一步细分为左外连接和右外连接,具体取决于保留哪个表的行(左或右)。
左外连接:- 表Employee和Location的左外连接(或简称为左连接)的结果始终包含“左”表( Employee)的所有记录,即使连接条件在“右”表( Location)。下面是左外连接的 SQL 的样子,使用上面的表:
select * from employee left outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
现在,运行此 SQL 的结果如下所示:
员工.EmpId | 员工.EmpName | 位置.EmpId | 位置.EmpLoc |
---|---|---|---|
13 | 杰森 | 13 | 圣荷西 |
8 | 亚历克斯 | 8 | 洛杉矶 |
3 | 内存 | 3 | 印度浦那 |
17 | 巴布 | 17 | 印度钦奈 |
25 | 约翰逊 | 空值 | 空值 |
请注意,虽然 Johnson 在员工位置表中没有条目,但他仍然包含在结果中,但位置字段为空。
右外连接:- 右外连接(或右连接)与左外连接非常相似,但表的处理方式相反。“右”表 ( Location ) 中的每一行将至少出现在连接表中一次。如果“左”表 ( Employee ) 中不存在匹配的行,则 NULL 将出现在Employee的列中,用于那些在Location中不匹配的记录。这就是 SQL 的样子:
select * from employee right outer join location on employee.empID = location.empID;
//Use of outer keyword is optional
使用上面的表格,我们可以显示右外连接的结果集是什么样的:
员工.EmpId | 员工.EmpName | 位置.EmpId | 位置.EmpLoc |
---|---|---|---|
13 | 杰森 | 13 | 圣荷西 |
8 | 亚历克斯 | 8 | 洛杉矶 |
3 | 内存 | 3 | 印度浦那 |
17 | 巴布 | 17 | 印度钦奈 |
空值 | 空值 | 39 | 印度班加罗尔 |
请注意,虽然没有列出在班加罗尔工作的员工,但它仍然包含在结果中,员工字段为空。
完全外连接:- 完全外连接或完全连接是通过在连接结果中包含不匹配的行来保留不匹配的信息,使用完全外连接。它包括两个表中的所有行,无论另一个表是否具有匹配值。
员工.EmpId | 员工.EmpName | 位置.EmpId | 位置.EmpLoc |
---|---|---|---|
13 | 杰森 | 13 | 圣荷西 |
8 | 亚历克斯 | 8 | 洛杉矶 |
3 | 内存 | 3 | 印度浦那 |
17 | 巴布 | 17 | 印度钦奈 |
25 | 约翰逊 | 空值 | 空值 |
空值 | 空值 | 39 | 印度班加罗尔 |
内部联接
仅检索匹配的行,即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
参考
简单来说:
内连接只检索匹配的行。
而外连接从一个表中检索匹配的行以及其他表中的所有行......结果取决于您使用的是哪一个:
Left:右表中的匹配行和左表中的所有行
右:左表中的匹配行和右表中的所有行或
完整:所有表中的所有行。有没有匹配都没关系
内连接仅在连接的另一(右侧)存在匹配记录时才显示行。
(左)外部联接在左侧显示每条记录的行,即使在联接的另一(右侧)侧没有匹配的行。如果没有匹配的行,另一侧(右侧)的列将显示 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 记录。
您可以使用它来查找没有任何 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.
}
}
右连接:与左连接完全相反。将表名放入右连接右侧的左连接中,您将获得与左连接相同的输出。
外连接:显示两个表中的所有记录No matter what
。如果左表中的记录根据Primary,Forieign key与右表不匹配,则使用NULL值作为连接结果。
例子 :
现在让我们假设 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;
从图形上看,它看起来像:
您用于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
不重复的组合。
答案在于每一个的含义,所以在结果中。
注意:里面没有or
。 而且里面也没有。SQLite
RIGHT OUTER JOIN
FULL OUTER JOIN
MySQL
FULL OUTER JOIN
我的回答是基于上面的Note。
当您有两个这样的表时:
--[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
好吧,根据您的需要,您可以选择满足您需要的每一个;)。
内部联接。
连接是组合两个表中的行。内连接尝试根据您在查询中指定的条件匹配两个表,并且只返回匹配的行。如果连接中第一个表中的一行与第二个表中的两行匹配,则结果中将返回两行。如果第一个表中有一行与第二个表中的行不匹配,则不返回;同样,如果第二个表中有一行与第一个表中的行不匹配,则不会返回。
外连接。
左连接尝试将第一个表中的行与第二个表中的行匹配。如果找不到匹配项,它将返回第一个表中的列,并将第二个表中的列留空(null)。
INNER JOIN
两个或多个表的最典型连接。它返回表 ON primarykey 和 forignkey 关系上的数据匹配。OUTER JOIN
与 相同INNER JOIN
,但它还包括NULL
ResultSet 上的数据。LEFT JOIN
=INNER JOIN
+左表不匹配数据与Null
右表匹配。RIGHT JOIN
=INNER JOIN
+右表不匹配的数据与Null
左表匹配。FULL JOIN
= +右表和左表INNER JOIN
上的不匹配数据都匹配。Null
- 自联接不是 SQL 中的关键字,当表本身引用数据时称为自联接。使用
INNER JOIN
andOUTER 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
我在其他答案中看不到有关性能和优化器的太多细节。
有时很高兴知道 onlyINNER 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)
RIGHT 外连接。这是为了确保第一个表的所有行都存在于最终结果中。
- 当条件计算为 时
注意:子句中指定的条件ON
可以是任何东西,不需要使用主键(并且您不需要总是引用两个表中的列)!例如:
... ON T1.title = T2.title AND T1.version < T2.version
(=>将此帖子作为示例用法:仅选择列上具有最大值的行)... ON T1.y IS NULL
... ON 1 = 0
(就像样本一样)
注意:左连接 = 左外连接,右连接 = 右外连接。
最简单的定义
内连接:从两个表中返回匹配的记录。
Full Outer Join:返回两个表中匹配和不匹配的记录,对于来自Both Tables的不匹配记录返回 null 。
Left Outer Join:仅从Left Side的表中返回匹配和不匹配的记录。
右外连接:仅从右侧的表中返回匹配和不匹配的记录。
简而言之
匹配 + 左不匹配 + 右不匹配 =完全外连接
匹配 + 左不匹配 =左外连接
匹配 + 右不匹配 =右外连接
匹配 =内连接
总体思路
请参阅Martin Smith的答案,以更好地说明和解释不同的连接,包括 , 和 之间的差异,尤其是差异。FULL OUTER JOIN
RIGHT OUTER JOIN
LEFT OUTER JOIN
这两个表构成了以下JOIN
s 表示的基础:
交叉连接
SELECT *
FROM citizen
CROSS JOIN postalcode
结果将是所有组合的笛卡尔积。无需JOIN
条件:
内部联接
INNER JOIN
与简单相同:JOIN
SELECT *
FROM citizen c
JOIN postalcode p ON c.postal = p.postal
结果将是满足所需JOIN
条件的组合:
左外连接
LEFT OUTER JOIN
是相同的LEFT JOIN
SELECT *
FROM citizen c
LEFT JOIN postalcode p ON c.postal = p.postal
结果将是所有内容,citizen
即使 中没有匹配项postalcode
。再次需要一个JOIN
条件:
播放数据
所有示例都在 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
来获得 a 的结果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
来获得 a 的结果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
表格)。
交叉连接
两个集合中的每个元素都与另一个集合中的每个元素匹配,这意味着我们需要A
每个B
元素B
的数量和每个A
元素的数量来正确表示这个笛卡尔积。集合论不适用于集合中的多个相同元素,因此我发现维恩图正确表示它是不切实际/不可能的。好像一点都UNION
不合适。
行是不同的。总共是 7行UNION
。但是它们对于一个共同的SQL
结果集是不兼容的。这根本不是 a 的CROSS JOIN
工作方式:
试图像这样表示它:
..但现在它看起来像一个INTERSECTION
,当然不是。INTERSECTION
此外,实际上在两个不同集合中的任何一个中都没有元素。但是,它看起来很像类似这样的可搜索结果:
CROSS JOIN
作为参考,可以在Tutorialgateway中查看 s 的一个可搜索结果。INTERSECTION
就像这个一样,是空的。
内部联接
元素的值取决于JOIN
条件。在每一行都对该条件唯一的条件下表示这一点是可能的。含义id=x
仅适用于一行。一旦 table() 中的一行在条件下与 table ( )A
中的citizen
多行匹配,结果与 有相同的问题:该行需要多次表示,而集合论并不是为此而制定的。在唯一性的条件下,图表可以工作,但请记住,条件决定了图表中元素的位置。仅查看该行的其余部分的条件值,以进行骑行:B
postalcode
JOIN
CROSS JOIN
JOIN
JOIN
INNER JOIN
当使用带有ON 1 = 1
将其变为 的条件时,此表示完全崩溃CROSS JOIN
。
使用 self- JOIN
,这些行实际上是两个表中的相同元素,但是将表表示为两者A
并且B
不是很合适。例如JOIN
,使元素 in与B 中的不同A
元素匹配的常见自条件是,在单独的元素上匹配 from到。从这样的例子中:ON A.parent = B.child
A
B
SQL
SELECT *
FROM citizen c1
JOIN citizen c2 ON c1.id = c2.leader
这意味着史密斯是格林和詹森的领袖。
外连接
当一行与另一表中的行有多个匹配时,麻烦再次开始。这更加复杂,因为OUTER JOIN
可以匹配空集。但在集合论中,任何集合C
与空集的并集总是公正的C
。空集什么也没增加。这种表示LEFT OUTER JOIN
通常只是显示所有的,A
以说明A
无论是否存在匹配,都会选择 in 中的行 from B
。然而,“匹配元素”具有与上图相同的问题。它们取决于条件。空集似乎已经徘徊到A
:
WHERE 子句 - 有意义
CROSS JOIN
从 a with Smith 和月球上的邮政编码中查找所有行:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
AND p.area = 'Moon';
现在维恩图不用于反映JOIN
. 它仅用于WHERE
子句:
..这是有道理的。
当 INTERSECT 和 UNION 有意义时
相交
正如解释的那样, anINNER JOIN
并不是真正的INTERSECT
. 但是INTERSECT
s 可以用于单独查询的结果。这里的维恩图很有意义,因为来自单独查询的元素实际上是属于一个结果或两者的行。相交显然只会返回两个查询中都存在该行的结果。这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
s 的所有结果:
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';
..并给出结果:
这里的维恩图也很有意义:
不适用时
一个重要的注意事项是,这些仅在两个 SELECT 的结果结构相同时才有效,从而可以进行比较或联合。这两个结果将无法实现:
SELECT *
FROM citizen
WHERE name = 'Smith'
SELECT *
FROM postalcode
WHERE area = 'Moon';
..尝试将结果UNION
与
ORA-01790: expression must have same datatype as corresponding expression
更多兴趣请阅读在解释 JOIN和sql 连接时对维恩图说不作为维恩图。两者也覆盖。EXCEPT
简单来说,
1. INNER JOIN OR EQUI JOIN :返回仅匹配两个表中的条件的结果集。
2. OUTER JOIN :返回两个表中所有值的结果集,无论是否存在条件匹配。
3. LEFT JOIN :返回左表中所有值的结果集,并且只返回与右表中的条件匹配的行。
4. RIGHT JOIN :返回右表中所有值的结果集,并且只返回与左表中的条件匹配的行。
5. FULL JOIN: Full Join和Full outer Join是一样的。
关于这个主题有很多错误信息,包括 Stack Overflow 上的这里。
left join on
(aka left outer join on
) 返回由s扩展的不匹配的左表行inner join on
。union all
null
right join (on
aka right outer join on
) 返回由s扩展的不匹配的右表行inner join on
。union all
null
full join on
(aka full outer join on
) 返回由 s 扩展的不匹配的左表行 由 s 扩展inner join on
的不匹配的右表行。union all
null
union all
null
(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1、一般规则 1 b、3 c & d、5 b。)
因此,outer join
在您知道所inner join
涉及的基础是什么之前,请不要这样做。
找出inner join on
返回的行:
SQL 中的 CROSS JOIN 与 INNER JOIN
这也解释了为什么维恩(类)图对内连接和外连接没有帮助。
有关为什么维恩(类)图对连接没有帮助的更多信息:
自然连接的维恩图
内连接-使用任一等效查询的内连接给出了两个表的交集,即它们共有的两行。
左外连接-左外连接将给出 A 中的所有行,以及 B 中的任何公共行。
全外连接-全外连接将为您提供 A 和 B 的并集,即 A 中的所有行和 B 中的所有行。如果 A 中的某些内容在 B 中没有对应的数据,则 B 部分是为空,反之亦然
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.右外连接:也称为右连接。它从左表(如果有)返回匹配的行,以及右表中存在的所有行。
连接的优点
- 执行速度更快。
inner join
和的区别outer join
如下:
Inner join
是基于匹配元组组合表outer join
的连接,而是基于匹配和不匹配元组组合表的连接。Inner join
合并两个表中的匹配行,其中不匹配的行被省略,而outer join
合并两个表中的行,不匹配的行填充空值。Inner join
类似于交集运算,而outer join
类似于联合运算。Inner join
是两种类型,而outer join
是三种类型。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 查询中只写为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
正如您在上面看到的,不会在输出中从EMPJose
打印,因为它的 dept_id在 Department 表中找不到匹配项。同样,不会从Department表中打印行,因为它们在 Emp 表中没有找到匹配项。6
HR
R&D
因此,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 。
这里有很多很好的答案,其中包含非常准确的关系代数示例。这是一个非常简化的答案,可能对遇到 SQL 编码困境的业余或新手编码员有所帮助。
基本上,JOIN
查询通常归结为两种情况:
对于一个数据SELECT
子集A
:
INNER JOIN
当您要查找的相关B
数据必须在每个数据库设计中存在时使用;- 当您正在寻找
LEFT JOIN
的相关数据可能或可能不存在每个数据库设计时使用。B
一个示范
设置
进入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;
AnINNER JOIN
返回所有人猫对。任何没有猫的人或没有人的猫都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Jen | Jellybean
Jen | Juniper
AFULL OUTER JOIN
返回所有人类和所有猫,NULL
如果两边都没有匹配项。
human_name | cat_name
------------+-----------
Abe | Axel
Ann | [NULL]
Ben | [NULL]
Jen | Jellybean
Jen | Juniper
[NULL] | Bitty
ALEFT OUTER JOIN
返回所有人类(左表)。任何没有猫的人都会NULL
在cat_name
列中得到一个。任何没有人的猫都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Ann | [NULL]
Ben | [NULL]
Jen | Jellybean
Jen | Juniper
ARIGHT OUTER JOIN
返回所有猫(右表)。任何没有人类的猫都会NULL
在human_name
列中获得一个。任何没有猫的人都被排除在外。
human_name | cat_name
------------+-----------
Abe | Axel
Jen | Jellybean
Jen | Juniper
[NULL] | Bitty
内部与外部
您可以看到虽然 anINNER JOIN
只获取匹配的对,但每种OUTER
连接都包含一些没有匹配的项目。
但是,实际单词INNER
并不OUTER
需要出现在查询中:
JOIN
本身就意味着INNER
LEFT JOIN
,RIGHT JOIN
并且OUTER JOIN
都暗示OUTER