BCS*_*BCS 616 sql join on-clause where-clause
阅读之后,这不是Explicit vs Implicit SQL Joins的重复.答案可能是相关的(甚至是相同的),但问题是不同的.
有什么区别,应该分别做些什么?
如果我理解正确的理论,查询优化器应该能够互换使用.
Joe*_*orn 802
它们不是同一件事.
考虑这些查询:
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345
Run Code Online (Sandbox Code Playgroud)
和
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
AND Orders.ID = 12345
Run Code Online (Sandbox Code Playgroud)
第一个将返回订单及其行(如果有)的订单号12345.第二个将返回所有订单,但只有订单12345将有任何与之关联的行.
有了INNER JOIN,条款实际上是等价的.然而,仅仅因为它们在功能上是相同的,因为它们产生相同的结果,并不意味着这两种子句具有相同的语义含义.
San*_*dal 303
外连接的事项
一个.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 |
Run Code Online (Sandbox Code Playgroud)
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 |
Run Code Online (Sandbox Code Playgroud)
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 |
Run Code Online (Sandbox Code Playgroud)
Cad*_*oux 141
在INNER JOINs上它们是可互换的,优化器将随意重新排列它们.
在OUTER JOINs上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧.
我根据可读性将它们放在任何一个地方.
HLG*_*GEM 43
我这样做的方式是:
ON如果你正在做的话,总是把连接条件放在子句中INNER JOIN.因此,不要向ON子句添加任何WHERE条件,将它们放在WHERE子句中.
如果您正在执行操作LEFT JOIN,请将任何WHERE条件添加到连接右侧ON表的子句中.这是必须的,因为添加引用连接右侧的WHERE子句会将连接转换为INNER JOIN.
例外情况是您查找不在特定表中的记录.您可以通过以下方式将对RIGHT JOIN表中的唯一标识符(不是NULL)的引用添加到WHERE子句中:WHERE t2.idfield IS NULL.因此,您应该在连接的右侧引用表的唯一时间是查找不在表中的那些记录.
Cid*_*Cid 13
让我们考虑这些表:
一种
id | SomeData
Run Code Online (Sandbox Code Playgroud)
乙
id | id_A | SomeOtherData
Run Code Online (Sandbox Code Playgroud)
id_A 作为表的外键 A
编写此查询:
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A;
Run Code Online (Sandbox Code Playgroud)
将提供这个结果:
/ : part of the result
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////+-------+-------------------------+
|/////////////////////////////|
+-----------------------------+
Run Code Online (Sandbox Code Playgroud)
在 A 中但不在 B 中的内容意味着 B 有空值。
现在,让我们考虑 中的特定部分B.id_A,并从之前的结果中突出显示它:
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////+---+///| |
|/////////////////////|***|///| |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+
Run Code Online (Sandbox Code Playgroud)
编写此查询:
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
AND B.id_A = SpecificPart;
Run Code Online (Sandbox Code Playgroud)
将提供这个结果:
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////| | |
|/////////////////////| | |
|/////////////////////+---+ | |
|/////////////////////|***| | |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+
Run Code Online (Sandbox Code Playgroud)
因为这会在内部连接中删除不在的值 B.id_A = SpecificPart
现在,让我们将查询更改为:
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
WHERE B.id_A = SpecificPart;
Run Code Online (Sandbox Code Playgroud)
结果现在是:
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
| | | |
| | | |
| +---+ | |
| |***| | |
| +---+---+-------------------------+
| |
+-----------------------------+
Run Code Online (Sandbox Code Playgroud)
因为整个结果进行筛选B.id_A = SpecificPart除去部件B.id_A IS NULL,即是在甲不在乙
Hri*_*hra 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 | |
+-------+-------------+------+-----+---------+-------+
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
查询"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)
Run Code Online (Sandbox Code Playgroud)
查询"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)
Run Code Online (Sandbox Code Playgroud)
很明显,第一个查询从行t1.v ='K'返回来自t1的记录及其从t2的依赖行(如果有的话).
第二个查询从t1返回行,但仅对于t1.v ='K'将包含任何关联的行.
就优化器而言,无论是使用ON还是WHERE定义join子句,都不应该有所区别.
但是,恕我直言,我认为在执行连接时使用ON子句要清楚得多.这样,您有一个特定的查询部分,它指示如何处理连接而不是与其余的WHERE子句混合.
小智 6
您是要连接数据还是过滤数据?
为了可读性,将这些用例分别隔离到 ON 和 WHERE 是最有意义的。
读取 WHERE 子句中存在 JOIN 条件和过滤条件的查询会变得非常困难。
性能方面你不应该看到差异,尽管不同类型的 SQL 有时会以不同的方式处理查询计划,因此值得尝试¯\_(?)_/¯(请注意缓存会影响查询速度)
另外正如其他人所指出的,如果您使用外部联接,如果将过滤条件放在 ON 子句中,您将获得不同的结果,因为它只影响其中一个表。
我在这里写了一篇更深入的文章:https : //dataschool.com/learn/difference-between-where-and-on-in-sql
| 归档时间: |
|
| 查看次数: |
621390 次 |
| 最近记录: |