阅读后,这不是Explicit与Implicit SQL Joins的重复。答案可能是相关的(甚至是相同的),但问题是不同的。
有什么区别,每个应该有什么区别?
如果我正确理解该理论,则查询优化器应该可以互换使用。
阅读后,这不是Explicit与Implicit SQL Joins的重复。答案可能是相关的(甚至是相同的),但问题是不同的。
有什么区别,每个应该有什么区别?
如果我正确理解该理论,则查询优化器应该可以互换使用。
Answers:
它们不是同一件事。
考虑以下查询:
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
条款:后加入。加入后将过滤记录。
b。ON
子句- 加入之前。记录(来自右表)将在加入之前被过滤。结果可能为空(由于OUTER连接)。
示例:考虑下表:
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 |
intermediate join table
?一些“解释”命令?
在INNER JOIN
s上它们是可互换的,优化器将随意重新排列它们。
在OUTER JOIN
s上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧。
我根据可读性将它们放在任何一个地方。
Orders.Join( OrderLines, x => x.ID, x => OrderID, (o,l) => new {Orders = o, Lines = l}).Where( ol => ol.Orders.ID = 12345)
我这样做的方式是:
ON
如果要执行连接操作,则始终将连接条件放在子句中INNER JOIN
。因此,请勿在ON子句中添加任何WHERE条件,而应将它们放在WHERE
子句中。
如果您正在执行LEFT JOIN
,则将任何WHERE条件添加到ON
联接右侧表的子句中。这是必须的,因为添加引用联接右侧的WHERE子句会将联接转换为INNER JOIN。
当您要查找不在特定表中的记录时,就是一个例外。您可以通过以下方式将对RIGHT JOIN表中唯一标识符(永远不会为NULL)的引用添加到WHERE子句中:WHERE t2.idfield IS NULL
。因此,唯一应在联接右侧引用表的地方是查找那些不在表中的记录。
这是一个非常常见的问题,因此此答案基于我写的这篇文章。
考虑到我们有以下post
与post_comment
表:
将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 JOIN子句允许您关联属于不同表的行。例如,CROSS JOIN将创建一个笛卡尔乘积,其中包含两个联接表之间所有可能的行组合。
尽管CROSS JOIN在某些情况下很有用,但在大多数情况下,您希望根据特定条件连接表。而且,这就是INNER JOIN发挥作用的地方。
SQL INNER JOIN允许我们根据通过ON子句指定的条件来过滤联接两个表的笛卡尔积。
如果提供“始终为真”的条件,则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
我们将获得post
和post_comment
记录的所有组合:
| p.id | pc.id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
因此,如果ON子句条件为“始终为true”,则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
另一方面,如果ON子句条件为“ always false”,则所有联接的记录将被过滤掉,结果集将为空。
因此,如果我们执行以下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
最常见的ON子句条件是将子表中的Foreign Key列与父表中的Primary Key列匹配的条件,如以下查询所示:
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
没有关联的行将post_comment
被排除,因为它们不能满足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 | 标题| pc.review | | ------ | ------------ | ------- || ------------ | --------- -| | 1 | 1 | 1 | Java | 好 | 1 | 1 | 2 | Java | 优秀| 1 | 2 | 3 | Java | 很棒| 2 | 1 | 1 | 休眠| 好| 2 | 1 | 2 | 休眠| 优秀| 2 | 2 | 3 | 休眠| 很棒| 3 | 1 | 1 | JPA | 好| 3 | 1 | 2 | JPA | 优秀| 3 | 2 | 3 | JPA | 很棒
可以使用WHERE子句将INNER JOIN语句重写为CROSS JOIN,该子句与您在INNER JOIN查询的ON子句中使用的条件相同。
并不是说这仅适用于INNER JOIN,不适用于OUTER 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 | |
+-------+-------------+------+-----+---------+-------+
表t2的ID为fid。
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'将具有任何关联的行。
让我们考虑那些表:
一个
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
删除A中的部分B.id_A = NULL
,而B中没有的部分
您是要联接数据还是过滤数据?
为了提高可读性,将这些用例分别隔离在ON和WHERE上是最有意义的。
读取在WHERE子句中存在JOIN条件和过滤条件的查询会变得非常困难。
在性能方面,尽管不同类型的SQL有时对查询计划的处理方式有所不同,所以您应该不会有所不同,因此值得尝试¯\_(ツ)_/¯
(请注意缓存会影响查询速度)
另外,正如其他人指出的那样,如果使用外部联接,则将过滤条件放在ON子句中将得到不同的结果,因为它仅影响其中一个表。
我在这里写了一篇更深入的文章:https : //dataschool.com/learn/difference-between-where-and-on-in-sql
在SQL中,“ WHERE”和“ ON”子句是条件状态子,但它们之间的主要区别在于,“选择/更新”语句中使用“ Where”子句来指定条件,而“ ON”子句在联接中使用,它在联接表之前验证或检查目标表和源表中的记录是否匹配
例如:-'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
希望我回答了你的问题。还原以进行任何澄清。
用于内部联接,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)
...
)
(IMHO)完全使人感到困惑,并且很容易忘记链接table1
到任何东西(因为“ driver”表没有“ on”子句),但这是合法的。
我认为可以通过SQL的逻辑逻辑顺序来最好地解释这种区别,即简化:
FROM
(包括联接)WHERE
GROUP BY
HAVING
WINDOW
SELECT
DISTINCT
UNION
,INTERSECT
,EXCEPT
ORDER BY
OFFSET
FETCH
联接不是select语句的子句,而是内的运算符FROM
。这样,当逻辑处理到达ON
子句时,属于相应JOIN
运算符的所有子句在逻辑上 “已经发生” WHERE
。这意味着,LEFT JOIN
例如对于a而言,在WHERE
应用该子句时,外部联接的语义已经发生。
我已经在此博客文章中更深入地解释了以下示例。运行此查询时:
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
得到它的工作。
希望能有所帮助。