SQL JOIN和不同类型的JOIN

M-D*_*M-D 238 sql join

什么是SQL JOIN,什么是不同类型?

Anu*_*nup 324

W3schools的插图:


INNER JOIN  - 仅记录与两个表中的条件匹配的记录


LEFT JOIN  - 表1中的所有记录以及与表2中的条件匹配的记录


RIGHT JOIN  - 表2中的所有记录以及表1中与条件匹配的记录


FULL OUTER JOIN  - 左和右外连接的组合匹配ON子句但保留两个表


  • @KNU w3fools应该从他们拍摄照片的想法中获得赞誉.请参阅Jeff Atwood的**[SQL连接的可视化](http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/)**(是的,是共同撰写SO的人)和**[Ligaya Turmelle的链接文章](http://www.khankennels.com/blog/index.php/archives/2007/04/20/getting-joins/)**Jeff得到了这个想法并解释了它. (25认同)
  • @avi左右连接类似,如果你不打扰哪个是连接所基于的主表. (2认同)
  • @philipxy:这是一个奇怪的定义(即使你是对的).但我宁愿走另一条路,从交叉连接开始,然后"构建"内部连接.毕竟,仅仅交叉连接的概念使这些非正式和不准确的维恩图可视化无效...... (2认同)

M-D*_*M-D 242

什么是SQL JOIN

SQL JOIN 是一种从两个或多个数据库表中检索数据的方法.

有什么不同SQL JOIN

共有五个JOIN.他们是 :

  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
Run Code Online (Sandbox Code Playgroud)

1.加入或内部加入:

在这种类型中JOIN,我们获得与两个表中的条件匹配的所有记录,并且不报告两个表中不匹配的记录.

换句话说,INNER JOIN基于以下单一事实:只有两个匹配的条目才能列出表格.

请注意,JOIN没有任何其他JOIN的关键字(如INNER,OUTER,LEFT,等)是INNER JOIN.换句话说,JOIN是一个语法糖INNER JOIN(参见:JOIN和INNER JOIN之间的差异).

2.外部加入:

OUTER JOIN 检索

或者,来自一个表的匹配行和另一个表中的所有行,或者所有表中的所有行(无论是否匹配都无关紧要).

外部加入有三种:

2.1 LEFT OUTER JOIN或LEFT JOIN

此连接返回左表中的所有行以及右表中的匹配行.如果右表中没有列匹配,则返回NULL值.

2.2正确的外部加入或正确的加入

JOIN将返回右表中的所有行以及左表中的匹配行.如果左表中没有列匹配,则返回NULL值.

2.3完全外部加入或完全加入

这种JOIN结合LEFT OUTER JOINRIGHT OUTER JOIN.它在满足条件时从任一表返回行,并NULL在没有匹配时返回值.

换句话说,OUTER JOIN基于以下事实:只应列出其中一个表中的匹配条目(RIGHT或LEFT)或表中的BOTH(FULL).

Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.
Run Code Online (Sandbox Code Playgroud)

3.自然联合:

它基于以下两个条件:

  1. JOIN对所有与平等相同名称的列所做的.
  2. 从结果中删除重复的列.

这似乎更具理论性,因此(可能)大多数DBMS甚至不打扰支持这一点.

4.交叉加入:

它是所涉及的两个表的笛卡尔积.CROSS JOIN在大多数情况下,a的结果都没有意义.而且,我们根本不需要这个(或者至少需要,确切地说).

5.自我加入:

这不是一个不同形式的JOIN,而它是一个JOIN(INNER,OUTER等)的表格的给自己.

基于运算符的JOIN

根据用于JOIN子句的运算符,可以有两种类型的JOINs.他们是

  1. Equi JOIN
  2. Theta JOIN

1. Equi JOIN:

对于任何JOIN类型(INNER,OUTER等),如果我们只使用相等运算符(=),那么我们说它JOIN是一个EQUI JOIN.

2. Theta JOIN:

这是相同的,EQUI JOIN但它允许所有其他运算符,如>,<,> =等.

许多人认为两者EQUI JOIN和Theta JOIN相似INNER,OUTER 等等JOIN.但我坚信这是一个错误,并使这些想法含糊不清.因为INNER JOIN,OUTER JOIN等等都与表及其数据,而连接EQUI JOINTHETA JOIN仅与我们前使用运营商连接.

同样,有许多人认为NATURAL JOIN某种"特殊" EQUI JOIN.事实上,这是真的,因为我提到的第一个条件NATURAL JOIN.但是,我们不必仅仅NATURAL JOIN依靠s 来限制它.INNER JOINs,OUTER JOINs等也可以EQUI JOIN.

  • 虽然这似乎是合理的,但我不认为答案"什么是SQL连接"以任何方式传达有用的信息.答案作为一个整体是为已经理解加入的人写的参考,而不是那些提出这些问题的人.它也省略了引用,既支持其声明(如果做出明确的答案,则适当),并通过外部资源提供额外的解释.如果您正在尝试编写一个权威的答案来链接新的SQL用户,那么可能值得填补空白,特别是"什么是连接"部分. (13认同)
  • 还有相对较新的LATERAL JOIN .. SELECT*FROM r1,LATERAL fx(r1) (2认同)

nay*_*ies 66

定义:


JOINS是查询同时从多个表组合在一起的数据的方法.

JOINS的类型:


关注RDBMS有5种类型的连接:

  • Equi-Join:根据相等条件组合两个表中的常用记录.从技术上讲,通过使用equality-operator(=)来比较一个表的主键和另一个表的外键值的值,因此结果集包括来自两个表的公共(匹配)记录.有关实现,请参阅INNER-JOIN.

  • Natural-Join: 它是Equi-Join的增强版本,其中SELECT操作省略了重复列.有关实现,请参阅INNER-JOIN

  • Non-Equi-Join:它是Equi-join的反向,其中连接条件是使用非等于运算符(=),例如,!=,<=,> =,>,<或BETWEEN等.有关实现,请参阅INNER-JOIN.

  • Self-Join ::自定义的连接行为,表格与自身结合; 查询自引用表(或一元关系实体)通常需要这样做.有关实现,请参阅INNER-JOIN.

  • 笛卡尔积:它交叉组合两个表的所有记录,没有任何条件.从技术上讲,它返回没有WHERE-Clause的查询的结果集.

根据SQL关注和进步,有3种类型的连接,并且可以使用这些类型的连接实现所有RDBMS连接.

  1. INNER-JOIN:它合并(或组合)来自两个表的匹配行.匹配是基于表的公共列及其比较操作完成的.如果基于相等的条件则执行:EQUI-JOIN,否则执行非EQUI-Join.

  2. **OUTER-JOIN:**它合并(或组合)来自两个表的匹配行和具有NULL值的不匹配行.但是,可以自定义选择不匹配的行,例如,通过子类型从第一个表或第二个表中选择不匹配的行:LEFT OUTER JOIN和RIGHT OUTER JOIN.

    2.1.LEFT Outer JOIN(aka,LEFT-JOIN):从两个表中返回匹配的行,并且仅从LEFT表(即第一个表)中取消匹配.

    2.2.RIGHT Outer JOIN(aka,RIGHT-JOIN):返回两个表中的匹配行,仅从RIGHT表中取消匹配.

    2.3.FULL OUTER JOIN(又名OUTER JOIN):返回两个表的匹配和不匹配.

  3. CROSS-JOIN:此连接不合并/组合,而是执行笛卡尔积.

在此输入图像描述 注意:自联接可以通过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>;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

1.3:使用非Equi-join实现的INNER-JOIN

Select *
 FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;
Run Code Online (Sandbox Code Playgroud)

1.4:使用SELF-JOIN进行INNER-JOIN

Select *
 FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;
Run Code Online (Sandbox Code Playgroud)

2.1:OUTER JOIN(全外连接)

Select *
 FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;
Run Code Online (Sandbox Code Playgroud)

2.2:LEFT JOIN

Select *
 FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;
Run Code Online (Sandbox Code Playgroud)

2.3:正确加入

Select *
 FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;
Run Code Online (Sandbox Code Playgroud)

3.1:交叉加入

Select *
 FROM TableA CROSS JOIN TableB;
Run Code Online (Sandbox Code Playgroud)

3.2:CROSS JOIN-Self JOIN

Select *
 FROM Table1 A1 CROSS JOIN Table1 A2;
Run Code Online (Sandbox Code Playgroud)

//要么//

Select *
 FROM Table1 A1,Table1 A2;
Run Code Online (Sandbox Code Playgroud)


Luk*_*der 38

有趣的是,大多数其他答案都存在以下两个问题:

我最近写了一篇关于这个主题的文章:一个可能不完整的综合指南,用于在SQL中加入表的许多不同方法,我将在这里总结一下.

首要的是:JOIN是笛卡尔积

这就是维恩图解释它们如此不准确的原因,因为JOIN 在两个连接的表之间创建了一个笛卡尔积.维基百科很好地说明了这一点:

在此输入图像描述

笛卡尔积的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
Run Code Online (Sandbox Code Playgroud)

它将一个表中的所有行与另一个表中的所有行组合在一起:

资源:

+--------+   +------------+
| day    |   | department |
+--------+   +------------+
| Jan 01 |   | Dept 1     |
| Jan 02 |   | Dept 2     |
| ...    |   | Dept 3     |
| Jan 30 |   +------------+
| Jan 31 |
+--------+
Run Code Online (Sandbox Code Playgroud)

结果:

+--------+------------+
| 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     |
+--------+------------+
Run Code Online (Sandbox Code Playgroud)

如果我们只是写一个逗号分隔的表列表,我们将得到相同的:

-- CROSS JOINing two tables:
SELECT * FROM table1, table2
Run Code Online (Sandbox Code Playgroud)

INNER JOIN(Theta-JOIN)

一个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
Run Code Online (Sandbox Code Playgroud)

请注意,关键字INNER是可选的(MS Access除外).

(查看结果示例的文章)

EQUI JOIN

一种特殊的Theta-JOIN是equi JOIN,我们最常用.谓词将一个表的主键与另一个表的外键连接起来.如果我们使用Sakila数据库进行说明,我们可以写:

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
Run Code Online (Sandbox Code Playgroud)

这将所有演员与他们的电影结合起来

或者,在一些数据库上:

SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)
Run Code Online (Sandbox Code Playgroud)

USING()语法允许用于指定必须存在于一个JOIN操作的表的任一侧,并创建关于这些两列相等谓词的列.

天然加入

其他答案分别列出了这个"JOIN类型",但这没有意义.它只是equi JOIN的语法糖形式,这是Theta-JOIN或INNER JOIN的特例.NATURAL JOIN只收集所有正在连接的表并加入USING()这些列的列.由于意外匹配(如Sakila数据库中的LAST_UPDATE列),这几乎没有用处.

这是语法:

SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film
Run Code Online (Sandbox Code Playgroud)

外联

现在,与它创造了几种笛卡尔产品OUTER JOIN有点不同.我们可以写:INNER JOINUNION

-- 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>
)
Run Code Online (Sandbox Code Playgroud)

没有人想写后者,所以我们写OUTER JOIN(通常由数据库更好地优化).

就像INNER,关键字OUTER是可选的,在这里.

OUTER JOIN 有三种口味:

  • LEFT [ OUTER ] JOIN:JOIN表达式的左表添加到联合,如上所示.
  • RIGHT [ OUTER ] JOIN:JOIN表达式的右表被添加到联合,如上所示.
  • FULL [ OUTER ] JOIN:JOIN表达式的两个表都添加到联合中,如上所示.

所有这些都可以用关键字进行组合USING()NATURAL(其实我已经有一个真正的世界用例的NATURAL FULL JOIN最近)

替代语法

Oracle和SQL Server中有一些历史悠久的,已弃用的语法,OUTER JOIN在SQL标准具有以下语法之前就已经支持了这些语法:

-- 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
Run Code Online (Sandbox Code Playgroud)

话虽如此,请不要使用此语法.我只是在这里列出这个,以便您可以从旧的博客文章/遗留代码中识别它.

分区 OUTER JOIN

很少有人知道这一点,但SQL标准指定了分区OUTER JOIN(而Oracle实现了它).你可以写这样的东西:

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
Run Code Online (Sandbox Code Playgroud)

部分结果:

+--------+------------+------------+
| 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
Run Code Online (Sandbox Code Playgroud)

这里的要点是,无论是否JOIN在"JOIN的另一侧"匹配任何内容,连接的分区侧的所有行都将结束.长话短说:这是为了填补报告中的稀疏数据.很有用!

SEMI JOIN

真的吗?没有其他答案得到这个?当然不是,因为它在SQL中没有本机语法,不幸的是(就像下面的ANTI JOIN).但我们可以用IN()EXISTS(),如发现谁已经在电影中扮演的所有行动者:

SELECT *
FROM actor a
WHERE EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)
Run Code Online (Sandbox Code Playgroud)

WHERE a.actor_id = fa.actor_id谓词充当半连接谓词.如果您不相信,请查看执行计划,例如在Oracle中.您将看到数据库执行SEMI JOIN操作,而不是EXISTS()谓词.

在此输入图像描述

反加入

这仅仅是SEMI相反JOIN(注意不要使用NOT IN,虽然,因为它有一个重要的警告)

以下是没有电影的所有演员:

SELECT *
FROM actor a
WHERE NOT EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)
Run Code Online (Sandbox Code Playgroud)

有些人(特别是MySQL人)也写这样的ANTI JOIN:

SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL
Run Code Online (Sandbox Code Playgroud)

我认为历史原因是表现.

横向加入

天哪,这个太酷了.我是唯一一个提到它的人吗?这是一个很酷的查询:

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
Run Code Online (Sandbox Code Playgroud)

它将为每个演员找到TOP 5创收电影.每当你需要一个TOP-N-per-something查询时,LATERAL JOIN你的朋友就会成为你的朋友.如果您是SQL Server人员,那么您知道该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
Run Code Online (Sandbox Code Playgroud)

好吧,也许这是作弊,因为一个LATERAL JOINAPPLY表达式实际上是一个产生几行的"相关子查询".但如果我们允许"相关子查询",我们也可以谈论......

MULTISET

这只是由Oracle和Informix实际实现的(据我所知),但它可以使用数组和/或XML在PostgreSQL中进行模拟,在SQL Server中使用XML进行模拟.

MULTISET生成相关子查询,并在外部查询中嵌套生成的行集.以下查询选择所有演员,并为每个演员在嵌套集合中收集他们的电影:

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
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,也有更多类型的JOIN不仅仅是"无聊"的INNER,OUTERCROSS JOIN那些通常提及.我的文章中有更多细节.请停止使用维恩图来说明它们.


Gis*_*way 10

在我看来,我创造了一个比文字更好解释的插图: SQL Join表的解释