在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。


它们之间有什么不同?每一种都应该有什么不同?

如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。


当前回答

内连接不重要吗 外部连接的注意事项 a. WHERE从句:加入后。在连接发生后,将过滤记录。 b. ON条款-加入前。记录(来自右表)将在加入之前被过滤。这可能在结果中以null结束(因为OUTER连接)。

示例:考虑以下表格:

文档: id 的名字 1 Document1 2 Document2 3. Document3 4 Document4 5 Document5 下载: id document_id 用户名 1 1 sandeep 2 1 思米 3. 2 sandeep 4 2 •拉赫曼 5 3. 思米

a) WHERE从句内:

   SELECT documents.name, downloads.id
     FROM documents
     LEFT OUTER JOIN downloads
       ON documents.id = downloads.document_id
     WHERE username = 'sandeep'

对于上述查询,中间连接表将如下所示。

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

在应用WHERE子句并选择列出的属性后,结果将是:

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'

对于上述查询,中间连接表将如下所示。

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

注意文档中不符合这两个条件的行是如何用NULL值填充的。

选择列出的属性后,结果将是:

name id
Document1 1
Document2 3
Document3 NULL
Document4 NULL
Document5 NULL

其他回答

简短的回答

这取决于连接类型是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。

我认为这种区别可以通过SQL中操作的逻辑顺序来最好地解释,这是简化的:

FROM(包括连接) 在哪里 集团 聚合 有 窗口 选择 截然不同的 并,相交,除 命令 抵消 获取

联接不是select语句的子句,而是FROM语句中的操作符。因此,当逻辑处理到达WHERE子句时,属于相应JOIN运算符的所有ON子句在逻辑上“已经发生”。这意味着在LEFT JOIN的情况下,例如,在应用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

简而言之

总是把谓词放在逻辑上最有意义的地方。

当涉及到左连接时,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返回一条记录,从t2返回与t1相关的行(如果有的话)。v = 'K'。

第二个查询返回来自t1的行,但只针对t1。v = 'K'将有任何与它相关的行。

我的做法是:

Always put the join conditions in the ON clause if you are doing an INNER JOIN. So, do not add any WHERE conditions to the ON clause, put them in the WHERE clause. If you are doing a LEFT JOIN, add any WHERE conditions to the ON clause for the table in the right side of the join. This is a must, because adding a WHERE clause that references the right side of the join will convert the join to an INNER JOIN. The exception is when you are looking for the records that are not in a particular table. You would add the reference to a unique identifier (that is not ever NULL) in the RIGHT JOIN table to the WHERE clause this way: WHERE t2.idfield IS NULL. So, the only time you should reference a table on the right side of the join is to find those records which are not in the table.

对于内连接,它们的含义相同。但是,在外部连接中将得到不同的结果,这取决于您将连接条件放在WHERE子句中还是on子句中。看看这个相关的问题和答案(由我)。

我认为习惯总是把连接条件放在ON子句中是最有意义的(除非它是一个外部连接,而且你确实想把它放在where子句中),因为它使阅读你的查询的任何人都更清楚表是在什么条件下被连接的,而且它还有助于防止where子句长几十行。