SQL JOIN and different types of JOINs
什么是SQL
W3学校的插图:
。
氧化镁
氧化镁
氧化镁
什么是
共有五个
1 2 3 4 5 6 7 8 9 10 | 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 |
1。连接或内部连接:
在这种类型的
换句话说,
注意,没有任何其他
或者,一个表中的匹配行和另一个表中的所有行或者,所有表中的所有行(是否匹配并不重要)。
外部连接有三种:
2.1左外接或左接
此联接将返回左表中的所有行以及右桌子。如果右表中没有匹配的列,则返回
2.2右外接或右接
此
2.3完全外部联接或完全联接
这种
换言之,
1 | Note that `OUTER JOIN` IS a loosened form OF `INNER JOIN`. |
号三。自然连接:
它基于两个条件:
这在本质上更具理论意义,因此(可能)大多数DBMS别费心支持这个。
4。交叉联接:它是所涉及的两个表的笛卡尔积。
它不是
根据用于
1。Equi连接:
对于任何
这与
Many consider both
EQUI JOIN and ThetaJOIN similar toINNER ,OUTER
etcJOIN s. But I strongly believe that its a mistake and makes the
ideas vague. BecauseINNER JOIN ,OUTER JOIN etc are all connected with
the tables and their data whereasEQUI JOIN andTHETA JOIN are only
connected with the operators we use in the former.Again, there are many who consider
NATURAL JOIN as some sort of
"peculiar"EQUI JOIN . In fact, it is true, because of the first
condition I mentioned forNATURAL JOIN . However, we don't have to
restrict that simply toNATURAL JOIN s alone.INNER JOIN s,OUTER JOIN s
etc could be anEQUI JOIN too.
号
定义:
联接是同时从多个表中查询组合在一起的数据的方法。
连接类型:关系到RDBMS有5种类型的连接:
equi join:根据相等条件组合两个表中的公共记录。从技术上讲,通过使用相等运算符(=)比较一个表的主键值和另一个表的外键值而进行的联接,因此结果集包括来自两个表的公共(匹配)记录。有关实现,请参见内部联接。
自然连接:它是Equi连接的增强版本,其中选择操作省略重复列。有关实现,请参见内部联接
非等参联接:与等参联接相反,联接条件使用的不是等量运算符(=),例如,!=、<=、>=、>、<或介于等之间。有关实现,请参阅内部联接。
自联接:一个表与自身结合的联接的自定义行为;查询自引用表(或一元关系实体)通常需要这种行为。有关实现,请参见内部联接。
笛卡尔积:它交叉组合两个表的所有记录,没有任何条件。从技术上讲,它返回查询的结果集,而不返回WHERE子句。
根据SQL关注点和高级,有三种类型的连接,所有RDBMS连接都可以使用这些类型的连接来实现。
内部联接:合并(或组合)两个表中匹配的行。匹配是根据表的公共列及其比较操作来完成的。如果是基于等式的条件,那么:执行了equi-join,否则是非equi-join。
**外部联接:**它合并(或组合)两个表中的匹配行和具有空值的不匹配行。但是,可以自定义不匹配行的选择,例如,按子类型从第一个表或第二个表中选择不匹配行:左外部联接和右外部联接。
2.1。左外部联接(又称左联接):返回两个表中匹配的行,并且仅从左表(即第一个表)中不匹配。
2.2。右外部联接(也称为右联接):返回两个表中匹配的行,仅从右表中不匹配的行。
2.3。完全外部联接(也称为外部联接):从两个表中返回匹配的和不匹配的。
交叉联接:此联接不合并/组合,而是执行笛卡尔积。
。注:自连接可以根据需要通过内部连接、外部连接和交叉连接实现,但表必须与自身连接。
更多信息:
示例:1.1:内部联接:Equi联接实现
1 2 3 | SELECT * FROM Table1 A INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>; |
1.2:内部联接:自然联接实现
1 2 3 | 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:具有非等连接实现的内部连接
1 2 | SELECT * FROM Table1 A INNER JOIN Table2 B ON A.Pk <= B.Fk; |
1.4:内部连接和自连接
1 2 | SELECT * FROM Table1 A1 INNER JOIN Table1 A2 ON A1.Pk = A2.Fk; |
。
2.1:外部连接(完全外部连接)
1 2 | SELECT * FROM Table1 A FULL OUTER JOIN Table2 B ON A.Pk = B.Fk; |
2.2:左连接
1 2 | SELECT * FROM Table1 A LEFT OUTER JOIN Table2 B ON A.Pk = B.Fk; |
。
2.3:右连接
1 2 | SELECT * FROM Table1 A RIGHT OUTER JOIN Table2 B ON A.Pk = B.Fk; |
。
3.1:交叉连接
1 2 | SELECT * FROM TableA CROSS JOIN TableB; |
3.2:交叉连接自连接
1 2 | SELECT * FROM Table1 A1 CROSS JOIN Table1 A2; |
。
/或/或/
1 2 | SELECT * FROM Table1 A1,Table1 A2; |
有趣的是,大多数其他答案都存在这两个问题:好的。
- 它们只关注连接的基本形式
- 他们(AB)使用维恩图,这是一个不准确的工具可视化连接(他们对联合更好)。
我最近写了一篇关于这个主题的文章:关于SQL中连接表的许多不同方法的一个可能不完整、全面的指南,我将在这里总结。好的。首先,连接是笛卡尔积
这就是为什么文氏图解释的如此不准确,因为一个连接在两个连接的表之间创建了笛卡尔积。维基百科很好地说明了这一点:好的。
。好的。
笛卡尔产品的SQL语法是
1 2 3 4 5 6 7 8 9 10 11 | 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 |
它将一个表中的所有行与另一个表中的所有行组合在一起:好的。
资料来源:好的。
1 2 3 4 5 6 7 8 9 | +--------+ +------------+ | DAY | | department | +--------+ +------------+ | Jan 01 | | Dept 1 | | Jan 02 | | Dept 2 | | ... | | Dept 3 | | Jan 30 | +------------+ | Jan 31 | +--------+ |
号
结果:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | +--------+------------+ | 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 | +--------+------------+ |
如果我们只写一个逗号分隔的表列表,我们将得到相同的结果:好的。
1 2 | -- CROSS JOINing two tables: SELECT * FROM table1, table2 |
。内部连接(Theta连接)
例如:好的。
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
注意,关键字
(查看文章中的结果示例)好的。Equi连接
一种特殊的theta连接是equi连接,我们使用最多。谓词将一个表的主键与另一个表的外键联接起来。如果我们使用Sakila数据库进行说明,我们可以编写:好的。
1 2 3 4 | 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 |
。
这将所有演员与他们的电影结合起来。好的。
或者,在某些数据库上:好的。
1 2 3 4 | SELECT * FROM actor JOIN film_actor USING (actor_id) JOIN film USING (film_id) |
。
其他答案单独列出了这个"连接类型",但这没有意义。它只是equi-join的语法糖形式,这是theta-join或inner-join的一种特殊情况。自然联接只收集被联接的两个表共用的所有列,并联接这些列。由于意外匹配(如sakila数据库中的
语法如下:好的。
1 2 3 4 | SELECT * FROM actor NATURAL JOIN film_actor NATURAL JOIN film |
外部连接
现在,
1 2 3 4 5 6 7 8 9 10 11 12 13 | -- Convenient syntax: SELECT * FROM a LEFT JOIN b ON [cc lang="sql"] -- Cumbersome, equivalent syntax: SELECT a.*, b.* FROM a JOIN b ON [cc lang="sql"] UNION ALL SELECT a.*, NULL, NULL, ..., NULL FROM a WHERE NOT EXISTS ( SELECT * FROM b WHERE [cc lang="sql"] ) |
。
没有人想写后者,所以我们写
和
LEFT [ OUTER ] JOIN :将JOIN 表达式的左表添加到联合中,如上图所示。RIGHT [ OUTER ] JOIN :将JOIN 表达式的右表添加到联合中,如上图所示。FULL [ OUTER ] JOIN :JOIN 表达式的两个表都添加到联合中,如上图所示。
所有这些都可以与关键字
Oracle和SQL Server中有一些历史性的、不推荐使用的语法,在SQL标准有语法之前就已经支持
1 2 3 4 5 6 7 8 9 10 11 | -- 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标准指定了分区的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 |
号
部分结果:好的。
1 2 3 4 5 6 7 8 9 10 11 12 | +--------+------------+------------+ | 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 |
这里的要点是,无论
真的吗?没有其他答案?当然不是,因为它在SQL中没有本机语法,不幸的是(就像下面的反连接)。但是我们可以使用
1 2 3 4 5 6 | SELECT * FROM actor a WHERE EXISTS ( SELECT * FROM film_actor fa WHERE a.actor_id = fa.actor_id ) |
。
。好的。反连接
这与半连接正好相反(不过,注意不要使用
以下是所有没有电影的演员:好的。
1 2 3 4 5 6 | SELECT * FROM actor a WHERE NOT EXISTS ( SELECT * FROM film_actor fa WHERE a.actor_id = fa.actor_id ) |
有些人(尤其是mysql人)也会这样写反连接:好的。
1 2 3 4 5 | SELECT * FROM actor a LEFT JOIN film_actor fa USING (actor_id) WHERE film_id IS NULL |
。
我认为历史原因是表演。好的。横向连接
天哪,这个太酷了。我是唯一一个提到它的人?这是一个很酷的查询:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
。
它将找到每个演员收入最高的5部电影。每当你需要一个顶级的N-per-something查询时,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
好吧,也许这是欺骗,因为一个
这实际上是由Oracle和Informix实现的(据我所知),但它可以在PostgreSQL中使用数组和/或XML进行模拟,在SQL Server中使用XML进行模拟。好的。
1 2 3 4 5 6 7 | 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 |
如您所见,连接的类型比通常提到的"无聊"的
在我看来,我创造了一个比文字更好的例证:氧化镁
我要推我的宠物尿:使用关键字。
如果联接两侧的两个表都有正确命名的外键(即,相同的名称,而不仅仅是"id"),则可以使用它:
1 2 | SELECT ... FROM customers JOIN orders USING (customer_id) |
。
我发现这非常实用,可读性强,而且使用频率不够。