[英]What is the difference between "INNER JOIN" and "OUTER JOIN"?
另外, LEFT OUTER JOIN
、 RIGHT OUTER JOIN
和FULL OUTER JOIN
如何适应?
假设您要加入没有重复的列,这是一种非常常见的情况:
例子
假设您有两个表,每个表都有一个列,数据如下:
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
子句,保留谓词评估为true
的行(注意:在实践中,查询优化器可能会找到比上述纯逻辑描述更有效的查询执行方式,但最终结果必须相同)
我将从完整外部连接的动画版本开始。 进一步的解释如下。
源表
首先从CROSS JOIN
(又名笛卡尔积)开始。 这没有ON
子句,只是返回两个表中的每个行组合。
从交叉连接 B 中选择 A.Colour、B.Colour
内连接和外连接有一个“ON”子句谓词。
从 A.Colour 上的 A INNER JOIN B 中选择 A.Colour、B.Colour = B.Colour
以上是经典的 equi join。
内连接条件不一定是相等条件,也不需要引用两个(甚至任何一个)表中的列。 在交叉连接返回的每一行上评估A.Colour NOT IN ('Green','Blue')
。
SELECT A.Colour, B.Colour 从 A INNER JOIN B ON 1 =1
对于交叉连接结果中的所有行,连接条件的计算结果为真,因此这与交叉连接相同。 16行的图我就不再赘述了。
外部联接的逻辑评估方式与内部联接相同,只是如果左表中的一行(对于左联接)根本不与右侧表中的任何行联接,则它在结果中保留为NULL
值右侧的列。
这只是将先前的结果限制为仅返回B.Colour IS NULL
的行。 在这种特殊情况下,这些将是保留的行,因为它们在右侧表中不匹配,并且查询返回表B
中不匹配的单个红色行。 这称为反半连接。
为IS NULL
测试选择一个不可为空的列或连接条件确保排除任何NULL
值以使此模式正常工作并避免只带回恰好具有除了不匹配的行之外,该列的NULL
值。
右外连接的作用类似于左外连接,除了它们保留右表中不匹配的行并且空扩展左列。
全外连接结合了左连接和右连接的行为,并保留左右表中不匹配的行。
交叉连接中没有行匹配1=0
谓词。 两侧的所有行都使用正常的外部连接规则保留,另一侧表的列中为 NULL。
对前面的查询稍作修改,就可以模拟两个表的UNION ALL
。
请注意, WHERE
子句(如果存在)逻辑上在连接之后运行。 一个常见的错误是执行左外连接,然后在右表上包含一个 WHERE 子句,该条件最终排除了不匹配的行。 以上最终执行了外部连接......
...然后“Where”子句运行。 NULL= 'Green'
不会评估为真,因此外部连接保留的行最终被丢弃(与蓝色连接一起),有效地将连接转换回内部连接。
如果打算只包括 B 中颜色为绿色的行以及 A 中的所有行,无论正确的语法是
查看这些示例在 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”相关联。
订单
订单详细信息
请求
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 OrderDetails.OrderID IS NULL
之类的 where 子句。
简单来说:
内连接-> 仅从父表和子表中获取公共记录,其中父表的主键与子表中的外键匹配。
左连接->
伪代码
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表是主表,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
的组合,没有重复。
答案在于每一个的含义,所以在结果中。
笔记 :
在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
或仅使用 获得所有这些表数据,
如下所示:
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
:
(对于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
:
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
相同,但它还包括 ResultSet 上的NULL
数据。
LEFT JOIN
= INNER JOIN
+左表不匹配数据与右表Null
匹配。RIGHT JOIN
= INNER JOIN
+ 右表的不匹配数据与左表的Null
匹配。FULL JOIN
= INNER JOIN
+左右表上的不匹配数据与Null
匹配。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
代替不同类型的连接是一个好习惯。 (当然,如果可以考虑预期的结果集。)
关于这种奇怪的关联行为,这里有几个很好的例子和解释:
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
表示的基础:
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
即使在postalcode
中没有匹配项,结果也将是来自citizen
的所有内容。 再次需要一个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
来获得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
的工作方式:
试图像这样表示它:
..但现在它看起来就像一个INTERSECTION
,它肯定不是。 此外, INTERSECTION
中没有任何元素实际上位于两个不同集合中的任何一个中。 但是,它看起来很像类似这样的可搜索结果:
作为参考,可以在Tutorialgateway中看到CROSS JOIN
的一个可搜索结果。 INTERSECTION
就像这个一样,是空的。
元素的值取决于JOIN
条件。 在每一行都对该条件唯一的条件下表示这一点是可能的。 含义id=x
仅适用于一行。 一旦表A
( citizen
)中的一行在JOIN
条件下匹配表B
( postalcode
)中的多行,结果与CROSS JOIN
存在相同的问题:行需要多次表示,而集合论不是真的为此而生。 在唯一性的条件下,图表可以工作,但请记住, JOIN
条件决定了元素在图表中的位置。 仅查看JOIN
条件的值以及该行的其余部分以进行乘车:
当使用带有ON 1 = 1
条件的INNER JOIN
使其成为CROSS JOIN
时,这种表示完全崩溃了。
使用自JOIN
,行实际上是两个表中的相同元素,但将表表示为A
和B
不是很合适。 例如,使A
中的元素与 B 中的不同元素匹配的常见自JOIN
条件是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
中的行,而不管B
是否存在匹配项。 然而,“匹配元素”具有与上图相同的问题。 它们取决于条件。 空集似乎已经徘徊到A
:
在月球上使用 Smith 和邮政编码从CROSS JOIN
中查找所有行:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
AND p.area = 'Moon';
现在维恩图不用于反映JOIN
。 它仅用于WHERE
子句:
..这是有道理的。
正如解释的那样, 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';
..并给出结果:
这里的维恩图也很有意义:
一个重要的注意事项是,这些仅在两个 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是一样的。
left join on
返回行上的inner join on
union all
由null
s 扩展的不匹配的左表行。
right join on
返回行上的inner join on
union all
由null
s 扩展的不匹配的右表行。
full join on
返回行的inner join on
union all
unmatched left table rows selected by null
s union all
unmatched right table rows are extended by 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
联接返回的行: 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
正如您在上面看到的, 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。
这里有很多很好的答案,其中包含非常准确的关系代数示例。 这是一个非常简化的答案,可能对遇到 SQL 编码困境的业余或新手编码员有所帮助。
基本上, JOIN
查询通常归结为两种情况:
对于A
数据子集的SELECT
:
B
数据必须在每个数据库设计中存在时,请使用INNER JOIN
;B
数据可能或可能不存在每个数据库设计时,请使用LEFT JOIN
。还有LEFT JOIN
, RIGHT JOIN
和FULL 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
“外部”和“内部”只是可选元素,您只是在处理两(三)种连接。 内部联接(或仅使用“联接”时的默认值)是一个联接,其中只有符合条件的元素出现在两个表上。
“外”连接与内连接相同,加上左表或右表中不匹配的元素,在另一个表的所有列上添加空值。
全连接是内连接加上左右连接。
总之,如果我们有这样的表 A
身份证 | 列表A | 数据库 |
---|---|---|
1 | 乔恩 | 1 |
2 | 莎拉 | 1 |
3 | 克拉克 | 2 |
4 | 芭比 | NULL |
像这样的表B:
数据库 | 列表B |
---|---|
1 | 康纳 |
2 | 肯特 |
3 | 斯波克 |
内连接:
from tableA join tableB on tableA.idB = tableB.idB
身份证 | 列表A | 数据库 | 列表B |
---|---|---|---|
1 | 乔恩 | 1 | 康纳 |
2 | 莎拉 | 1 | 康纳 |
3 | 克拉克 | 2 | 肯特 |
左外连接:
from tableA left join tableB on tableA.idB = tableB.idB
身份证 | 列表A | 数据库 | 列表B |
---|---|---|---|
1 | 乔恩 | 1 | 康纳 |
2 | 莎拉 | 1 | 康纳 |
3 | 克拉克 | 2 | 肯特 |
4 | 芭比 | NULL | NULL |
右外连接:
from tableA right join tableB on tableA.idB = tableB.idB
身份证 | 列表A | 数据库 | 列表B |
---|---|---|---|
1 | 乔恩 | 1 | 康纳 |
2 | 莎拉 | 1 | 康纳 |
3 | 克拉克 | 2 | 肯特 |
NULL | NULL | 3 | 斯波克 |
全外连接:
from tableA full join tableB on tableA.idB = tableB.idB
身份证 | 列表A | 数据库 | 列表B |
---|---|---|---|
1 | 乔恩 | 1 | 康纳 |
2 | 莎拉 | 1 | 康纳 |
3 | 克拉克 | 2 | 肯特 |
4 | 芭比 | NULL | NULL |
NULL | NULL | 3 | 斯波克 |
内连接在两个或多个表中提供匹配的记录。 在 where 子句中应用任何过滤器并匹配另一张表的记录后,外连接给出一张表的所有记录。 全外连接给出了两个表中的所有记录。
表 A col1 1 2 3 4 5
表 B Col1 3 4 5 6 7
Col1 上的内连接:3 4 5
Col1 上的左外连接 B:1 2 3 4 5
Col1 上的右外连接 B:3 4 5 6 7
Col1 上的完全外连接 B:1 2 3 4 5 6 7
在 SQL 中,连接用于比较和组合——字面意思是连接——并从数据库中的两个或多个表中返回特定的数据行。 INNER JOIN
从表中查找并返回匹配数据,而OUTER JOIN
从表中查找并返回匹配数据和一些不同的数据。
INNER JOIN
侧重于 2 个表之间的相似性。 使用INNER JOIN
时,要比较的两个(或多个)表之间必须至少有一些匹配的数据。 INNER JOIN
在表中搜索匹配或重叠的数据。 如果找到, INNER JOIN
将组合信息并将其返回到新表中。
让我们考虑一个有两个表的常见场景:产品价格和数量。 两个表中的共同信息是产品名称,所以这是连接表的逻辑列。 有些产品在两个表中是相同的; 其他人对一个人来说是独一无二的,并且在另一个人中没有匹配项。
产品INNER JOIN
仅返回有关两个表共有的产品的信息。
我整理了一些 HTML 和 CSS 的交互,以便您可以直观地想象整个事情。
/* CSS styles */ .circles { display: flex; } .circle { height: 100px; width: 100px; border-radius: 50%; background: #99AEBA; } .circle:first-child { background: #FF0000; } .circle:nth-child(2) { background: #05E156; transform: translateX(-45px); z-index: 2; mix-blend-mode: multiply; }
<!DOCTYPE html> <html> <head> <title>INNER JOIN</title> </head> <body> <div class="circles"> <div class="circle"></div> <div class="circle"></div> </div> </body> </html>
此处红色圆圈描述您的PRICES
,绿色圆圈描述您的QUANTITIES
。
价格 | 数量 |
---|
P.产品 | P.价格 | Q.产品 | Q.数量 |
---|---|---|---|
一个 | 6€ | 一个 | 92 |
乙 | 5€ | 乙 | 27 |
C | 5€ | D | 66 |
D | 1€ | 乙 | 20 |
如果您现在想要一个确定交集的INNER JOIN
,您可以编写以下查询:
SELECT Prices.*, Quantities.Quantity
FROM Prices INNER JOIN Quantities
ON Prices.Product = Quantities.Product;
作为交叉点,您现在从图中获得黑色区域,并得出以下查询结果:
产品 | 价格 | 数量 |
---|---|---|
一个 | 6€ | 92 |
乙 | 5€ | 27 |
D | 1€ | 66 |
OUTER JOIN
返回一组记录(或行),其中包含INNER JOIN
将返回的内容,但还包含在另一个表中未找到对应匹配项的附加行。
外连接分为三种类型:
LEFT OUTER JOIN
连接(或左连接)RIGHT OUTER 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=The%20biggest%20difference%20between%20an,table%20in% 20%20resulting%20 表。
- https://towardsdatascience.com/what-is-the-difference-between-an-inner-and-an-outer-join-in-sql-5b5ec8277377
我想告诉您,当您想要连接以提高性能时,您必须使用索引列。
在内连接中,我们可以检索不同表中具有相同/相关数据的数据
SELECT Sname, Tname FROM student s JOIN teacher t ON s.id = t.id;
在这种情况下,如果您还想从不同的表中获取其他数据信息,我们可以使用 go 进行外连接
我们有 3 种类型的外部连接:
左连接、右连接和完全连接
我们还将为LEFT OUTER JOIN 从左(第一个)表中检索不相关的数据。
SELECT Sname, Tname FROM student s LEFT JOIN teacher t ON s.id = t.id;
RIGHT OUTER JOIN ,我们还从右表(第二个表)中检索不相关的数据
SELECT Sname, Tname FROM student s RIGHT JOIN teacher t ON s.id = t.id;
当我们想从两个表中检索所有/完整数据时,我们可以使用FULL OUTER JOIN ,缺失的数据将用 NULL 填充。
SELECT Sname, Tname FROM student s FULL JOIN teacher t ON s.id = t.id;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.