阅读之后,这不是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
条款 - 加入之前.在加入之前将过滤记录(来自右表).这可能最终在结果中为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子句中.看看这个相关的问题和这个答案(由我).
我认为最常见的做法是始终将连接条件放在ON子句中(除非它是外部连接,并且实际上确实需要在where子句中),因为它使任何读取查询的人都更清楚这些表的连接条件是什么,它还有助于防止WHERE子句长达数十行.
本文清楚地解释了差异.它还解释了"ON joined_condition vs WHERE joined_condition或joined_alias为null".
WHERE子句过滤JOIN的左侧和右侧,而ON子句将始终仅过滤右侧.
如果你总是想要获取左侧行而只是在某些条件匹配时才加入,那么你应该使用ON子句.
如果要过滤连接双方的产品,则应使用WHERE子句.
当涉及左连接时,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子句混合.