在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。
它们之间有什么不同?每一种都应该有什么不同?
如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。
在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。
它们之间有什么不同?每一种都应该有什么不同?
如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。
当前回答
对于内部连接,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链接到任何东西(因为“driver”表没有“on”子句),但这是合法的。
其他回答
简短的回答
这取决于连接类型是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。
就优化器而言,使用ON或WHERE定义连接子句应该没有区别。
然而,恕我直言,我认为在执行连接时使用ON子句更清楚。这样,您就有了查询的特定部分,该部分规定了如何处理连接,而不是与其余WHERE子句混合。
a. WHERE子句:加入后,记录将被过滤。
b. ON子句-在加入之前,记录(来自右边表格)将被过滤。
让我们考虑一下这些表格:
A
id | SomeData
B
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中
为了获得更好的性能,表应该有一个用于join的特殊索引列。
所以如果你的条件列不是那些索引列之一,那么我怀疑最好把它保存在WHERE。
所以你使用索引列来JOIN,然后在JOIN之后你在无索引列上运行条件。