SQL JOIN和不同类型的JOIN

时间:2013-07-30 11:48:12

标签: sql join

什么是SQL JOIN以及有哪些不同的类型?

6 个答案:

答案 0 :(得分:320)

来自W3schools的插图:


INNER JOIN - Only records which match the condition in both tables


LEFT JOIN - All records from table 1 in conjunction with records which match the condition in table 2


RIGHT JOIN - All records from table 2 in conjunction with records from table 1 which match the condition


FULL OUTER JOIN - Combination of both Left and Right Outer joins matching ON clause but preserving both tables


答案 1 :(得分:236)

什么是SQL JOIN

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

有哪些不同的SQL JOIN s?

总共有五个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

1。加入或内部加入:

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

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

请注意,JOIN没有任何其他JOIN个关键字(例如INNEROUTERLEFT等)是INNER JOIN。换句话说,JOININNER JOIN的句法糖(参见:Difference between JOIN and INNER JOIN)。

2。外联赛:

OUTER JOIN检索

无论哪种,        一个表中的匹配行和另一个表中的所有行      要么,        所有表格中的所有行(无论是否匹配都无关紧要。)

外部加入有三种:

2.1 LEFT OUTER JOIN或LEFT JOIN

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

2.2 RIGHT OUTER JOIN或RIGHT JOIN

这个JOIN返回右表中的所有行以及来自的匹配行      左桌。如果左表中没有列匹配,则返回NULL个值。

2.3 FULL OUTER JOIN或FULL JOIN

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

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

Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.

3。自然加入:

它基于以下两个条件:

  1. JOIN是在所有具有相同名称的列上进行的。
  2. 从结果中删除重复的列。
  3. 这似乎更具理论性,因此(可能)大多数是DBMS 甚至不打扰支持这个。

    4。交叉加入:

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

    5。自我加入:

    它不是JOIN的另一种形式,而是表格的JOININNEROUTER等)。

    基于运算符的JOIN

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

    1. Equi JOIN
    2. Theta JOIN
    3. 1。 Equi JOIN:

      对于JOIN类型(INNEROUTER等),如果我们只使用相等运算符(=),那么我们就说 JOINEQUI JOIN

      2。 Theta JOIN:

      这与EQUI JOIN相同,但它允许所有其他运营商,例如>,<,> =等。

        

      许多人认为EQUI JOIN和Theta JOIN都与INNER相似,OUTER   等JOIN。但我坚信这是一个错误,并使之成为现实   想法含糊不清。因为INNER JOINOUTER JOIN等都与之相关   表格及其数据只有EQUI JOINTHETA JOIN   与我们在前者使用的运营商联系。

           

      同样,有许多人认为NATURAL JOIN是某种形式   “奇特的”EQUI JOIN。事实上,这是真的,因为第一个   我为NATURAL JOIN提到的条件。但是,我们没有   将其仅限于NATURAL JOIN s。 INNER JOIN s,OUTER JOIN s   等等也可能是EQUI JOIN

答案 2 :(得分:64)

定义:


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

JOINS的类型:


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

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

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

  • 非Equi-Join: Equi-join的反向,其中连接条件是使用除了相等运算符(=)之外的其他运算符,例如,!=,< =,> = ,>,<或者BETWEEN等。有关实施,请参阅INNER-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 (a.k.a,LEFT-JOIN):从两个表中返回匹配的行,并且只从LEFT表(即第一个表)中取消匹配。

    2.2。 RIGHT Outer JOIN (a.k.a,RIGHT-JOIN):返回两个表中的匹配行,仅与RIGHT表不匹配。

    2.3。 FULL OUTER JOIN (a.k.a OUTER JOIN):从两个表中返回匹配和不匹配。

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

  4. enter image description here 注意:自联接可以通过INNER-JOIN,OUTER-JOIN和CROSS-JOIN根据需求实现,但表必须自己加入。

    <强> For more information:

    示例:

    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:自然联接实施

    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:外部联接(完全外部联接)

    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;
    

    // // OR

    Select *
     FROM Table1 A1,Table1 A2;
    

答案 3 :(得分:36)

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

我最近写了一篇关于这个主题的文章:A Probably Incomplete, Comprehensive Guide to the Many Different Ways to JOIN Tables in SQL,我将在这里总结一下。

首先:JOIN是笛卡儿产品

这就是为什么维恩图解释它们如此不准确,因为JOIN在两个连接的表之间创建cartesian product。维基百科很好地说明了这一点:

enter image description here

笛卡尔积的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(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

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

look at the article for result examples

EQUI JOIN

一种特殊的Theta-JOIN是equi JOIN,我们最常用。谓词将一个表的主键与另一个表的外键连接起来。如果我们使用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)

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

NATURAL JOIN

其他答案列出了这个&#34; JOIN类型&#34;分开,但这没有意义。它只是equi JOIN的语法糖形式,这是Theta-JOIN或INNER JOIN的一个特例。 NATURAL JOIN只收集所有列,这些列对于要加入的两个表都是通用的,并且连接USING()这些列。由于意外匹配(例如Sakila database中的LAST_UPDATE列),这几乎没用。

这是语法:

SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film

外部加入

现在,OUTER JOININNER 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 ] JOINJOIN表达式的左表添加到联合中,如上所示。
  • RIGHT [ OUTER ] JOINJOIN表达式的右表已添加到联合中,如上所示。
  • FULL [ OUTER ] JOINJOIN表达式的两个表都添加到联合中,如上所示。

所有这些都可以与关键字USING()NATURALI've actually had a real world use-case for a NATURAL FULL JOIN recently

结合使用

替代语法

Oracle和SQL Server中有一些历史悠久的,已弃用的语法,在SQL标准具有此语法之前已经支持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

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

已分区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

部分结果:

+--------+------------+------------+
| 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匹配&#34; JOIN&#34;的另一侧是否有任何内容,连接的分区一侧的所有行都会结束。长话短说:这是为了填补报告中的稀疏数据。非常有用!

SEMI JOIN

真的?没有其他答案得到这个?当然不是,because it doesn't have a native syntax in SQL, unfortunately(就像下面的ANTI JOIN一样)。但我们可以使用IN()EXISTS(),例如找到所有参演过电影的演员:

SELECT *
FROM actor a
WHERE EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

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

enter image description here

ANTI JOIN

这与SEMI JOIN相反(be careful not to use NOT IN though,因为它有一个重要的警告)

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

SELECT *
FROM actor a
WHERE NOT EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

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

SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL

我认为历史原因是表现。

LATERAL JOIN

OMG,这个太酷了。我是唯一一个提到它的人吗?这是一个很酷的问题:

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

它将为每个演员找到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

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

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

正如您所看到的,有更多类型的JOIN而不仅仅是&#34;无聊&#34;通常提到的INNEROUTERCROSS JOINMore details in my article。请停止使用维恩图来说明它们。

答案 4 :(得分:9)

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

答案 5 :(得分:-3)

我要推开我的宠儿:USING关键字。

如果JOIN两边的两个表都正确命名了外键(即同名,而不仅仅是“id”),那么可以使用:

SELECT ...
FROM customers JOIN orders USING (customer_id)

我发现这非常实用,可读,而且经常使用不够。