阅读之后,这不是Explicit vs Implicit SQL Joins的重复。答案可能是相关的(甚至是相同的),但问题是不同的。
有什么区别,应该分别做些什么?
如果我理解正确的理论,查询优化器应该能够互换使用。
它们不是同一件事。
考虑这些查询:
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
可以互换使用。实际上,可以在相关子查询中使用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”条款),但这是合法的。
为了获得更好的性能,表格应该有一个特殊的索引列用于JOINS。
因此,如果您所关注的列不是那些索引列中的一个,那么我怀疑将它保存在WHERE中会更好。
所以你使用索引列加入,然后在JOIN之后运行无索引列的条件。
通常,一旦两个表已经连接,就会在WHERE子句中处理过滤。虽然您可能希望在加入表之前过滤其中的一个或两个表,但这是可能的。即,where子句适用于整个结果集,而on子句仅适用于有问题的连接。
在SQL中,'WHERE'和'ON'子句是一种条件状态,但它们之间的主要区别在于,'Where'子句用于选择/更新语句以指定条件,而'ON'子句在Joins中使用,在连接表之前验证或检查目标和源表中的记录是否匹配
例如: - 'WHERE'
SELECT * FROM employee WHERE employee_id=101
例如: - 'ON'
employee表和employee_details有两个表,匹配列是employee_id。
SELECT * FROM employee
INNER JOIN employee_details
ON employee.employee_id = employee_details.employee_id
希望我已经回答了你的问题。恢复任何澄清。
让我们考虑一下这些表:
一个
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 = NULL
,这是在A中不在B中
您是否尝试加入数据或过滤数据?
为了便于阅读,将这些用例分别分别用于ON和WHERE是最有意义的。
在WHERE子句中存在JOIN条件和过滤条件的查询可能变得非常困难。
性能方面你不应该看到差异,虽然不同类型的SQL有时会以不同的方式处理查询规划,因此值得尝试¯\_(ツ)_/¯
(请注意缓存影响查询速度)
另外正如其他人所指出的那样,如果使用外连接,如果将过滤条件放在ON子句中,则会得到不同的结果,因为它只影响其中一个表。
我在这里写了一篇更深入的帖子:https://dataschool.com/learn/difference-between-where-and-on-in-sql
我认为这种区别最好通过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
总是把你的谓词逻辑地放在最有意义的地方。
这是我的解决方案。
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
让它工作。
希望这有帮助。
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 |
在INNER JOIN
s上,它们是可互换的,优化器将随意重新排列它们。
在OUTER JOIN
s上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧。
我根据可读性将它们放在任何一个地方。
我这样做的方式是:
ON
,总是把连接条件放在INNER JOIN
子句中。因此,不要向ON子句添加任何WHERE条件,将它们放在WHERE
子句中。LEFT JOIN
,请将任何WHERE条件添加到连接右侧表的ON
子句中。这是必须的,因为添加引用连接右侧的WHERE子句会将连接转换为INNER JOIN。
例外情况是您查找不在特定表中的记录。您可以通过以下方式将对RIGHT JOIN表中的唯一标识符(不是NULL)的引用添加到WHERE子句:WHERE t2.idfield IS NULL
。因此,您应该在连接的右侧引用表的唯一时间是查找不在表中的那些记录。在内连接上,它们意味着同样的事情。但是,在外连接中将获得不同的结果,具体取决于是否将连接条件放在WHERE与ON子句中。看看this related question和this answer(由我)。
我认为最常见的做法是始终将连接条件放在ON子句中(除非它是外部连接,并且实际上确实需要在where子句中),因为它使任何读取查询的人都更清楚这些表的连接条件是什么,它还有助于防止WHERE子句长达数十行。
This article清楚地解释了差异。它还解释了“ON joined_condition vs WHERE joined_condition或joined_alias为null”。
WHERE子句过滤FROM子句的结果以及JOIN,而ON子句用于生成FROM和JOIN表之间的表结果。
当涉及左连接时,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'将包含任何关联的行。
就优化器而言,无论是使用ON还是WHERE定义join子句,都不应该有所区别。
但是,恕我直言,我认为在执行连接时使用ON子句要清楚得多。这样,您有一个特定的查询部分,它指示如何处理连接而不是与其余的WHERE子句混合。
我认为这是连接序列效应。在左上角连接的情况下,SQL首先执行左连接,然后执行筛选。在downer的情况下,首先找到Orders.ID = 12345,然后再加入。