SQL join:where子句与on子句

SQL join: where clause vs. on clause

在读取之后,这不是显式和隐式SQL联接的副本。答案可能是相关的(甚至是相同的),但问题是不同的。

有什么区别?每个都应该有什么区别?

如果我正确理解这一理论,查询优化器应该能够同时使用这两种方法。


他们不是一回事。

考虑以下问题:

1
2
3
4
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345

1
2
3
4
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
    AND Orders.ID = 12345

第一个将返回订单及其行(如果有),用于订单号12345。第二个将返回所有订单,但只有订单12345将有任何相关联的行。

使用INNER JOIN时,这些条款实际上是等效的。然而,仅仅因为它们在功能上是相同的,它们产生相同的结果,并不意味着这两种从句具有相同的语义意义。


  • 内部连接不重要
  • 外部连接事项

    A.WHERE条款:加入后。连接发生后将筛选记录。

    b.ON条款-加入前。连接前将筛选记录(来自右表)。这可能会在结果中结束为空(因为外部联接)。

示例:考虑下表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    1. documents:
     | id    | name        |
     --------|-------------|
     | 1     | Document1   |
     | 2     | Document2   |
     | 3     | Document3   |
     | 4     | Document4   |
     | 5     | Document5   |


    2. downloads:
     | id   | document_id   | username |
     |------|---------------|----------|
     | 1    | 1             | sandeep  |
     | 2    | 1             | simi     |
     | 3    | 2             | sandeep  |
     | 4    | 2             | reya     |
     | 5    | 3             | simi     |

a)在WHERE条款内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  SELECT documents.name, downloads.id
    FROM documents
    LEFT OUTER JOIN downloads
      ON documents.id = downloads.document_id
    WHERE username = 'sandeep'

 FOR above query the intermediate JOIN TABLE will look LIKE this.

    | id(FROM documents) | name         | id (FROM downloads) | document_id | username |
    |--------------------|--------------|---------------------|-------------|----------|
    | 1                  | Document1    | 1                   | 1           | sandeep  |
    | 1                  | Document1    | 2                   | 1           | simi     |
    | 2                  | Document2    | 3                   | 2           | sandeep  |
    | 2                  | Document2    | 4                   | 2           | reya     |
    | 3                  | Document3    | 5                   | 3           | simi     |
    | 4                  | Document4    | NULL                | NULL        | NULL     |
    | 5                  | Document5    | NULL                | NULL        | NULL     |

  After applying the `WHERE` clause AND selecting the listed attributes, the RESULT will be:

   | name         | id |
   |--------------|----|
   | Document1    | 1  |
   | Document2    | 3  |

b)JOIN条款内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  SELECT documents.name, downloads.id
  FROM documents
    LEFT OUTER JOIN downloads
      ON documents.id = downloads.document_id
        AND username = 'sandeep'

FOR above query the intermediate JOIN TABLE will look LIKE this.

    | id(FROM documents) | name         | id (FROM downloads) | document_id | username |
    |--------------------|--------------|---------------------|-------------|----------|
    | 1                  | Document1    | 1                   | 1           | sandeep  |
    | 2                  | Document2    | 3                   | 2           | sandeep  |
    | 3                  | Document3    | NULL                | NULL        | NULL     |
    | 4                  | Document4    | NULL                | NULL        | NULL     |
    | 5                  | Document5    | NULL                | NULL        | NULL     |

Notice how the ROWS IN `documents` that did NOT MATCH BOTH the conditions are populated WITH `NULL` VALUES.

After Selecting the listed attributes, the RESULT will be:

   | name       | id   |
   |------------|------|
   |  Document1 | 1    |
   |  Document2 | 3    |
   |  Document3 | NULL |
   |  Document4 | NULL |
   |  Document5 | NULL |


INNER JOIN上,它们是可互换的,优化器将随意重新排列它们。

OUTER JOIN上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧。

我根据可读性将它们放在任何一个地方。


我的做法是:

  • 如果要执行INNER JOIN,请始终将连接条件放在ON子句中。因此,不要在ON条款中添加任何WHERE条件,将它们放在WHERE条款中。

  • 如果要执行LEFT JOIN操作,请在ON子句中为联接右侧的表添加任何where条件。这是必须的,因为添加引用联接右侧的WHERE子句会将联接转换为内部联接。

    例外情况是当您在查找不在特定表中的记录时。您可以这样将对右联接表中唯一标识符的引用添加到WHERE子句中:WHERE t2.idfield IS NULL。因此,您应该引用联接右侧的表的唯一时间是查找那些不在表中的记录。


在内部连接上,它们的含义相同。但是,在外部联接中会得到不同的结果,这取决于您是否将联接条件放在WHERE和ON子句中。看看这个相关的问题和答案(由我)。

我认为习惯于总是把连接条件放在ON子句中是最有意义的(除非它是一个外部连接,而实际上您确实希望它放在WHERE子句中),因为它使阅读您的查询的任何人都能更清楚地了解表连接的条件,而且它还有助于防止WHERE子句成为许多LIN很长。


这篇文章清晰地解释了区别。It also explains the"on joined u condition vs where joined u condition or joined n's null."

在该条款过滤从条款到附录的结果时,该条款被用来产生从条款到附录表之间的表格。

  • 如果你想制作一张表格,结果加入两张表格,那么你应该在条款中确定表格是如何连接的。从课程中,这也可以从原始表格中滤出一个内部连接的路径。
  • 如果你想过滤两个侧面的连接产品,那么你应该使用该条款。

  • 在条款的位置与条款的位置之间有很大的差别,当它来到左边。

    这是一个例子:

    ZZU1

    There fid is id of table t2.

    1
    2
    3
    4
    5
    6
    7
    8
    mysql> DESC t2;
    +-------+-------------+------+-----+---------+-------+
    | FIELD | TYPE        | NULL | KEY | DEFAULT | Extra |
    +-------+-------------+------+-----+---------+-------+
    | id    | INT(11)     | NO   |     | NULL    |       |
    | v     | VARCHAR(10) | NO   |     | NULL    |       |
    +-------+-------------+------+-----+---------+-------+
    2 ROWS IN SET (0.00 sec)

    Query on"on clause":

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    mysql> SELECT * FROM `t1` LEFT JOIN t2 ON fid = t2.id AND t1.v = 'K'
        -> ;
    +----+-----+---+------+------+
    | id | fid | v | id   | v    |
    +----+-----+---+------+------+
    |  1 |   1 | H | NULL | NULL |
    |  2 |   1 | B | NULL | NULL |
    |  3 |   2 | H | NULL | NULL |
    |  4 |   7 | K | NULL | NULL |
    |  5 |   5 | L | NULL | NULL |
    +----+-----+---+------+------+
    5 ROWS IN SET (0.00 sec)

    Query on"where clause":

    1
    2
    3
    4
    5
    6
    7
    mysql> SELECT * FROM `t1` LEFT JOIN t2 ON fid = t2.id WHERE t1.v = 'K';
    +----+-----+---+------+------+
    | id | fid | v | id   | v    |
    +----+-----+---+------+------+
    |  4 |   7 | K | NULL | NULL |
    +----+-----+---+------+------+
    1 ROW IN SET (0.00 sec)

    很清楚第一个从T1返回一个记录,如果有的话,从T2返回一个记录。

    第二个从T1返回的路径,但只为T1.V=K will have any return row with it.


    在优化器方面,用on或where定义联接子句不会有什么不同。

    但是,imho,我认为在执行连接时使用on子句要清楚得多。这样,您就有了一个查询的特定部分,该部分指示如何处理联接,而不是与其余的WHERE子句混合。


    我认为这是连接序列效应。在左上联接的情况下,SQL首先执行左联接,然后执行where筛选器。在downer的情况下,首先查找orders.id=12345,然后执行join。


    对于一个内在连接,WHEREON可以互换使用。事实上,有可能使用ON在相关的副产品中。For example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    UPDATE mytable
    SET myscore=100
    WHERE EXISTS (
    SELECT 1 FROM table1
    INNER JOIN table2
    ON (table2.key = mytable.key)
    INNER JOIN table3
    ON (table3.key = table2.key AND table3.key = table1.key)
    ...
    )

    This is(IMHO)otterly confusing to a human,and it's very easy to forget to link table1to anything(因为"driver"table doesn't have an"on"clause,but it's legal.


    您试图加入数据还是过滤数据?

    对于可读性,将这些用例分别隔离到on和where是最有意义的。

    • 在上加入数据
    • 在其中筛选数据

    在WHERE子句中存在联接条件和筛选条件的情况下,读取查询可能会变得非常困难。

    从性能上看,您不应该看到差异,尽管不同类型的SQL有时处理查询计划的方式不同,因此值得尝试ˉ\_(ツ)_/ˉ(请注意缓存会影响查询速度)

    另外,正如其他人所指出的,如果使用外部联接,那么如果将筛选条件放在ON子句中,则会得到不同的结果,因为它只影响其中一个表。

    我在这里写了一篇更深入的文章:https://dataschool.com/learn/difference-between-where-and-on-in-sql


    对于更好的性能表,应该有一个特殊的索引柱用于加入。

    所以,如果你的条件不是指数柱中的一个,那么我怀疑它最好保持在哪里。

    所以你用指数柱加入,然后加入到非指数柱上的条件。


    通常,过滤是在两个表格中的一个条款已经结合的地方进行的。有可能,尽管你可能想在他们进来之前过滤一张或两张桌子。I.E.The where clause applies to the whole result set whereas the on clause only applies to the join in question.


    在SQL中,"where"和"on"子句是一种条件语句,但它们之间的主要区别是,在select/update语句中使用"where"子句指定条件,而在join中使用"on"子句,在join中验证或检查目标表和源表中的记录在表之前是否匹配。已加入

    例如:—"在哪里"

    1
    SELECT * FROM employee WHERE employee_id=101

    例如:—"开"

    有两个表Employee和Employee_Details,匹配的列是Employee_ID。

    1
    2
    3
    SELECT * FROM employee
    INNER JOIN employee_details
    ON employee.employee_id = employee_details.employee_id

    希望我已经回答了你的问题。回复任何澄清。


    I think this distinction can best be explained by the logical order of operations in SQL,which is,admissed:

    • 包括连接在内的
    • WHERE
    • 法国电力公司
    • 聚集
    • 法国电力公司
    • 法国电力公司
    • 法国电力公司
    • 法国电力公司
    • UNIONINTERSECTEXCEPT
    • 法国电力公司
    • 法国电力公司
    • 法国电力公司

    Joins不是选择声明的条款,而是FROM内的操作员。运算符有"Already happened"logically by the time logical processing reaches the WHERE这意味着,以LEFT JOIN为例,外界的语句已经在WHERE时发生。

    我在这张博客上解释了更多的事例。When running this query:

    1
    2
    3
    4
    5
    6
    SELECT a.actor_id, a.first_name, a.last_name, COUNT(fa.film_id)
    FROM actor a
    LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
    WHERE film_id < 10
    GROUP BY a.actor_id, a.first_name, a.last_name
    ORDER BY COUNT(fa.film_id) ASC;

    即使演员不在电影里演出,演员也会被过滤,就像他们的FILM_IDWill be NULLand the WHERE一样。The result is something like:

    1
    2
    3
    4
    5
    6
    7
    ACTOR_ID  FIRST_NAME  LAST_NAME  COUNT
    --------------------------------------
    194       MERYL       ALLEN      1
    198       MARY        KEITEL     1
    30        SANDRA      PECK       1
    85        MINNIE      ZELLWEGER  1
    123       JULIANNE    DENCH      1

    I.E.just as if we inner joined the two tables.如果我们在ON中移动过滤器前缀,它现在成为外部连接的标准:

    1
    2
    3
    4
    5
    6
    SELECT a.actor_id, a.first_name, a.last_name, COUNT(fa.film_id)
    FROM actor a
    LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
      AND film_id < 10
    GROUP BY a.actor_id, a.first_name, a.last_name
    ORDER BY COUNT(fa.film_id) ASC;

    意思是,结果将包括演员,没有任何电影,或没有任何电影与EDOCX1〕〔28〕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ACTOR_ID  FIRST_NAME  LAST_NAME     COUNT
    -----------------------------------------
    3         ED          CHASE         0
    4         JENNIFER    DAVIS         0
    5         JOHNNY      LOLLOBRIGIDA  0
    6         BETTE       NICHOLSON     0
    ...
    1         PENELOPE    GUINESS       1
    200       THORA       TEMPLE        1
    2         NICK        WAHLBERG      1
    198       MARY        KEITEL        1

    短期内

    总是把你的假设放在最有意义的地方,逻辑上。


    让我们考虑一下这些表:

    1
    id | SomeData

    1
    id | id_A | SomeOtherData

    id_A是表A的外键

    编写此查询:

    1
    2
    3
    4
    SELECT *
    FROM A
    LEFT JOIN B
    ON A.id = B.id_A;

    将提供此结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    / : part OF the RESULT
                                           B
                          +---------------------------------+
                A         |                                 |
    +---------------------+-------+                         |
    |/////////////////////|///////|                         |
    |/////////////////////|///////|                         |
    |/////////////////////|///////|                         |
    |/////////////////////|///////|                         |
    |/////////////////////+-------+-------------------------+
    |/////////////////////////////|
    +-----------------------------+

    A中是什么而不是B中是什么意味着B有空值。

    现在,让我们考虑一下B.id_A中的一个特定部分,并从前面的结果中突出显示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    / : part OF the RESULT
    * : part OF the RESULT WITH the specific B.id_A
                                           B
                          +---------------------------------+
                A         |                                 |
    +---------------------+-------+                         |
    |/////////////////////|///////|                         |
    |/////////////////////|///////|                         |
    |/////////////////////+---+///|                         |
    |/////////////////////|***|///|                         |
    |/////////////////////+---+---+-------------------------+
    |/////////////////////////////|
    +-----------------------------+

    编写此查询:

    1
    2
    3
    4
    5
    SELECT *
    FROM A
    LEFT JOIN B
    ON A.id = B.id_A
    AND B.id_A = SpecificPart;

    将提供此结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    / : part OF the RESULT
    * : part OF the RESULT WITH the specific B.id_A
                                           B
                          +---------------------------------+
                A         |                                 |
    +---------------------+-------+                         |
    |/////////////////////|       |                         |
    |/////////////////////|       |                         |
    |/////////////////////+---+   |                         |
    |/////////////////////|***|   |                         |
    |/////////////////////+---+---+-------------------------+
    |/////////////////////////////|
    +-----------------------------+

    因为这样会消除内部联接中不在B.id_A = SpecificPart中的值。

    现在,让我们将查询更改为:

    1
    2
    3
    4
    5
    SELECT *
    FROM A
    LEFT JOIN B
    ON A.id = B.id_A
    WHERE B.id_A = SpecificPart;

    结果是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    / : part OF the RESULT
    * : part OF the RESULT WITH the specific B.id_A
                                           B
                          +---------------------------------+
                A         |                                 |
    +---------------------+-------+                         |
    |                     |       |                         |
    |                     |       |                         |
    |                     +---+   |                         |
    |                     |***|   |                         |
    |                     +---+---+-------------------------+
    |                             |
    +-----------------------------+

    因为整个结果都是针对B.id_A = SpecificPart过滤的,删除了a中不在b中的部分B.id_A = NULL


    这是我的解决方案。

    1
    2
    3
    4
    SELECT song_ID,songs.fullname, singers.fullname
    FROM music JOIN songs ON songs.ID = music.song_ID  
    JOIN singers ON singers.ID = music.singer_ID
    GROUP BY songs.fullname

    你必须要有GROUP BY才能让它工作。

    希望能帮上忙。