什么是SQL JOIN
以及什么是不同类型?
SQL JOIN
?SQL JOIN
是一种从两个或多个数据库表中检索数据的方法。
SQL JOIN
s ?共有五个JOIN
s。他们是 :
1. JOIN or INNER JOIN
2. OUTER JOIN
2.1 LEFT OUTER JOIN or LEFT JOIN
2.2 RIGHT OUTER JOIN or RIGHT JOIN
2.3 FULL OUTER JOIN or FULL JOIN
3. NATURAL JOIN
4. CROSS JOIN
5. SELF JOIN
在这种JOIN
中,我们获得了与两个表中的条件匹配的所有记录,并且不报告两个表中不匹配的记录。
换句话说,INNER JOIN
基于以下单一事实:只有表格中的匹配条目才应列出。
请注意,没有任何其他JOIN
关键字的JOIN
(如INNER
,OUTER
,LEFT
等)是INNER JOIN
。换句话说,JOIN
是INNER JOIN
的一种语法糖(参见:Difference between JOIN and INNER JOIN)。
OUTER JOIN
检索
或者,来自一个表的匹配行和另一个表中的所有行,或者所有表中的所有行(无论是否匹配都无关紧要)。
外部加入有三种:
2.1 LEFT OUTER JOIN或LEFT JOIN
此连接返回左表中的所有行以及右表中的匹配行。如果右表中没有列匹配,则返回NULL
值。
2.2正确的外部加入或正确的加入
这个JOIN
返回右表中的所有行以及左表中的匹配行。如果左表中没有列匹配,则返回NULL
值。
2.3完全外部加入或完全加入
这个JOIN
结合了LEFT OUTER JOIN
和RIGHT OUTER JOIN
。它在满足条件时从任一表返回行,并在没有匹配时返回NULL
值。
换句话说,OUTER JOIN
基于以下事实:只应列出其中一个表中的匹配条目(RIGHT或LEFT)或表中的两个(FULL)。
Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.
它基于以下两个条件:
JOIN
是在所有具有相同名称的列上进行的。这似乎更具理论性,因此(可能)大多数DBMS甚至不打扰支持这一点。
它是所涉及的两个表的笛卡尔积。在大多数情况下,CROSS JOIN
的结果都没有意义。而且,我们根本不需要这个(或者至少需要,确切地说)。
它不是JOIN
的另一种形式,而是它本身的JOIN
(INNER
,OUTER
等)。
根据用于JOIN
子句的运算符,可以有两种类型的JOIN
s。他们是
对于任何JOIN
类型(INNER
,OUTER
等),如果我们只使用相等运算符(=),那么我们说JOIN
是EQUI JOIN
。
这与EQUI JOIN
相同,但它允许所有其他运算符,如>,=等。
许多人认为
EQUI JOIN
和ThetaJOIN
都类似于INNER
,OUTER
等JOIN
s。但我坚信这是一个错误,并使这些想法含糊不清。因为INNER JOIN
,OUTER JOIN
等都与表及其数据相关联,而EQUI JOIN
和THETA JOIN
只与我们在前者中使用的运算符相关联。同样,有许多人认为
NATURAL JOIN
是某种“奇特的”EQUI JOIN
。事实上,这是真的,因为我提到的NATURAL JOIN
的第一个条件。但是,我们不必仅仅依靠NATURAL JOIN
s来限制它。INNER JOIN
s,OUTER JOIN
s等也可能是EQUI JOIN
。
定义:
JOINS是查询同时从多个表组合在一起的数据的方法。
关注RDBMS有5种类型的连接:
根据SQL关注和进步,有3种类型的连接,并且可以使用这些类型的连接实现所有RDBMS连接。
注意:自连接可以通过INNER-JOIN,OUTER-JOIN和CROSS-JOIN根据需要实现,但表必须与自身连接。
1.1:INNER-JOIN:Equi-join实施
SELECT *
FROM Table1 A
INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;
1.2:INNER-JOIN:自然JOIN实现
Select A.*, B.Col1, B.Col2 --But no B.ForeignKeyColumn in Select
FROM Table1 A
INNER JOIN Table2 B On A.Pk = B.Fk;
1.3:使用非Equi-join实现的INNER-JOIN
Select *
FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;
1.4:使用SELF-JOIN进行INNER-JOIN
Select *
FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;
2.1:OUTER JOIN(全外连接)
Select *
FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;
2.2:LEFT JOIN
Select *
FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;
2.3:正确加入
Select *
FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;
3.1:交叉加入
Select *
FROM TableA CROSS JOIN TableB;
3.2:CROSS JOIN-Self JOIN
Select *
FROM Table1 A1 CROSS JOIN Table1 A2;
//要么//
Select *
FROM Table1 A1,Table1 A2;
有趣的是,大多数其他答案都存在以下两个问题:
我最近写了一篇关于这个主题的文章:A Probably Incomplete, Comprehensive Guide to the Many Different Ways to JOIN Tables in SQL,我将在这里总结一下。
这就是维恩图解释它们如此不准确的原因,因为JOIN在两个连接的表之间创建了一个cartesian product。维基百科很好地说明了这一点:
笛卡尔积的SQL语法是CROSS JOIN
。例如:
SELECT *
-- This just generates all the days in January 2017
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Here, we're combining all days with all departments
CROSS JOIN departments
它将一个表中的所有行与另一个表中的所有行组合在一起:
资源:
+--------+ +------------+
| day | | department |
+--------+ +------------+
| Jan 01 | | Dept 1 |
| Jan 02 | | Dept 2 |
| ... | | Dept 3 |
| Jan 30 | +------------+
| Jan 31 |
+--------+
结果:
+--------+------------+
| day | department |
+--------+------------+
| Jan 01 | Dept 1 |
| Jan 01 | Dept 2 |
| Jan 01 | Dept 3 |
| Jan 02 | Dept 1 |
| Jan 02 | Dept 2 |
| Jan 02 | Dept 3 |
| ... | ... |
| Jan 31 | Dept 1 |
| Jan 31 | Dept 2 |
| Jan 31 | Dept 3 |
+--------+------------+
如果我们只是写一个逗号分隔的表列表,我们将得到相同的:
-- CROSS JOINing two tables:
SELECT * FROM table1, table2
INNER JOIN
只是一个过滤的CROSS JOIN
,其中过滤谓词在关系代数中称为Theta
。
例如:
SELECT *
-- Same as before
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Now, exclude all days/departments combinations for
-- days before the department was created
JOIN departments AS d ON day >= d.created_at
请注意,关键字INNER
是可选的(MS Access除外)。
(Qazxswpoi)
一种特殊的Theta-JOIN是equi JOIN,我们最常用。谓词将一个表的主键与另一个表的外键连接起来。如果我们使用look at the article for result examples作为插图,我们可以写:
Sakila database
这将所有演员与他们的电影结合起来
或者,在一些数据库上:
SELECT *
FROM actor AS a
JOIN film_actor AS fa ON a.actor_id = fa.actor_id
JOIN film AS f ON f.film_id = fa.film_id
SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)
语法允许指定必须存在于JOIN操作表的任一侧的列,并在这两列上创建等式谓词。
其他答案分别列出了这个“JOIN类型”,但这没有意义。它只是equi JOIN的语法糖形式,这是Theta-JOIN或INNER JOIN的特例。 NATURAL JOIN只收集所有要连接的表共有的列,并将这些列连接到USING()
。由于意外匹配(如USING()
中的LAST_UPDATE
列),这几乎没有用处。
这是语法:
Sakila database
现在,SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film
与OUTER JOIN
略有不同,因为它创造了几种笛卡尔产品的INNER JOIN
。我们可以写:
UNION
没有人想写后者,所以我们写-- Convenient syntax:
SELECT *
FROM a LEFT JOIN b ON <predicate>
-- Cumbersome, equivalent syntax:
SELECT a.*, b.*
FROM a JOIN b ON <predicate>
UNION ALL
SELECT a.*, NULL, NULL, ..., NULL
FROM a
WHERE NOT EXISTS (
SELECT * FROM b WHERE <predicate>
)
(通常由数据库更好地优化)。
和OUTER JOIN
一样,关键字INNER
在这里是可选的。
OUTER
有三种口味:
OUTER JOIN
:如上所示,LEFT [ OUTER ] JOIN
表达式的左表被添加到联合中。JOIN
:如上所示,RIGHT [ OUTER ] JOIN
表达式的右表被添加到联合中。JOIN
:如上所示,FULL [ OUTER ] JOIN
表达式的两个表都被添加到联合中。所有这些都可以与关键字JOIN
或USING()
(NATURAL
)结合使用
在Oracle和SQL Server中有一些历史悠久的,已弃用的语法,它们在SQL标准具有以下语法之前就已经支持I've actually had a real world use-case for a NATURAL FULL JOIN
recently了:
OUTER JOIN
话虽如此,请不要使用此语法。我只是在这里列出这个,以便您可以从旧的博客文章/遗留代码中识别它。
-- Oracle
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id = fa.actor_id(+)
AND fa.film_id = f.film_id(+)
-- SQL Server
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id *= fa.actor_id
AND fa.film_id *= f.film_id
很少有人知道这一点,但SQL标准指定了分区的OUTER JOIN
(而Oracle实现了它)。你可以写这样的东西:
OUTER JOIN
部分结果:
WITH
-- Using CONNECT BY to generate all dates in January
days(day) AS (
SELECT DATE '2017-01-01' + LEVEL - 1
FROM dual
CONNECT BY LEVEL <= 31
),
-- Our departments
departments(department, created_at) AS (
SELECT 'Dept 1', DATE '2017-01-10' FROM dual UNION ALL
SELECT 'Dept 2', DATE '2017-01-11' FROM dual UNION ALL
SELECT 'Dept 3', DATE '2017-01-12' FROM dual UNION ALL
SELECT 'Dept 4', DATE '2017-04-01' FROM dual UNION ALL
SELECT 'Dept 5', DATE '2017-04-02' FROM dual
)
SELECT *
FROM days
LEFT JOIN departments
PARTITION BY (department) -- This is where the magic happens
ON day >= created_at
这里的要点是,无论+--------+------------+------------+
| day | department | created_at |
+--------+------------+------------+
| Jan 01 | Dept 1 | | -- Didn't match, but still get row
| Jan 02 | Dept 1 | | -- Didn't match, but still get row
| ... | Dept 1 | | -- Didn't match, but still get row
| Jan 09 | Dept 1 | | -- Didn't match, but still get row
| Jan 10 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 11 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 12 | Dept 1 | Jan 10 | -- Matches, so get join result
| ... | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 31 | Dept 1 | Jan 10 | -- Matches, so get join result
是否匹配“JOIN的另一侧”的任何内容,连接的分区侧的所有行都将结束。长话短说:这是为了填补报告中的稀疏数据。很有用!
真的吗?没有其他答案得到这个?当然不是,JOIN
(就像下面的ANTI JOIN一样)。但我们可以使用because it doesn't have a native syntax in SQL, unfortunately和IN()
,例如找到所有演过电影的演员:
EXISTS()
SELECT *
FROM actor a
WHERE EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
谓词充当半连接谓词。如果您不相信,请查看执行计划,例如:在Oracle中您将看到数据库执行SEMI JOIN操作,而不是WHERE a.actor_id = fa.actor_id
谓词。
EXISTS()
以下是没有电影的所有演员:
be careful not to use NOT IN
though
有些人(特别是MySQL人)也写这样的ANTI JOIN:
SELECT *
FROM actor a
WHERE NOT EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
我认为历史原因是表现。
天哪,这个太酷了。我是唯一一个提到它的人吗?这是一个很酷的查询:
SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL
它将为每个演员找到TOP 5创收电影。每次你需要一个TOP-N-per-something查询时,SELECT a.first_name, a.last_name, f.*
FROM actor AS a
LEFT OUTER JOIN LATERAL (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa USING (film_id)
JOIN inventory AS i USING (film_id)
JOIN rental AS r USING (inventory_id)
JOIN payment AS p USING (rental_id)
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
ON true
将成为你的朋友。如果您是SQL Server人员,那么您知道这个名为LATERAL JOIN
的JOIN
类型
APPLY
好吧,也许这是作弊,因为SELECT a.first_name, a.last_name, f.*
FROM actor AS a
OUTER APPLY (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa ON f.film_id = fa.film_id
JOIN inventory AS i ON f.film_id = i.film_id
JOIN rental AS r ON i.inventory_id = r.inventory_id
JOIN payment AS p ON r.rental_id = p.rental_id
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
或LATERAL JOIN
表达式实际上是一个产生多行的“相关子查询”。但如果我们允许“相关子查询”,我们也可以谈论......
这只是由Oracle和Informix实际实现的(据我所知),但它可以使用数组和/或XML在PostgreSQL中进行模拟,在SQL Server中使用XML进行模拟。
APPLY
生成相关子查询,并在外部查询中嵌套生成的行集。以下查询选择所有演员,并为每个演员在嵌套集合中收集他们的电影:
MULTISET
如你所见,有更多类型的JOIN,而不仅仅是通常提到的“无聊的”SELECT a.*, MULTISET (
SELECT f.*
FROM film AS f
JOIN film_actor AS fa USING (film_id)
WHERE a.actor_id = fa.actor_id
) AS films
FROM actor
,INNER
和OUTER
。 CROSS JOIN
。请停止使用维恩图来说明它们。
在我看来,我创造了一个比文字说明更好的插图:More details in my article