ChatGPT解决这个技术问题 Extra ChatGPT

SQL JOIN - WHERE 子句与 ON 子句

阅读后,这不是Explicit vs Implicit SQL Joins 的重复。答案可能相关(甚至相同),但问题却不同。

有什么区别,每个应该做什么?

如果我正确理解了理论,查询优化器应该能够互换使用两者。

只是为了将来的读者和您的信息,您应该阅读 sql 执行顺序。这将帮助您更准确地了解潜在的差异。

J
Joel Coehoorn

它们不是同一件事。

考虑以下查询:

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID 
    AND Orders.ID = 12345

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

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


通过将 where 子句放在内部连接的“on”子句中,您会获得更好的性能吗?
@FistOfFury Sql Server 使用查询优化器过程来编译和评估您的代码以生成它可以的最佳执行计划。它并不完美,但大多数时候它并不重要,无论哪种方式你都会得到相同的执行计划。
在 Postgres 中,我注意到它们不等价并导致不同的查询计划。如果使用ON,则导致使用materialize。如果您使用 WHERE,则它使用哈希。物化有一个更糟糕的情况,它的成本是散列的 10 倍。这是使用一组 ID 而不是单个 ID。
@JamesHutchison 很难根据这样的观察到的行为做出可靠的性能概括。前一天是真的,下一天往往是错误的,因为这是一个实现细节,而不是记录在案的行为。数据库团队一直在寻找提高优化器性能的地方。如果 ON 行为没有改进以匹配 WHERE,我会感到惊讶。除了诸如“一般性能改进”之类的内容之外,它甚至可能不会出现在不同版本的发行说明中的任何地方。
@FiHoran 这不是 Sql Server 的工作方式。当统计数据显示它可能有用时,它将根据 WHERE 子句中的项目积极地进行预过滤。
M
Makyen

对于内部连接无关紧要

外部连接的事项WHERE 子句:加入后。加入后将过滤记录。湾。 ON 子句 - 在加入之前。加入前将过滤记录(来自右表)。这可能最终在结果中为 null(因为 OUTER join)。

示例:考虑下表:

文档:id 名称 1 Document1 2 Document2 3 Document3 4 Document4 5 Document5 下载:id document_id 用户名 1 1 sandeep 2 1 simi 3 2 sandeep 4 2 reya 5 3 simi

a) 在 WHERE 子句内:

   SELECT documents.name, downloads.id
     FROM documents
     LEFT OUTER JOIN downloads
       ON documents.id = downloads.document_id
     WHERE username = 'sandeep'

对于上述查询,中间连接表将如下所示。

id(来自文档) name id(来自下载) 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

应用 WHERE 子句并选择列出的属性后,结果将是:

名称 id Document1 1 Document2 3

b) 在 JOIN 子句内

   SELECT documents.name, downloads.id
   FROM documents
     LEFT OUTER JOIN downloads
       ON documents.id = downloads.document_id
         AND username = 'sandeep'

对于上述查询,中间连接表将如下所示。

id(来自文档) name id(来自下载) 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

请注意 documents 中与这两个条件都不匹配的行是如何用 NULL 值填充的。

选择列出的属性后,结果将是:

名称 id Document1 1 Document2 3 Document3 NULL Document4 NULL Document5 NULL


IMO 这是最好的答案,因为它清楚地展示了其他流行答案的“幕后”发生了什么。
很好的解释....干得好! - 只是好奇你做了什么来得到那个intermediate join table?。一些“解释”命令?
@ManuelJordan 不,这只是为了解释。数据库可能会做一些比创建中间表更高效的事情。
明白了,我假设可能使用了第三种工具。
这是正确解释的好答案。我仍然认为值得一提的是,大多数(如果不是全部)SQL 服务器在应用 WHERE 条件之前实际上并没有像这样创建完整的中间表。他们都有优化!知道这一点非常重要,因为当您的查询包含具有数百万行的表的许多 JOINS 时,但您的 WHERE 条件将结果集限制为仅几行时,考虑创建这个大笛卡尔积中间体的性能table 只是扔掉 99.9% 的结果行可能会很吓人。 :) 和误导。
C
Cade Roux

INNER JOIN 上它们可以互换,优化器会随意重新排列它们。

OUTER JOIN 上,它们不一定可以互换,具体取决于它们所依赖的联接的哪一侧。

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


在 Where 子句中可能更清楚,尤其是在 Linq-To-Entities lambda 表达式 Orders.Join( OrderLines, x => x.ID, x => OrderID, (o,l) => new {Orders = o, Lines = l}).Where( ol => ol.Orders.ID = 12345)
M
Marc.2377

我这样做的方式是:

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

如果您正在执行 LEFT JOIN,请将任何 WHERE 条件添加到连接右侧表的 ON 子句。这是必须的,因为添加引用连接右侧的 WHERE 子句会将连接转换为 INNER JOIN。例外情况是当您查找不在特定表中的记录时。您可以通过这种方式将 RIGHT JOIN 表中对唯一标识符(永远不是 NULL)的引用添加到 WHERE 子句:WHERE t2.idfield IS NULL。因此,您应该引用连接右侧的表的唯一时间是查找那些不在表中的记录。


这是迄今为止我读到的最好的答案。一旦您的大脑了解左连接将返回左表中的所有行并且您必须稍后过滤它,这完全是有道理的。
如果你用一个可以为空的列外连接一个表,那么你仍然可以“在哪里”该列为空而不使其成为内连接?这并不完全是在寻找不在特定表中的记录。您可以寻找 1. 不存在 2. 根本没有价值。
我的意思是你可以同时寻找:“1. 不存在 2. 根本没有价值”。这适用于该字段不是 idfield 的情况。
正如我遇到的这种情况:在没有紧急联系人的情况下寻找包括大一新生(数据尚未输入)的参与者。
C
Community

在内部连接中,它们的含义相同。但是,根据您是否将连接条件放在 WHERE 与 ON 子句中,您将在外连接中得到不同的结果。看看 this related questionthis answer(我的)。

我认为养成始终将连接条件放在 ON 子句中的习惯是最有意义的(除非它是外连接并且您确实希望在 where 子句中使用它),因为它使阅读您的查询的任何人都更清楚连接表的条件是什么,它还有助于防止 WHERE 子句长达数十行。


V
Vlad Mihalcea

表关系

考虑到我们有以下 postpost_comment 表:

https://i.stack.imgur.com/YjnW7.png

post 有以下记录:

| id | title     |
|----|-----------|
| 1  | Java      |
| 2  | Hibernate |
| 3  | JPA       |

post_comment 具有以下三行:

| id | review    | post_id |
|----|-----------|---------|
| 1  | Good      | 1       |
| 2  | Excellent | 1       |
| 3  | Awesome   | 2       |

SQL 内连接

SQL JOIN 子句允许您关联属于不同表的行。例如,CROSS JOIN 将创建一个笛卡尔积,其中包含两个连接表之间所有可能的行组合。

虽然 CROSS JOIN 在某些情况下很有用,但大多数时候,您希望根据特定条件连接表。而且,这就是 INNER JOIN 发挥作用的地方。

SQL INNER JOIN 允许我们根据通过 ON 子句指定的条件过滤连接两个表的笛卡尔积。

SQL INNER JOIN - 在“始终为真”条件下

如果您提供“始终为真”条件,则 INNER JOIN 不会过滤连接的记录,结果集将包含两个连接表的笛卡尔积。

例如,如果我们执行以下 SQL INNER JOIN 查询:

SELECT
   p.id AS "p.id",
   pc.id AS "pc.id"
FROM post p
INNER JOIN post_comment pc ON 1 = 1

我们将获得 postpost_comment 记录的所有组合:

| p.id    | pc.id      |
|---------|------------|
| 1       | 1          |
| 1       | 2          |
| 1       | 3          |
| 2       | 1          |
| 2       | 2          |
| 2       | 3          |
| 3       | 1          |
| 3       | 2          |
| 3       | 3          |

因此,如果 ON 子句条件为“始终为真”,则 INNER JOIN 就等同于 CROSS JOIN 查询:

SELECT
   p.id AS "p.id",
   pc.id AS "pc.id"
FROM post p
CROSS JOIN post_comment
WHERE 1 = 1
ORDER BY p.id, pc.id

SQL INNER JOIN - ON“总是假”条件

另一方面,如果 ON 子句条件为“始终为假”,则所有连接的记录都将被过滤掉,结果集将为空。

因此,如果我们执行以下 SQL INNER JOIN 查询:

SELECT
   p.id AS "p.id",
   pc.id AS "pc.id"
FROM post p
INNER JOIN post_comment pc ON 1 = 0
ORDER BY p.id, pc.id

我们不会得到任何结果:

| p.id    | pc.id      |
|---------|------------|

这是因为上面的查询等价于下面的 CROSS JOIN 查询:

SELECT
   p.id AS "p.id",
   pc.id AS "pc.id"
FROM post p
CROSS JOIN post_comment
WHERE 1 = 0
ORDER BY p.id, pc.id

SQL INNER JOIN - 使用外键和主键列的 ON 子句

最常见的 ON 子句条件是子表中的外键列与父表中的主键列匹配的条件,如以下查询所示:

SELECT
   p.id AS "p.id",
   pc.post_id AS "pc.post_id",
   pc.id AS "pc.id",
   p.title AS "p.title",
   pc.review  AS "pc.review"
FROM post p
INNER JOIN post_comment pc ON pc.post_id = p.id
ORDER BY p.id, pc.id

在执行上述 SQL INNER JOIN 查询时,我们得到以下结果集:

| p.id    | pc.post_id | pc.id      | p.title    | pc.review |
|---------|------------|------------|------------|-----------|
| 1       | 1          | 1          | Java       | Good      |
| 1       | 1          | 2          | Java       | Excellent |
| 2       | 2          | 3          | Hibernate  | Awesome   |

因此,只有符合 ON 子句条件的记录才会包含在查询结果集中。在我们的例子中,结果集包含所有 post 及其 post_comment 记录。没有关联 post_commentpost 行被排除,因为它们不能满足 ON 子句条件。

同样,上面的 SQL INNER JOIN 查询等价于下面的 CROSS JOIN 查询:

SELECT
   p.id AS "p.id",
   pc.post_id AS "pc.post_id",
   pc.id AS "pc.id",
   p.title AS "p.title",
   pc.review  AS "pc.review"
FROM post p, post_comment pc
WHERE pc.post_id = p.id

未命中的行是满足 WHERE 子句的行,只有这些记录才会包含在结果集中。这是可视化 INNER JOIN 子句如何工作的最佳方式。

| p.id | pc.post_id | pc.id | p.title   | pc.review |
|------|------------|-------|-----------|-----------|
| 1    | 1          | 1     | Java      | Good      |
| 1    | 1          | 2     | Java      | Excellent |

 | 1    | 2          | 3     | Java      | Awesome   |


 | 2    | 1          | 1     | Hibernate | Good      |


 | 2    | 1          | 2     | Hibernate | Excellent |

| 2    | 2          | 3     | Hibernate | Awesome   |

 | 3    | 1          | 1     | JPA       | Good      |


 | 3    | 1          | 2     | JPA       | Excellent |


 | 3    | 2          | 3     | JPA       | Awesome   |

结论

INNER JOIN 语句可以重写为 CROSS JOIN,其 WHERE 子句与您在 INNER JOIN 查询的 ON 子句中使用的条件匹配。

并不是说这仅适用于 INNER JOIN,不适用于 OUTER JOIN。


感谢你的回答。这是一本很好的读物,但除了这里提出的问题外,它讨论了所有内容
H
Hrishikesh Mishra

当涉及到左连接时,where 子句与 on 子句之间存在很大差异。

这是示例:

mysql> desc t1; 
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |
| fid   | int(11)     | NO   |     | NULL    |       |
| v     | varchar(20) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

fid 是表 t2 的 id。

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)

查询“on 子句”:

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)

查询“where子句”:

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.v = 'K'。

第二个查询从 t1 返回行,但仅对于 t1.v = 'K' 将有任何关联的行。


C
Cid

让我们考虑这些表:

一个

id | SomeData

id | id_A | SomeOtherData

id_A 是表 A 的外键

编写此查询:

SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A;

将提供此结果:

/ : part of the result
                                       B
                      +---------------------------------+
            A         |                                 |
+---------------------+-------+                         |
|/////////////////////|///////|                         |
|/////////////////////|///////|                         |
|/////////////////////|///////|                         |
|/////////////////////|///////|                         |
|/////////////////////+-------+-------------------------+
|/////////////////////////////|
+-----------------------------+

在 A 中但不在 B 中的内容意味着 B 存在空值。

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

/ : part of the result
* : part of the result with the specific B.id_A
                                       B
                      +---------------------------------+
            A         |                                 |
+---------------------+-------+                         |
|/////////////////////|///////|                         |
|/////////////////////|///////|                         |
|/////////////////////+---+///|                         |
|/////////////////////|***|///|                         |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+

编写此查询:

SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
AND B.id_A = SpecificPart;

将提供此结果:

/ : part of the result
* : part of the result with the specific B.id_A
                                       B
                      +---------------------------------+
            A         |                                 |
+---------------------+-------+                         |
|/////////////////////|       |                         |
|/////////////////////|       |                         |
|/////////////////////+---+   |                         |
|/////////////////////|***|   |                         |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+

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

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

SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
WHERE B.id_A = SpecificPart;

结果现在是:

/ : part of the result
* : part of the result with the specific B.id_A
                                       B
                      +---------------------------------+
            A         |                                 |
+---------------------+-------+                         |
|                     |       |                         |
|                     |       |                         |
|                     +---+   |                         |
|                     |***|   |                         |
|                     +---+---+-------------------------+
|                             |
+-----------------------------+

因为整个结果是针对 B.id_A = SpecificPart 过滤的,删除了部分 B.id_A IS NULL,即在 A 中但不在 B 中的部分


G
Grant Limberg

就优化器而言,是否使用 ON 或 WHERE 定义连接子句应该没有区别。

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


m
matthew david

您是要加入数据还是过滤数据?

为了可读性,将这些用例分别隔离到 ON 和 WHERE 是最有意义的。

在 ON 中加入数据

在 WHERE 中过滤数据

读取 WHERE 子句中存在 JOIN 条件和过滤条件的查询会变得非常困难。

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

也正如其他人所指出的,如果您使用外连接,如果您将过滤条件放在 ON 子句中,则会得到不同的结果,因为它只影响其中一个表。

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


O
Onkar Musale

在 SQL 中,'WHERE' 和 'ON' 子句是一种条件语句,但它们之间的主要区别在于,在 Select/Update 语句中使用 'Where' 子句来指定条件,而 'ON' 子句用于联接,在联接表之前验证或检查目标表和源表中的记录是否匹配

例如: - 'WHERE'

SELECT * FROM employee WHERE employee_id=101

例如: - '开'

有两个表employee 和employee_details,匹配的列是employee_id。

SELECT * FROM employee 
INNER JOIN employee_details 
ON employee.employee_id = employee_details.employee_id

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


但是您可以使用关键字 WHERE 代替 ON,不是吗? sqlfiddle.com/#!2/ae5b0/14/0
X
Xing-Wei Lin

我认为这是连接序列效应。在左上连接的情况下,SQL 先做左连接,再做 where 过滤。在较弱的情况下,先找到 Orders.ID=12345,然后再加入。


J
John Paul

对于内部连接,WHEREON 可以互换使用。事实上,可以在相关子查询中使用 ON。例如:

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)
...
)

这(恕我直言)对人类来说完全是一种困惑,而且很容易忘记将 table1 链接到任何东西(因为“驱动程序”表没有“on”子句),但它是合法的。


y
yakoub abaya

为了获得更好的性能,表应该有一个特殊的索引列用于 JOINS 。

因此,如果您条件的列不是那些索引列之一,那么我怀疑最好将其保留在 WHERE 中。

所以你 JOIN 使用索引列,然后在 JOIN 之后在无索引列上运行条件。


S
Sreelekshmi V

通常,一旦两个表已经连接,过滤就会在 WHERE 子句中处理。有可能,但您可能希望在加入其中一个或两个表之前对其进行过滤。即,where 子句适用于整个结果集,而 on 子句仅适用于相关连接。


情况并非如此,因为 DBMS“通常”会优化。
@philipxy 它仍然是一个重要的区别。虽然内部连接可以进行优化,但外部连接在语义上是不同的,不能以这种方式优化,因为它们会产生不同的结果。
@Shirik“一旦两个表已经加入,过滤就在 WHERE 子句中处理”是不正确的——除非你在谈论定义查询返回什么而不是“处理”的“逻辑”“处理”优化/实施——这就是问题所在。优化器经常评估实现部分中的 WHERE 部分,这些部分或多或少对应于内连接和外连接的连接。 (例如,参见 MySQL 手册“WHERE 子句优化”。)
L
Lukas Eder

我认为这种区别可以通过 logical order of operations in SQL 得到最好的解释,即简化:

FROM(包括连接)

在哪里

通过...分组

聚合

拥有

窗户

选择

清楚的

联合,相交,除了

订购方式

抵消

拿来

联接不是 select 语句的子句,而是 FROM 内的运算符。因此,当逻辑处理到达 WHERE 子句时,属于相应 JOIN 运算符的所有 ON 子句都“已经发生”逻辑。这意味着在 LEFT JOIN 的情况下,例如,外连接的语义在应用 WHERE 子句时已经发生。

I've explained the following example more in depth in this blog post。运行此查询时:

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;

LEFT JOIN 并没有真正有用的效果,因为即使演员没有在电影中演出,演员也会被过滤掉,因为它的 FILM_ID 将是 NULL 并且 WHERE 子句将过滤此类一排。结果是这样的:

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

即就像我们在内部加入了两个表一样。如果我们在 ON 子句中移动过滤谓词,它现在成为外连接的条件:

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;

这意味着结果将包含没有任何电影的演员,或者没有任何带有 FILM_ID < 10 的电影

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

简而言之

总是把你的谓词放在最合乎逻辑的地方。


b
blue_note

从字面上看,它们是等价的。

在大多数开源数据库(最著名的例子,在 MySql 和 postgresql 中)中,查询计划是出现在关系数据库管理系统中的访问路径选择中的经典算法的变体(Selinger 等人,1979 年)。在这种方法中,条件有两种类型

引用单个表的条件(用于过滤)

引用两个表的条件(视为连接条件,无论它们出现在哪里)

特别是在 MySql 中,您可以看到自己,通过跟踪优化器,join .. on 条件在解析期间被替换为等效的 where 条件。在 postgresql 中也会发生类似的事情(虽然无法通过日志查看,但您必须阅读源描述)。

无论如何,重点是,两种语法变体之间的差异在解析/查询重写阶段丢失了,甚至没有到达查询计划和执行阶段。因此,毫无疑问它们在性能方面是否相同,它们在到达执行阶段之前很久就变得相同。

您可以使用 explain 来验证它们是否生成相同的计划。例如,在 postgres 中,计划将包含一个 join 子句,即使您没有在任何地方使用 join..on 语法

Oracle 和 SQL Server 不是开源的,但据我所知,它们是基于等价规则(类似于关系代数中的规则),并且它们在两种情况下也产生相同的执行计划。

显然,这两种语法风格对于外连接来说是不等价的,对于那些你必须使用 join ... on 语法的人来说


所以你说你不同意上述答案?
我对以前的答案感到非常惊讶,因为 Oracle 中的 AFAIK 它们是等价的
E
Ezequiel

关于你的问题,

只要您的服务器可以获取它,内部连接上的“on”或“where”都是相同的:

select * from a inner join b on a.c = b.c

select * from a inner join b where a.c = b.c

并非所有口译员都知道的“where”选项,因此可能应该避免使用。当然,“on”子句更加清晰。


G
Gourav

一个。 WHERE 子句:加入后,会过滤记录。

湾。 ON 子句 - 在加入之前,将过滤记录(来自右表)。


您的答案是正确的,但在您之前已经由其他人写过 (stackoverflow.com/a/20981676/2692292)。尽量避免重复的答案。
E
Edridge D'Souza

为了添加到 Joel Coehoorn 的响应中,我将添加一些特定于 sqlite 的优化信息(其他 SQL 风格可能表现不同)。在原始示例中,LEFT JOIN 具有不同的结果,具体取决于您使用的是 JOIN ON ... WHERE 还是 JOIN ON ... AND。这是一个稍微修改的示例来说明:

SELECT *
FROM Orders
LEFT JOIN OrderLines ON Orders.ID = OrderLines.OrderID
    WHERE Orders.Username = OrderLines.Username

相对

SELECT *
FROM Orders
LEFT JOIN OrderLines ON Orders.ID = OrderLines.OrderID 
    AND Orders.Username = OrderLines.Username

现在,原始答案指出,如果您使用普通内连接而不是左连接,则两个查询的结果将是相同的,但执行计划会有所不同。我最近意识到两者之间的语义差异是前者强制查询优化器使用与 ON 子句关联的索引,而后者允许优化器选择 { 2} 子句,取决于它认为最有效的方式。

有时,优化器会猜错,您会想要强制执行某个执行计划。在这种情况下,假设 SQLite 优化器错误地断定执行此连接的最快方法是使用 Orders.Username 上的索引,而您从经验测试中知道 Orders.ID 上的索引会更快地传递您的查询。

在这种情况下,以前的 JOIN ON ... WHERE 语法本质上允许您强制ID 参数上发生主连接操作,而对 Username 的辅助过滤仅在主连接完成后执行.相比之下,JOIN ON ... AND 语法允许优化器选择是使用 Orders.ID 还是 Orders.Username 上的索引,理论上它可能会选择最终速度较慢的那个。


B
BCS

这是我的解决方案。

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 才能使其工作。

希望这有帮助。


在选择 song_id 和singers.fullname 的同时仅对songs.fullname 进行分组在大多数数据库中都是一个问题。