在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。
它们之间有什么不同?每一种都应该有什么不同?
如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。
在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。
它们之间有什么不同?每一种都应该有什么不同?
如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。
当前回答
为了获得更好的性能,表应该有一个用于join的特殊索引列。
所以如果你的条件列不是那些索引列之一,那么我怀疑最好把它保存在WHERE。
所以你使用索引列来JOIN,然后在JOIN之后你在无索引列上运行条件。
其他回答
简短的回答
这取决于连接类型是INNER还是OUTER。
对于INNER JOIN,答案是肯定的,因为INNER JOIN语句可以重写为带有WHERE子句的CROSS JOIN,该子句与您在INNER JOIN查询的ON子句中使用的条件相同。
但是,这只适用于INNER JOIN,不适用于OUTER JOIN。
长回答
考虑到我们有如下的post和post_comment表:
该职位有以下记录:
| id | title |
|----|-----------|
| 1 | Java |
| 2 | Hibernate |
| 3 | JPA |
并且post_comment有以下三行:
| id | review | post_id |
|----|-----------|---------|
| 1 | Good | 1 |
| 2 | Excellent | 1 |
| 3 | Awesome | 2 |
SQL内部连接
SQL JOIN子句允许您关联属于不同表的行。例如,CROSS JOIN将创建一个笛卡尔积(Cartesian Product),其中包含两个连接表之间所有可能的行组合。
虽然CROSS JOIN在某些场景中很有用,但大多数情况下,您希望基于特定的条件来连接表。这就是INNER JOIN发挥作用的地方。
SQL INNER JOIN允许我们根据通过on子句指定的条件筛选连接两个表的笛卡尔积。
SQL内部连接-对“始终为真”的条件
如果您提供了一个“always true”条件,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子句条件为"always 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
SQL INNER JOIN - ON "always false"条件
另一方面,如果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
SQL INNER JOIN - ON子句使用外键和主键列
最常见的ON子句条件是子表中的外键列与父表中的主键列相匹配的条件,如下面的查询所示:
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_comment记录。没有关联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 | p.title | pc.review | |------|------------|-------|-----------|-----------| | 1 | 1 | 1 | Java | Good | | 1 | 1 | 2 | Java | Excellent || 1 | 2 | 3 | Java | Awesome || 2 | 1 | 1 | Hibernate | Good || 2 | 1 | 2 | Hibernate | Excellent || 2 | 2 | 3 | Hibernate | Awesome || 3 | 1 | 1 | JPA | Good || 3 | 1 | 2 | JPA | Excellent || 3 | 2 | 3 | JPA | Awesome |
结论
INNER JOIN语句可以重写为带有WHERE子句的CROSS JOIN,该子句与在INNER JOIN查询的ON子句中使用的条件相同。
并不是说这只适用于INNER JOIN,而不适用于OUTER JOIN。
在INNER join中,它们是可互换的,优化器会随意重新排列它们。
在OUTER连接上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧。
我把它们放在任意一个位置,取决于可读性。
这就是我的解。
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才能让它工作。
希望这对你有所帮助。
您正在尝试连接数据还是过滤数据?
为了可读性,将这些用例分别隔离到ON和WHERE是最有意义的。
在ON中联接数据 在WHERE中过滤数据
如果where子句中存在JOIN条件和过滤条件,那么读取查询将变得非常困难。
性能方面,你应该看不到区别,尽管不同类型的SQL有时处理查询计划的方式不同,所以值得尝试¯\_()_/¯(注意缓存会影响查询速度)
另外,正如其他人注意到的那样,如果使用外部连接,如果将筛选条件放在ON子句中,则会得到不同的结果,因为它只影响其中一个表。
我在这里写了一篇更深入的文章: https://dataschool.com/learn/difference-between-where-and-on-in-sql
为了补充Joel Coehoorn的回答,我将添加一些特定于sqlite的优化信息(其他SQL风格的行为可能不同)。在原来的例子中,LEFT JOIN有不同的结果,这取决于你是否使用JOIN on…在哪里或加入…和。下面是一个稍微修改过的例子:
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上的索引。用户名,当您从经验测试中知道,对订单的索引。ID可以更快地传递查询。
在这种情况下,前者JOIN ON…WHERE语法实际上允许您强制对ID参数执行主连接操作,只有在主连接完成之后才对Username执行辅助过滤。相反,JOIN ON…AND语法允许优化器选择是否在Orders上使用索引。ID或订单。用户名,理论上有可能它会选择一个速度较慢的。