阅读后,不是Explicit vs Implicit SQL Joins的副本。 答案可能是相关的(甚至相同),但问题是不同的。
有什么区别,应该分别做些什么?
如果我理解正确的理论,查询优化器应该能够互换使用。
答案 0 :(得分:758)
他们不是一回事。
考虑以下问题:
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
,条款有效等效。然而,仅仅因为它们在功能上是相同的,因为它们产生相同的结果,并不意味着这两种从句具有相同的语义。
答案 1 :(得分:257)
外部联接的事项
一个。 WHERE
子句: 加入后。加入后,记录将被过滤。
湾ON
条款 - 之前加入。在加入之前将过滤记录(来自右表)。这可能最终在结果中为null(因为OUTER join)。
示例:请考虑以下表格:
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
条款:
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
条款
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 |
答案 2 :(得分:140)
在INNER JOIN
上它们是可以互换的,优化器会随意重新排列它们。
在OUTER JOIN
上,它们不一定是可互换的,具体取决于它们所依赖的连接的哪一侧。
我根据可读性将它们放在任何一个地方。
答案 3 :(得分:40)
我这样做的方式是:
如果您正在执行ON
,请始终将连接条件放在INNER JOIN
子句中。因此,不要在ON子句中添加任何WHERE条件,将它们放在WHERE
子句中。
如果您正在执行LEFT JOIN
,请在 右侧 一侧的表格的ON
子句中添加任何WHERE条件加入。这是必须的,因为添加引用连接右侧的WHERE子句会将连接转换为INNER JOIN。
例外情况是您查找不在特定表中的记录。您可以通过以下方式将对RIGHT JOIN表中的唯一标识符(不是NULL)的引用添加到WHERE子句:WHERE t2.idfield IS NULL
。因此,您应该在联接右侧引用表的唯一时间是查找表中没有的记录。
答案 4 :(得分:30)
在内连接上,它们的意思相同。但是,在外连接中将获得不同的结果,具体取决于是否将连接条件放在WHERE与ON子句中。请查看this related question和this answer(由我)。
我认为最常见的做法是始终将连接条件放在ON子句中(除非它是外部连接,并且实际上确实需要在where子句中),因为它使任何人都更清楚读取查询表连接的条件是什么,并且它还有助于防止WHERE子句长达数十行。
答案 5 :(得分:21)
This article清楚地解释了差异。它还解释了“ON joined_condition vs WHERE joined_condition或joined_alias为null”。
WHERE子句过滤FROM子句的结果以及JOIN,而ON子句用于在FROM和JOIN表之间生成表结果。
答案 6 :(得分:11)
对于左连接, 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.v =' K',第一个查询从t1返回记录,并从t2返回其依赖行(如果有的话)。
第二个查询从t1返回行,但仅针对t1.v =' K'将与它有任何相关的行。
答案 7 :(得分:8)
就优化器而言,无论是使用ON还是WHERE定义join子句,都不应该有所区别。
然而,恕我直言,我认为在执行连接时使用ON子句要清楚得多。这样,您就会有一个特定的查询部分,它指示如何处理连接而不是与其余WHERE子句混合。
答案 8 :(得分:1)
我认为这是连接序列效应。 在左上角连接的情况下,SQL首先执行左连接,然后执行筛选。 在downer的情况下,首先找到Orders.ID = 12345,然后再加入。
答案 9 :(得分:1)
在SQL中,'WHERE'和'ON'子句是一种条件状态,但它们之间的主要区别在于,'Where'子句在Select / Update Statements中用于指定条件,而'' ON'Clause在Joins中使用,在连接表之前验证或检查记录是否在目标和源表中匹配
例如: - '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
希望我已经回答了你的问题。 恢复任何澄清。
答案 10 :(得分:1)
对于内部联接,WHERE
和ON
可以互换使用。实际上,可以在相关子查询中使用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”条款),但它是合法的。
答案 11 :(得分:1)
为了获得更好的性能表,应该有一个特殊的索引列用于JOINS。
因此,如果您所关注的列不是这些索引列之一,那么我怀疑将它保存在WHERE中会更好。
所以你使用索引列加入,然后在JOIN之后在无索引列上运行条件。
答案 12 :(得分:1)
通常,一旦两个表已经连接,就会在WHERE子句中处理过滤。虽然您可能希望在加入表之前过滤其中的一个或两个表,但这是可能的。 即,where子句适用于整个结果集,而on子句仅适用于有问题的连接。
答案 13 :(得分:1)
您要合并数据还是过滤数据?
出于可读性考虑,将这些用例分别隔离到ON和WHERE最为有意义。
要读取WHERE子句中存在JOIN条件和过滤条件的查询会变得非常困难。
从性能上来说,您应该看不出任何区别,尽管不同类型的SQL有时对查询计划的处理方式也不同,因此值得尝试¯\_(ツ)_/¯
(请注意缓存会影响查询速度)
正如其他人指出的那样,如果使用外部联接,则将过滤条件放在ON子句中会得到不同的结果,因为它仅影响其中一个表。
我在这里写了一篇更深入的文章: https://dataschool.com/learn/difference-between-where-and-on-in-sql
答案 14 :(得分:0)
我认为可以通过logical order of operations in SQL(最好是简化的)来最好地解释这种区别:
FROM
(包括联接)WHERE
GROUP BY
HAVING
WINDOW
SELECT
DISTINCT
UNION
,INTERSECT
,EXCEPT
ORDER BY
OFFSET
FETCH
联接不是select语句的子句,而是FROM
内的运算符。这样,当逻辑处理到达ON
子句时,属于相应JOIN
运算符的所有WHERE
子句在逻辑上“已经发生”。这意味着,例如在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
从逻辑上讲,始终将谓词放在最有意义的位置。
答案 15 :(得分:0)
让我们考虑一下这些表:
A
id | SomeData
B
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.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
过滤了整个结果,因此删除了{em> A中不在B中的部分B.id_A = NULL
答案 16 :(得分:0)
关于您的问题,
只要服务器可以获取,内部连接的“ 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”子句更清楚。
答案 17 :(得分:0)
从字面上看,它们是等效的。
在大多数开源数据库中(最著名的示例,在 MySql 和 postgresql 中),查询计划是出现在 Access Path中的经典算法的一种变体关系数据库管理系统中的选择(Selinger等,1979)。这种方法有两种类型的条件
特别是在MySql中,通过跟踪优化器,您可以看到自己,join .. on
条件在解析期间被替换为等效的{{1} } 条件。在postgresql中也会发生类似的事情(尽管无法通过日志查看它,但是您必须阅读源代码描述)。
无论如何,要点是,在解析/查询重写阶段,两个语法变体之间的差异丢失了,甚至还没有达到查询计划和执行阶段。因此,毫无疑问,它们在性能上是否相等,它们在进入执行阶段很久之前就变得相同。
您可以使用where
来验证它们是否生成相同的计划。例如,在postgres中,即使您未在任何地方使用explain
语法,该计划也会包含一个join
子句。
Oracle和SQL Server不是开源的,但是据我所知,它们是基于等效规则的(类似于关系代数中的规则),并且在两种情况下它们也产生相同的执行计划。
很明显,对于外部联接,这两种语法样式 不相同,因为必须使用
join..on
语法
答案 18 :(得分:0)
为了添加到 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
子句关联的索引,而后者允许优化器选择其中的任何索引ON ... AND
子句,取决于它认为最有效的方式。
有时,优化器会猜错,您会想要强制执行某个执行计划。在这种情况下,假设 SQLite 优化器错误地得出执行此连接的最快方法是使用 Orders.Username
上的索引,当您从经验测试中知道 Orders.ID
上的索引将提供您的查询速度更快。
在这种情况下,前一个 JOIN ON ... WHERE
语法本质上允许您强制在 ID
参数上进行主要连接操作,对 {{1} 进行二次过滤仅在主连接完成后执行。相比之下,Username
语法允许优化器选择是使用 JOIN ON ... AND
上的索引还是 Orders.ID
上的索引,并且理论上有可能选择最终速度较慢的那个。< /p>
答案 19 :(得分:-1)
一个。 WHERE 子句:加入后,记录将被过滤。
B. ON 子句 - 在加入之前,记录(来自右表)将被过滤。
答案 20 :(得分:-5)
这是我的解决方案。
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
才能让它发挥作用。
希望得到这个帮助。