此外,如何使用 LEFT OUTER JOIN
, RIGHT OUTER JOIN
, 和 FULL 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部分为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
对我来说,文氏图并不能很好地展示不同类型的连接谓词之间的区别,例如交叉连接和内连接,更普遍地说,并没有展示任何连接谓词的区别或提供用于推理它们将如何操作的框架。
没有什么能替代理解逻辑处理,而且相对容易掌握。
on
子句,并保留谓词评估为true
的行。(注:实际上,查询优化器可能会找到比上述纯逻辑描述更高效的执行查询的方法,但最终结果必须相同)
我首先从一个完全外连接的动画版本开始。随后是更详细的说明。
源表
首先开始一个CROSS JOIN
(也称笛卡尔积)。它没有ON
子句,仅返回两个表中行的每种组合。
SELECT A.Colour, B.Colour FROM A CROSS JOIN B
内部和外部连接有一个“ON”子句谓词。
SELECT A.Colour, B.Colour FROM A INNER JOIN B ON A.Colour = B.Colour
以上是经典的等值连接。
内连接条件可以不是相等条件,也可以不引用任何一个或两个表中的列。对于交叉连接的每一行,评估A.Colour NOT IN ('Green','Blue')
返回结果。
从A表和B表选择颜色字段,条件为1=1,并与B表进行内连接
连接条件对交叉连接结果中的所有行均返回true,因此这与交叉连接相同。我不会再重复16行的图片了。
外连接逻辑上与内连接的计算方式相同,只是如果左表(对于左连接)的某一行根本没有与右表的任何行连接,它将以NULL
值保留在结果中的右列。
B.Colour IS NULL
的行这仅将之前的结果限制为仅返回B.Colour IS NULL
的行。在这种特定情况下,这些是未匹配到表B
中的单个红行,并且查询返回该行。这被称为反半连接。
重要的是选择一列进行IS NULL
测试,该列既不可为空,也确保联接条件排除了任何NULL
值,以使此模式正确工作并避免仅返回具有该列的NULL
值和未匹配的行。
右外连接与左外连接类似,但它保留来自右表的非匹配行,并扩展左侧列的NULL
值。
全外连接结合了左连接和右连接的行为,并保留了左表和右表中的非匹配行。
在交叉连接中没有行与1=0
谓词匹配。使用普通外部连接规则保留来自两侧的所有行,并在来自另一侧表的列中使用NULL。
通过对上一个查询进行微小修改,可以模拟两个表的UNION ALL
。
请注意,如果存在WHERE
子句,则逻辑上会在连接之后运行。一个常见的错误是执行左外连接,然后在带有右表条件的WHERE子句中包含一个条件,该条件最终排除非匹配行。以上查询执行外部连接...
...然后运行“Where”子句。 NULL= 'Green'
不会被评估为真,因此由外部连接保留的行最终被丢弃(以及蓝色行),有效地将连接转换回内连接。
如果意图是仅包括B中颜色为绿色的行,而所有来自A的行都是正确的语法,则应该是
查看这些示例在SQLFiddle.com上实时运行。
员工编号 | 员工所在地 |
---|---|
13 | 圣何塞 |
8 | 洛杉矶 |
3 | 印度浦那 |
17 | 印度金奈 |
39 | 印度班加罗尔 |
内连接: 内连接通过基于连接谓词,将两个表(员工和所在地)的列值组合成一个新的结果表。查询将员工的每一行与所在地的每一行进行比较,以找到满足连接谓词的所有行对。当匹配非空值时,将员工和所在地的每个匹配行的列值组合成一个结果行。 下面是内连接的 SQL 语句:
select * from employee inner join location on employee.empID = location.empID
OR
select * from employee, location where employee.empID = location.empID
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 |
左外连接: 对于表Employee和Location的左外连接(或简称为左连接),其结果始终包含“左”表(Employee)的所有记录,即使连接条件在“右”表(Location)中找不到任何匹配的记录。 下面是使用上述表进行左外连接的SQL语句:
select * from employee left 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 |
25 | Johnson | NULL | NULL |
右外连接: 右外连接(或右连接)与左外连接非常相似,只是表的处理方式相反。来自“右”表(位置)的每一行都将至少出现在连接表中。如果“左”表(员工)中没有匹配的行,则对于那些在位置中没有匹配的记录,员工的列中将出现NULL。 这就是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 | Bangalore, India |
全外连接: 全外连接或全连接是通过在联接结果中包含不匹配的行来保留非匹配信息的,使用全外连接。它包括来自两个表的所有行,无论另一个表是否具有匹配值。
员工编号 | 员工姓名 | 所在地.员工编号 | 所在地.员工位置 |
---|---|---|---|
13 | Jason | 13 | 圣何塞 |
8 | Alex | 8 | 洛杉矶 |
3 | Ram | 3 | 印度浦那 |
17 | Babu | 17 | 印度钦奈 |
25 | Johnson | NULL | NULL |
NULL | NULL | 39 | 印度班加罗尔 |
仅检索匹配的行,即A 交 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
简单来说:
内连接只检索匹配的行。
而外连接会检索一个表中匹配的行以及另一个表中的所有行,结果取决于您使用的哪种类型:
左连接:在左侧表中匹配的行和右侧表中的所有行。
右连接:在右侧表中匹配的行和左侧表中的所有行。
完全连接:所有表中的所有行。是否有匹配都无所谓。
内连接(inner join)仅在与另一侧(右侧)的表具有匹配记录时显示行。
左外连接(left outer join)为左侧每个记录显示行,即使在连接的另一侧(右侧)没有匹配的行也如此。如果没有匹配的行,则显示其他(右侧)侧的列为NULL。
内连接要求加入表中存在具有相关ID的记录。
外连接会返回左侧的记录,即使右侧没有对应的记录。
例如,您有一个订单和一个订单详细信息表,它们由“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
如果这样做,它将返回来自订单表的记录,即使它们没有订单详细信息。
您可以通过添加像 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
(MySQL)改为了 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
。我不确定关于额外的条件,它们混合得很好... - PhiLho简单来说:
内连接 -> 仅从父表和子表中取出主键与外键匹配的共同记录。
左连接 ->
pseudo code
1.Take All records from left Table
2.for(each record in right table,) {
if(Records from left & right table matching on primary & foreign key){
use their values as it is as result of join at the right side for 2nd table.
} else {
put value NULL values in that particular record as result of join at the right side for 2nd table.
}
}
右连接:与左连接完全相反。在右连接中,将表的名称放置在LEFT JOIN右侧,您将获得与LEFT JOIN相同的输出。
外连接:显示两个表中的所有记录,无论如何。如果左表中的记录与基于主键、外键的右表不匹配,则使用NULL值作为连接的结果。
示例:
假设现在有2个表:
1.员工,2.员工电话号码
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)匹配时。
因此查询语句应该是:
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
的组合,没有重复项。