SQL INNER JOIN语法

29

下面两个 SQL 语句得到的结果相同

SELECT c.name, o.product  
FROM customer c, order o  
WHERE c.id = o.cust_id  
AND o.value = 150  

SELECT c.name, o.product  
FROM customer c  
INNER JOIN order o on c.id = o.cust_id  
WHERE o.value = 150

我看到不同公司都使用这两种风格。从我所见,第二种风格是大多数人在网上推荐的。除了风格之外,是否有任何真正的原因?使用 Inner Join 有时是否具有更好的性能?

我注意到 Ingres 和 Oracle 开发人员倾向于使用第一种风格,而 Microsoft SQL Server 用户则倾向于使用第二种风格,但这可能只是巧合。

感谢任何见解,我已经想了很长时间。

编辑:我已经更改了标题,将“SQL 内连接与笛卡尔积”改为正确的术语。非常感谢迄今为止的所有回复。

7个回答

29

这两个查询均为内连接且等价。第一个是早期的做法,而使用JOIN语法只是在SQL-92标准引入后才变得常见(我认为它在早期的定义中就存在了,只是以前没有被广泛使用)。

使用JOIN语法比较好,因为它将连接逻辑与过滤逻辑分离在WHERE子句中。虽然JOIN语法实际上只是内连接的语法糖,但其强项在于外连接,而老式的*语法可能会产生无法明确描述连接且解释是依赖于具体实现的情况。[LEFT | RIGHT] JOIN语法避免了这些问题,因此为了一致性,在所有情况下都建议使用JOIN语句。

请注意,这两个示例都不是笛卡尔积。如果要进行笛卡尔积操作,可以使用以下语法:

SELECT c.name, o.product  
FROM customer c, order o  
WHERE o.value = 150  
或者
SELECT c.name, o.product  
FROM customer c  CROSS JOIN order o 
WHERE o.value = 150

1
JOIN 符号是在 SQL-92 中引入的,但直到后来才变得普遍。 - Jonathan Leffler
感谢您的纠正,我确实检查过是否能找到日期。就个人而言,我只在2001或2002年左右开始使用连接(JOINS) - 起初我觉得使用有点奇怪,但现在更喜欢它了。 - Cruachan
请问您能否举个例子,说明在哪些情况下旧语法必然会产生歧义?谢谢。 - Evgeniy

6
回答你的问题,我认为Oracle JOIN ... ON语法早期的一些错误使得Oracle用户不再使用该语法。但是现在没有任何特殊的问题。
它们是等价的,并且应该被解析成相同的内部表示以进行优化。

ANSI JOIN 语法中的一些错误并不是我所谓的“早期”错误。在之前的一份工作中,我们做了很多大型查询,如果使用 ANSI JOIN 语法,您会遇到查询中可以拥有的总列数的硬性限制。即使在 10g 版本发布时,您仍然会发现存在问题。 - Nate C-K
是的,我仍然不使用 ANSI 连接。 - WW.

4

实际上这些例子是等价的,也都不是笛卡尔积。当你在没有指定连接条件时加入两个表格时,比如下面这种情况,才会返回笛卡尔积:

select *
from t1,t2

关于这个问题,在维基百科上有很好的讨论。


4

Oracle在支持JOIN ... ON(ANSI)语法方面较晚(直到Oracle 9),因此Oracle开发人员经常不使用它。

个人而言,当一个表驱动查询,其他表是查找表时,在逻辑上清晰时,我更喜欢使用ANSI语法。当表“相等”时,我倾向于使用笛卡尔语法。

性能不应该有任何区别。


3

这两个查询都执行内连接,只是语法不同。


3

JOIN... ON...语法是SQL ANSI和ISO规范的较新添加。通常更喜欢使用JOIN... ON...语法,因为它可以将连接条件从WHERE子句中移出,使得WHERE子句只用于过滤,并且每个JOIN必须至少伴随一个ON子句,这样如果你正在创建可怕的笛卡尔积,就会更加明显。如果所有连接条件都在WHERE子句中并且使用AND连接,那么当一个或多个条件缺失时就不太容易发现。


2

简短总结

使用WHERE子句匹配INNER JOIN查询中ON子句所使用的相同条件,可以将INNER JOIN语句重写为CROSS JOIN。

表关系

假设我们有以下postpost_comment表:

The post and post_comment tables

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内部连接

SQL JOIN子句允许您关联属于不同表的行。例如,CROSS JOIN将创建一个笛卡尔积,其中包含两个连接表之间所有可能的行组合。

虽然CROSS JOIN在某些场景下很有用,但大多数情况下,您希望根据特定条件连接表格。这就是INNER JOIN发挥作用的地方。

SQL INNER JOIN允许我们基于通过ON子句指定的条件筛选连接两个表的笛卡尔积。

SQL内部连接 - 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

我们将获取所有 postpost_comment 记录的组合:
| p.id    | pc.id      |
|---------|------------|
| 1       | 1          |
| 1       | 2          |
| 1       | 3          |
| 2       | 1          |
| 2       | 2          |
| 2       | 3          |
| 3       | 1          |
| 3       | 2          |
| 3       | 3          |

因此,如果ON子句的条件“始终为真”,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" condition

如果ON子句的条件是“始终为假”,那么所有连接记录都将被过滤掉,结果集将为空。

因此,如果我们执行以下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以及它们的post_comment记录。没有相关联的post_commentpost行被排除在外,因为它们无法满足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,而不适用于OUTER JOIN。


约束对于连接的定义是无关紧要的,因此在解释连接的工作原理之前提到它们是具有误导性的。 - philipxy
我一点也不觉得它有误导性。 - Vlad Mihalcea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接