“ INNER JOIN”和“ OUTER JOIN”有什么区别?


4669

此外怎么办LEFT JOINRIGHT JOINFULL JOIN适合吗?


63
在下面的答案和评论及其参考中,只有一个实际上解释了维恩图如何表示算子:圆交集区域表示A JOIN B中的行集。每个圆圈唯一的区域表示通过获取其行得到的行集。不参与A JOIN B并添加其他表唯一的列的表的行都设置为NULL。(而且大多数人给A和B都模糊地圈出了伪造的对应。)
philipxy 2014年

1
从以下基于理论的答案跳至实际应用:我经常使用实验数据,在处理器设计上运行基准测试。通常,我希望比较2个或更多硬件选项之间的结果。INNER JOIN表示我只看到在所有实验中成功运行的基准;OUTER JOIN意味着我可以看到所有实验,包括那些无法在某些配置上运行的实验。重要的是要看到此类实验中的失败以及成功。重要的是,当许多RDBMS都缺少PerlSQL时,我写了PerlSQL来获得它,
Krazy Glew

4
已经提供了很多答案,但是我没有看到本教程提到的内容。如果您知道维恩图,那么这是一个很棒的教程: blog.codinghorror.com/a-visual-explanation-of-sql-joins 对我来说,它足够简洁,可以快速阅读,但仍然可以理解整个概念并可以完成所有工作。情况很好。如果您不知道维恩图是什么,请花5到10分钟的时间进行学习,这将在需要可视化使用集合和管理集合操作时提供帮助。
DanteTheSmith'5

14
@DanteTheSmith不,与这里的图表存在同样的问题。请参阅我在问题上方的评论,以及在该博客文章下方的评论:“杰夫在评论中往后几页拒绝了他的博客”。维恩图显示集合中的元素。只需尝试准确地确定这些图中的集合和元素是什么。集合不是表,元素不是它们的行。另外,任何两个表都可以连接,因此PK和FK无关紧要。都是假的 您正在做其他成千上万的工作-蒙蔽了您(错误地)认为有意义的印象
philipxy

3
克里斯,我建议你阅读这篇文章:towardsdatascience.com/... ...并考虑改变你接受的答案的选择(也许是为了一个与奖金),以不使用维恩图的答案。当前接受的答案误导了太多人。我敦促您这样做是为了我们社区的利益和我们知识库的质量。
Colm Bhandal

Answers:


6110

假设您要加入没有重复的列,这是一种很常见的情况:

  • 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

41
最好通过在表B中添加值为4的另一行来扩展该示例。这将表明内部联接不必等于行数。
softveda

472
一个很好的解释,但是这种说法:A和B的外部连接给出A并集B的结果,即维恩图并集的外部。措词不正确。外部联接除以下之一外,还将给出A与B相交的结果:全部A(左联接),全部B(右联接)或全部A和全部B(完全联接)。只有最后一种情况才真正是A联合B。但是,写得很好。
托马斯,

11
我对吗,FULL JOIN是FULL OUTER JOIN的别名,LEFT JOIN是LEFT OUTER JOIN的别名?
Damian 2013年

3
是的,很棒的解释。但是为什么在b列中值不按顺序排列?即是6,5不是5,6?
Ameer

7
@Ameer,谢谢。加入并不能保证订单,您需要添加ORDER BY子句。
Mark Harrison

730

维恩图对我来说并不是真的。

例如,它们没有显示交叉联接和内部联接之间的任何区别,或更笼统地说,它们显示了不同类型的联接谓词之间的任何区别,或者提供了推理它们如何操作的框架。

理解逻辑处理是无可替代的,无论如何,它都是相对直接的。

  1. 想象一下交叉连接。
  2. on针对第1步中的所有行评估该子句,保留谓词求和的行true
  3. (仅适用于外部联接)重新添加在步骤2中丢失的所有外部行。

(注意:实际上,查询优化器可能会找到比上面的纯粹逻辑描述更有效的查询执行方式,但最终结果必须相同)

我将从完整外部联接的动画版本开始。接下来是进一步的解释。

在此处输入图片说明


说明

源表

在此处输入链接说明

首先从CROSS JOIN(又称笛卡尔积)开始。它没有ON子句,仅返回两个表中所有行的组合。

从交叉连接中选择A.颜色,B。颜色

在此处输入链接说明

内连接和外连接具有“ ON”子句谓词。

  • 内部联接。为交叉连接结果中的所有行评估“ ON”子句中的条件。如果为true,则返回加入的行。否则将其丢弃。
  • 左外联接。与内部联接相同,然后对左表中任何不匹配的行输出这些,并为右表列输出NULL值。
  • 右外连接。与内部联接相同,然后在右表中与所有表都不匹配的任何行中,用左表列的NULL值输出这些行。
  • 完全外部联接。与内部联接相同,然后保留与左侧外部联接相同的左侧不匹配行,以及根据右侧外部联接的右侧不匹配行。

一些例子

从A.Colour = B.Colour的内部联接B中选择A.Colour,B.Colour

上面是经典的等分连接。

内部联接

动画版

在此处输入图片说明

从A.COLOUR NOT IN(“绿色”,“蓝色”)的内连接B中选择A.颜色,B。颜色

内部联接条件不必一定是相等条件,也不必引用两个表(甚至其中一个表)的列。A.Colour NOT IN ('Green','Blue')对交叉联接的每一行进行评估。

内部2

从1 = 1的内部联接B中选择A.颜色,B。颜色

对于交叉连接结果中的所有行,连接条件的评估结果均为true,因此与交叉连接相同。我将不再重复16行的图片。

从A.Colour = B.Colour的左外连接B选择A.Colour,B.Colour

外连接以同样的方式在逻辑上评价为内连接不同的是,如果左表行(左连接)不从右手表在所有它在结果中保留任何行会合NULL的价值观右手列。

LOJ

从A.COLOUR = B.COLOUR的左外连接B中选择A.Colour,B.Colour B.Colour为NULL

这只是将先前的结果限制为仅返回其中的行B.Colour IS NULL。在这种特殊情况下,这些行将被保留,因为它们在右侧表中不匹配,并且查询返回表中未匹配的单个红色行B。这称为反半联接。

IS NULL测试选择一列不可为空或连接条件可确保NULL排除任何值以使此模式正常工作,并避免仅带回恰好具有该NULL值的行,这一点很重要列以及未匹配的行。

loj为空

从A.Colour = B.Colour的右外连接B中选择A.Colour,B.Colour

右外部联接的行为与左外部联接类似,不同之处在于它们保留了来自右表的不匹配行,并且null扩展了左手列。

罗杰

从A.Colour = B.Colour的完整外部连接B中选择A.Colour,B.Colour

完全外部联接将左右联接的行为结合在一起,并保留左右表中不匹配的行。

福建

从完全外部联接B上选择A.颜色,B。颜色1 = 0

交叉联接中没有行与1=0谓词匹配。两侧的所有行均使用常规外部连接规则保留,另一侧的表的列中为NULL。

FOJ 2

从完整的外部连接B上选择颜色为COALESCE(A.Colour,B.Colour)作为颜色= 0

通过对前面的查询进行较小的修改,可以模拟UNION ALL两个表中的一个。

全联盟

从左外连接B上选择A.颜色,B。颜色B.颜色= B.颜色WHERE B.颜色='绿色'

请注意,该WHERE子句(如果存在)在逻辑上在联接之后运行。一个常见的错误是执行左外部联接,然后在右表上包含带有条件的WHERE子句,该条件最终将排除不匹配的行。以上结束了执行外部联接...

LOJ

...然后运行“ Where”子句。NULL= 'Green'不会评估为true,因此外部联接保留的行最终会被丢弃(与蓝色联接在一起),从而有效地将联接转换回内部联接。

LOJtoInner

如果意图是仅包含B的行(其中Color为绿色)和A的所有行,而无论正确的语法是

从A.Colour = B.Colour和B.Colour ='Green'的左外连接B中选择A.Colour,B.Colour

在此处输入图片说明

SQL小提琴

请参阅这些示例,在SQLFiddle.com上实时运行


46
我会说,尽管这对我来说几乎不及维恩图有效,但我感谢人们之间的差异和学习方式有所不同,这是一个很好的呈现方式,与我以前见过的解释不同,因此我在其中支持@ypercube奖励积分。也可以很好地解释将附加条件放在JOIN子句与WHERE子句中的区别。马丁·史密斯(Martin Smith),对您表示敬意。
Old Pro

22
@OldPro我猜想,Venn图是可行的,但是它们对如何表示交叉联接或区分一种联接谓词(例如,等联接)和其他联接谓词没有任何意见。评估交叉联接结果每一行上的联接谓词的心智模型,然后在外部联接的情况下添加不匹配的行,并最终评估where对我来说更好。
马丁·史密斯

18
维恩图非常适合表示并集,相交和差异,但不能表示联接。对于非常简单的联接(即联接条件位于唯一列上的联接),它们具有一些较小的教育价值。
ypercubeᵀᴹ

12
@Arth-不,你错了。SQL 提琴sqlfiddle.com/#!3/9eecb7db59d16c80417c72d1/5155这是维恩图无法说明的内容。
马丁·史密斯

7
@MartinSmith哇,我同意,我完全错了!太习惯了一对多..感谢您的纠正。
Arth

187

联接用于合并两个表中的数据,结果是一个新的临时表。联接是基于称为谓词的事物执行的,谓词指定了执行联接所使用的条件。内部联接和外部联接之间的区别在于,内部联接将仅返回基于联接谓词实际匹配的行。例如-让我们考虑Employee和Location表:

在此处输入图片说明

内部联接:-内部联接通过基于联接谓词 组合两个表( Employee Location)的列值来创建新的结果表。该查询将 Employee的每一行与 Location的每一行进行比较,以找到满足join谓词的所有行对。当通过匹配非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 Location的左外部联接(或简单地称为左联接)的结果始终包含“ left”表( Employee)的所有记录,即使联接条件未在其中找到任何匹配的记录“正确的”表( Location)。使用上面的表,这是用于左外部联接的SQL的样子:

select  * from employee left outer join location on employee.empID = location.empID;
//Use of outer keyword is optional

现在,运行此SQL的结果如下所示: 在此处输入图片说明

右外部联接:-右外部联接 (或右联接)与左外部联接非常相似,除了对表的处理相反。“右”表( Location)中的每一行将至少出现在联接表中一次。如果从“左”表(不匹配的行员工)存在,NULL就会出现在列的员工对于那些在没有匹配的记录位置。这是SQL的样子:

select * from employee right outer join location  on employee.empID = location.empID;
//Use of outer keyword is optional

使用上面的表,我们可以显示右外部联接的结果集是什么样的:

在此处输入图片说明

完全外部联接:- 完全外部联接或完全联接通过在联接结果中包括不匹配的行来保留不匹配的信息,请使用完全外部联接。它包括两个表中的所有行,而不管另一个表是否具有匹配值。

图片来源

MySQL 8.0参考手册-连接语法

Oracle Join操作


3
到目前为止最好的答案,替代语法-这就是我一直在寻找的,谢谢!
乔伊(Joey)

1
维恩图贴错标签。请参阅我对问题和其他答案的评论。这种语言中的大多数也很差。例如:“当通过匹配非NULL值满足连接谓词时,Employee和Location的每对匹配行对的列值将合并到结果行中。” 不,不是“当通过匹配非NULL值来满足连接谓词时”。行中的值与条件整体上是正确还是错误无关紧要。对于真实条件,某些值很可能为NULL。
philipxy

尽管没有明确说明,但其中的图是维恩图。通常,维恩图不是联接的正确数学特征。我建议删除维恩图。
Colm Bhandal

@ColmBhandal:删除维恩图
ajitksharma

使用文字而非图片/链接作为文字,包括表格和ERD。仅将图像用于不能表示为文本的内容或将其用作扩展文本。无法搜索或剪切和粘贴图像。在图像中包括图例/键和说明。
philipxy

132

内部联接

仅检索匹配的行,即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

参考文献


14
工具的名称是什么?我发现它很有趣,因为它显示了行数和
维恩

2
@GrijeshChauhan是的,但是您可以尝试使用wine来运行它。
Tushar Gupta-curioustushar 2014年

2
哦!是的,我..我使用SQLyog和酒一起使用..还有PlayOnLinux
Grijesh Chauhan,

1
您的文字不清楚和错误。“仅匹配的行”是来自A和B交叉连接的行,并且检索到的内容(A内部连接B)不是A与B相交,而是(A左连接B)相交(A右连接B)。“选定的”行不是来自A和B,而是来自A交叉连接B以及来自A和B的行的空扩展值
。– philipxy

@ TusharGupta-curioustushar,您应该包括“用于SQL示例的表”
Manuel Jordan

111

简单来说:

一个内部联接仅检索匹配的行。

外部联接从一个表中检索匹配的行,而从另一表中检索所有行..结果取决于您使用的是哪一个:

  • :右表中的匹配行以及左表中的所有行

  • :左表中匹配的行以及右表中的所有行或

  • 完整:所有表中的所有行。是否有比赛无关紧要


1
@nomen并不是这个答案可以解决这个问题,但是如果左右集合/圆包含(分别)左和右连接的行,则INNER JOIN是一个交集,而FULL OUTER JOIN是对应的UNION 。PS这个答案对于输入与输出中的行尚不清楚。它将“在左/右表中”与“在左/右中具有左/右部分”混淆,并且使用“匹配的行”与“全部”来表示从其他表中逐行扩展的行,而不是空值。
philipxy 2015年

103

内部联接仅在联接的另一侧(右侧)显示匹配记录时才显示行。

(左)外部联接在左侧显示每个记录的行,即使联接的另一(右侧)没有匹配的行。如果没有匹配的行,则另一(右侧)列将显示NULL。


81

内部联接要求联接表中存在具有相关ID的记录。

即使右侧没有任何内容,外部联接也将返回左侧的记录。

例如,您有一个Orders和一个OrderDetails表。它们通过“ OrderID”关联。

命令

  • 订单编号
  • 顾客姓名

订单详细信息

  • OrderDetailID
  • 订单编号
  • 产品名称
  • 数量
  • 价钱

要求

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的订单WHERE OrderDetails.OrderID IS NULL


1
我赞赏简单但现实的示例。我成功更改了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 ASCSELECT 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)之类的请求。我不确定其他条件,它们混合得很好...
PhiLho 2013年

68

简单来说:

内部联接 ->仅从父表和子表中获取公共记录,而父表的主键与子表中的外键匹配。

左联接 ->

伪码

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右侧的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表是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;

图表上看起来像:

图表


4
结果与主键/唯一键/候选键和外键无关(本身)。可以并且应该不对它们进行描述。计算交叉联接,然后滤除不符合“打开”条件的行;此外,对于外部联接,已过滤/不匹配的行将扩展为NULL(按LEFT / RIGHT / FULL并包括在内。)
philipxy 2015年

SQL连接始终是主键/外键匹配的假设导致这种Venn图的滥用。请相应地修改您的答案。
Colm Bhandal

58

您可以INNER JOIN从两个表中匹配的地方返回所有行。即在结果表中,所有行和列都将具有值。

OUTER JOIN结果表中可能有空列。外部连接可以是LEFTRIGHT

LEFT OUTER JOIN 即使第二张表中没有匹配项,也返回第一张表中的所有行。

RIGHT OUTER JOIN 返回第二个表中的所有行,即使第一个表中没有匹配项也是如此。



54

INNER JOIN要求在比较这两个表时至少要有一个匹配项。例如,表A和表B表示A ٨ B(交点B)。

LEFT OUTER JOINLEFT JOIN一样。它提供了两个表中都匹配的所有记录以及左表的所有可能性。

同样,RIGHT OUTER JOIN并且RIGHT JOIN是相同的。它提供了两个表中都匹配的所有记录以及右表的所有可能性。

FULL JOIN是重复LEFT OUTER JOINRIGHT OUTER JOIN不重复的组合。


43

答案是每个人的意思,结果也是如此。

注:
SQLite没有RIGHT OUTER JOINFULL 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

好吧,根据您的需要,您可以选择满足您需求的每一个;)。


您可以添加注释,full outer joinMySQL中也没有。
potashin

35

内部联接。

联接是合并两个表中的行。一个内连接尝试根据您在查询中指定的标准,这两个表匹配,并且只返回行那场比赛。如果联接中第一个表中的一行与第二个表中的两行匹配,那么结果中将返回两行。如果第一个表中的行与第二个表中的行不匹配,则不返回该行;同样,如果第二个表中的行与第一个表中的行不匹配,则不返回该行。

外连接。

一个左连接试图找到第二个表匹配第一个表中的行的行。如果找不到匹配项,它将返回第一个表中的列,并将第二个表中的列留空(空)。


28

在此处输入图片说明

  • INNER JOIN两个或多个表的最典型联接。它返回表ON主键和外键关系上的数据匹配。
  • OUTER JOIN与相同INNER JOIN,但也包含NULLResultSet上的数据。
    • LEFT JOIN= INNER JOIN+ 表的不匹配数据与表的Null匹配项。
    • RIGHT JOIN= INNER JOIN+ 表的不匹配数据与Null左表的匹配。
    • FULL JOIN= INNER JOIN+ 左右表上具有Null匹配项的不匹配数据。
  • 当表本身引用数据时,自连接不是SQL中的关键字,称为自连接。使用INNER JOINOUTER 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 

27

在其他答案中,我没有看到太多有关性能和优化器的细节。

有时最好知道只有INNER JOIN关联性,这意味着优化器具有最多的选择。它可以对连接顺序重新排序,以使其更快地保持相同的结果。优化器可以使用最多的联接模式。

通常,尝试使用INNER JOIN而不是其他类型的联接是一个好习惯。(当然,如果可以考虑预期的结果集。)

关于这种奇怪的关联行为,这里有几个很好的例子和解释:


4
在另一种类型上使用一种连接不可能是“好的做法”。您使用哪个联接确定所需的数据。如果您使用其他人,那是不正确的。另外,至少在Oracle中,这个答案是完全错误的。听起来一切都是错误的,您没有证据。你有证据吗?
2014年

1.我的意思是尝试使用。我看到很多人在没有任何充分理由的情况下使用LEFT OUTER进行连接。(联接的列为'not null'。)在那种情况下,使用INNER联接肯定会更好。2.我添加了一个链接,它比我能更好地解释了非关联行为。
Lajos Veres 2014年

据我所知,INNER JOIN它比LEFT JOIN大多数情况下都要慢,并且人们可以使用LEFT JOIN而不是INNER JOIN通过添加a WHERE来删除意外的NULL结果;)。
shA.t 2015年

这些评论使我有些不确定。您为什么认为INNER速度较慢?
Lajos Veres,2015年

取决于引擎。gnu join,joinkeys,DB2,MySQL。性能陷阱比比皆是,例如松散的键入或显式强制转换。
mckenzm

26

批评了广受欢迎的红色阴影的维恩图之后,我认为发布自己的尝试是公平的。

尽管@Martin Smith的答案在很多方面都是最好的,但他只显示每个表中的键列,而我认为理想情况下还应显示非键列。

在允许的半小时内,我能做的最好,但我仍然认为这并不能充分表明空值是由于缺少键值而造成的,TableB或者OUTER JOIN实际上是联合而不是联接:

在此处输入图片说明


2
问题是,尽管内部连接和外部连接之间存在差异,但不一定要离开外部连接大声笑
LearnByReading

@LearnByReading:我右边的图片是右外部TableA a LEFT OUTER JOIN TableB bTableB B RIGHT OUTER JOIN TableA a
联接,

26

精确的算法INNER JOINLEFT/RIGHT OUTER JOIN如下:

  1. 从第一张表中取出每一行: a
  2. 考虑第二个表旁边的所有行: (a, b[i])
  3. 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 (仅作为示例)

内部联接与左外部联接


在此处输入图片说明

注意:左联接=左外部联接,右联接=右外部联接。


20

最简单的定义

内部联接:从两个表中返回匹配的记录

FULL OUTER JOIN:返回匹配和不匹配的记录从空从匹配的记录两个表这两个表

左外部联接:仅从“ 左侧”表中返回匹配和不匹配的记录。

右外部联接:仅从“ 右侧”表中返回匹配和不匹配的记录。

简而言之

匹配+左不匹配+右不匹配= 完全外部联接

匹配+左不匹配= 左外连接

匹配+无匹配= 右外部连接

匹配= 内部加入


1
这很了不起,说明了为什么联接无法按时间序列索引的预期进行。相隔一秒的时间戳是无与伦比的。
yeliabsalohcin

1
@yeliabsalohcin您在此处对问题的评论中并未“按预期”或“有效”进行解释。这只是您奇怪地期望其他人无法解释的一些个人误解。如果您在阅读时将字词当作是草率的话-误解了清晰的文字和/或接受了不清楚的文字-就像您在这里写作时那样,那么您可能会有误解。实际上,这个答案像这里大多数情况一样,尚不清楚和错误。输入列集不同时,“内部联接:从两个表中返回匹配的记录”是错误的。它正在尝试说某些事情,但事实并非如此。(请参阅我的回答。)
philipxy

9

简单来说,

1. INNER JOIN或EQUI JOIN:返回仅与两个表中的条件均匹配的结果集。

2. OUTER JOIN:返回两个表中所有值的结果集,即使条件匹配与否。

3. LEFT JOIN:返回左表中所有值的结果集,并且仅返回与右表中的条件匹配的行。

4. RIGHT JOIN:返回右表中所有值的结果集,并且仅返回与左表中的条件匹配的行。

5. 完全联接:完全联接和完全外部联接相同。


5

SQL中的JOIN主要有2种类型:[INNER和OUTER]


例子

假设您有两个表,每个表都有一个列,数据如下:

A    B
-    -
1    3
2    4
3    5
4    6
7
8

请注意,(1,2,7,8)对于A是唯一的,(3,4)是公共的,而(5,6)对于B是唯一的。



  • (内)加入

只要条件满足,INNER JOIN关键字就会从两个表中选择所有行。此关键字将通过组合两个表中满足条件的所有行来创建结果集,即,公共字段的值将相同。

内部联接

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


  • 左(外)联接

此联接返回联接左侧的表的所有行,以及联接右侧的表的匹配行。右侧没有匹配行的行,结果集将包含null。左联接也被称为LEFT OUTER JOIN

左联接/左联接

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
7 | null
8 | null


  • RIGHT(OUTER)JOIN:从右表返回所有记录,并从左表返回匹配的记录

正确的联接/正确的外部联接

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


  • 完全(外部)加入

    FULL JOIN通过组合LEFT JOIN和RIGHT JOIN的结果来创建结果集。结果集将包含两个表中的所有行。没有匹配的行,结果集将包含NULL值。

全联接/全外联接

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
   7 | null
   8 | null

1
在一般情况下,维恩图不足以描述SQL连接。SQL连接不一定必须一对一地匹配表之间的行,例如使用外键与主键。
Colm Bhandal

如何在不匹配行的情况下联接2个表?您必须使用列主键或外键或某个公共字段,才能执行连接。我想知道你为什么反对这个答案。维恩图是解释SQL JOIN如何工作的源。您是否有更好的例子来代表联接?如果不是,请对其进行投票,以便人们获得更好的解决方案。谢谢。
Mayur

您就是将图表放在帖子中的人。图的图例是什么?-每组的元素是什么?那桌子不是袋子呢?你不说 他们没有设置的一件事是每个标签的A和B行。请参阅我对本页上的帖子的评论。这篇文章只是盲目地重复了其他地方看到的错误用法,但并未理解或质疑。另外,您在此处的文字内容不清楚也不正确。此外,它对已在此处提供的许多答案没有任何帮助。(即使几乎所有的人都很穷。)PS请通过编辑而不是评论进行澄清。
philipxy

FK不需要加入或查询。任何2个表都可以在涉及其列的任何条件下进行连接,而不受任何约束,触发器或断言的影响。
philipxy

3
  • 内部联接 - 使用任一等价查询的内部联接给出两个的交集,即它们共有的两行。

  • 左外部联接 - 左外部联接将给出A中的所有行,以及B中的所有常见行。

  • 完全外部连接 - 完全外部连接将为您提供A和B的并集,即A中的所有行和B中的所有行。如果A中的某些内容在B中没有对应的基准,则B部分为null,反之亦然


1
这既错又不清楚。除非表具有相同的列,否则联接不是交叉点。外部联接没有A或B的行,除非它们具有相同的列,在这种情况下,不添加任何空值。您正在尝试说些什么,但您并未在说。您没有正确或清楚地解释。
philipxy

@philipxy:不同意您的语句Join is not an intersection unless the tables have the same columns编号。您可以连接所需的任何列,如果值匹配,它们将连接在一起。
SuicideSheep

该评论与您的​​答案一样不清楚。(我想您可能会这样想,结果的公共列的子行值集是每个输入的公共列的子行值集的交集;但这不是您编写的内容。尚不清楚。)
philipxy

我的意思是,当联接是具有相同列的输入的自然内部联接时,联接仅是输入的交集。您错误地使用了“交集”和“联合”一词。
philipxy

3

1. 内部联接:也称为联接。仅当存在match时,它才返回Left表和right表中存在的行。否则,它将返回零记录。

例:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
INNER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

输出1

2. 完全外部联接:也称为完全联接。它返回在左表和右表中都存在的所有行

例:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
FULL OUTER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

输出2

3. 左外部联接或简称为“左联接”。它返回左侧表中存在的所有行以及右侧表中的匹配行(如果有)。

4. 右外连接:也称为右连接。它从左表(如果有)返回匹配的行,并在右表中返回所有行。

加入

加盟优势

  1. 执行速度更快。

2
仅当表具有相同的列集时,这才是正确的。(它使内部连接与交集混淆,而完全连接与联合混淆。)而且“ match”是未定义的。阅读我的其他评论。
philipxy

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表中找不到匹配项。同样,HRR&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    

因此,如果您观察到以上输出,则将打印LEFT表(Emp)中的所有记录,并仅打印RIGHT表中的匹配记录。

HR并且R&D没有从Department表中打印行,因为它们在dept_id的Emp表中找不到匹配项。

因此,LEFT JOIN返回Left表中的所有行,并且仅返回Right表中的匹配行。

也可以在此处检查DEMO 。


2

总体思路

请参阅答案马丁·史密斯美好illustations和解释的不同的连接,包括之间特别是不同FULL OUTER JOINRIGHT OUTER JOINLEFT OUTER JOIN

这两张表构成了JOIN以下s 表示形式的基础:

基础

交叉加入

交叉连接

SELECT *
  FROM citizen
 CROSS JOIN postalcode

结果将是所有组合的笛卡尔乘积。无需任何JOIN条件:

CrossJoinResult

内部联接

INNER JOIN 与简单的相同: JOIN

内部联接

SELECT *
  FROM citizen    c
  JOIN postalcode p ON c.postal = p.postal

结果将是满足所需JOIN条件的组合:

InnerJoinResult

左外连接

LEFT OUTER JOIN 是相同的 LEFT JOIN

左加入

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON c.postal = p.postal

结果将是的所有结果,citizen即使中没有匹配项也是如此postalcode。再次JOIN需要一个条件:

LeftJoinResult

播放数据

所有示例都在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导致行显示为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联接交叉内部外部”上的图像互联网搜索将显示大量的维恩图。我曾经在桌上放着一份印刷品。但是表示存在问题。

维恩图对于集合论非常有用,因为其中一个元素可以位于一个集合或两个集合中。但是对于数据库来说,在我看来,一个“集合”中的一个元素似乎是表中的一行,因此在其他任何表中也不存在。多个表中不存在一行。该表唯一的一行。

自连接是一个极端的情况,其中两个元素中的每个元素实际上都相同。但是它仍然不能解决以下任何问题。

在下面的讨论中,集合A表示左边的集合(citizen表格),而集合B是右边的集合(postalcode表格)。

交叉加入

两个集合中的每个元素都与另一个集合中的每个元素匹配,这意味着我们需要A每个B元素的B数量和每个A元素的数量才能正确表示此笛卡尔积。集合论不是针对集合中的多个相同元素建立的,因此我发现维恩图正确地表示它是不切实际/不可能的。似乎根本不合适UNION

这些行是不同的。的UNION是在总7行。但是它们对于通用SQL结果集是不兼容的。这根本不是CROSS JOIN工作原理:

CrossJoinUnion1

试图这样表示:

CrossJoinUnion2Crossing

..但是现在看起来就像一个INTERSECTION,当然不是。此外,中的元素INTERSECTION实际上不在两个不同的集合中。但是,它看起来非常类似于以下可搜索结果:

CrossJoinUnionUnion3

作为参考,CROSS JOIN可以在Tutorialgateway中看到s的一个可搜索结果。的INTERSECTION,就像这一次,是空的。

内部联接

元素的值取决于JOIN条件。可以在每一行对该条件唯一的条件下表示这种情况。意思id=x是只有真正的一个排。一旦在表中的一行Acitizen)在表中的多个行匹配Bpostalcode下)JOIN条件,结果有相同的问题CROSS JOIN:该行需要被代表多次和集合论是不是真的该做。在唯一性的条件下,该图可以工作,但要记住,JOIN条件决定了元素在图中的位置。只看JOIN条件的值,其余部分就顺其自然:

InnerJoinIntersection-已填充

当使用INNER JOIN带有ON 1 = 1使其成为的条件时,此表示形式完全瓦解CROSS JOIN

用自JOIN,行是在两个表中实际上idential元素,但表示该表作为既AB不是很适合。例如JOIN,使B中的一个元素A另一个元素匹配的常见自我条件是ON A.parent = B.child,使匹配从AB单独的元素。从这样的例子中SQL

SELECT *
  FROM citizen c1
  JOIN citizen c2 ON c1.id = c2.leader

SelfJoinResult

意思是史密斯是格林和詹森的领导者。

外连接

当一行与另一张表中的行有多个匹配项时,麻烦就再次出现。这更加复杂,因为OUTER JOIN可以匹配空集。但是在集合论中,任何集合C与一个空集合的并集总是正义的C。空集不添加任何内容。表示LEFT OUTER JOIN通常只是显示所有内容,A以说明A选择了in 的行,无论from中是否存在匹配项B。然而,“匹配元件”具有与以上图示相同的问题。它们取决于条件。空集似乎已经漂移到A

LeftJoinIntersection-已填充

WHERE子句-有意义

CROSS JOIN使用史密斯(Smith)和邮政编码在月球上查找的所有行:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
   AND p.area = 'Moon';

哪里-结果

现在,维恩图不再用于反映JOIN。它用于WHERE子句:

哪里

..那是有道理的。

当INTERSECT和UNION有意义时

相交

如所解释的INNER 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';

联盟

An OUTER JOIN不是一个UNION。但是,UNION在与相同的条件下工作INTERSECT,导致将SELECTs 结合在一起的所有结果都返回:

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

有关进一步的兴趣,请在解释JOINsql联接为venn图阅读对Venn图说不。两者也都包括在内EXCEPT


1

这里有很多非常准确的关系代数示例的良好答案。这是一个非常简化的答案,可能对具有SQL编码难题的业余或新手编码员有所帮助。

基本上,查询通常可以JOIN归结为两种情况:

对于SELECT数据子集A

  • 使用INNER JOIN时,相关的数据B,你正在寻找MUST每个数据库设计存在;
  • 使用LEFT JOIN时,相关的数据B,你正在寻找MIGHT可能不会每个数据库的设计存在。

1

inner join和之间的区别outer join如下:

  1. Inner join是一个基于匹配元组合并表的联接,而是outer join一个基于匹配和不匹配元组合并表的联接。
  2. Inner join合并两个表中的匹配行,其中省略不匹配的行,而outer join合并两个表中的行和不匹配的行则填充空值。
  3. Inner join就像相交运算,而outer join像联合运算。
  4. Inner join是两种类型,而是outer join三种类型。
  5. outer join比快inner join

1
外部联接的结果与内部联接的结果相同,但是加上一些额外的行,因此我不知道为什么您认为外部联接会更快。内连接的“两种”又是什么?我想您指的是外部的完整,左侧和右侧?
马丁·史密斯

1
@ M.achaibou请不要进行不必要的格式更改编辑。除非在代码中使用运算符,否则它们不是代码。请不要更改发布的含义,对作者发表评论。如果您没有代表,请等到您这样做。可能是发布者批准了此操作,但请不要进行此类编辑。PS外部联接不比内部联接快。
philipxy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.