1048

为简单起见,假设所有相关字段都是NOT NULL.

你可以做:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

要不然:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

这两个工作方式相同MySQL吗?

4

12 回答 12

762

INNER JOIN是您应该使用的 ANSI 语法。

它通常被认为更具可读性,尤其是当您连接大量表时。

OUTER JOIN每当需要时,它也可以很容易地替换为。

WHERE语法更面向关系模型。

两个表JOINed 的结果是应用过滤器的表的笛卡尔积,该过滤器仅选择与连接列匹配的那些行。

WHERE使用语法更容易看到这一点。

至于您的示例,在 MySQL 中(通常在 SQL 中),这两个查询是同义词。

另外,请注意 MySQL 也有一个STRAIGHT_JOIN子句。

使用此子句,您可以控制JOIN顺序:在外循环中扫描哪个表,在内循环中扫描哪个表。

您无法在 MySQL 中使用WHERE语法来控制它。

于 2009-06-19T16:17:28.200 回答
211

其他人指出,这INNER JOIN有助于人类的可读性,这是首要任务,我同意。
让我试着解释一下为什么连接语法更具可读性。

一个基本的SELECT查询是这样的:

SELECT stuff
FROM tables
WHERE conditions

SELECT子句告诉我们我们得到了什么FROM子句告诉我们从哪里得到它,子句WHERE告诉我们从哪一个得到。

JOIN是关于表的声明,它们是如何绑定在一起的(从概念上讲,实际上是一个表)。

任何控制表的查询元素——我们从中获取东西——在语义上都属于FROM子句(当然,这就是JOIN元素所在的地方)。将 join-elements 放入WHERE子句将whichwhere-fromJOIN混为一谈,这就是首选语法的原因。

于 2009-06-19T16:30:59.527 回答
153

在 ON / WHERE 中应用条件语句

这里我已经解释了逻辑查询处理步骤。


参考:Inside Microsoft® SQL Server™ 2005 T-SQL 查询
出版商:Microsoft Press
Pub 日期:2006 年 3 月 7 日
印刷 ISBN-10:0-7356-2313-9
印刷 ISBN-13:978-0-7356-2313-2
页数:640

Microsoft® SQL Server™ 2005 T-SQL 查询内部

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

SQL 与其他编程语言不同的第一个显着方面是代码的处理顺序。在大多数编程语言中,代码是按照编写的顺序处理的。在 SQL 中,处理的第一个子句是 FROM 子句,而最先出现的 SELECT 子句几乎最后处理。

每个步骤都会生成一个虚拟表,用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或外部查询)不可用。只有最后一步生成的表才会返回给调用者。如果查询中未指定某个子句,则简单地跳过相应的步骤。

逻辑查询处理阶段的简要说明

如果这些步骤的描述目前似乎没有多大意义,请不要太担心。这些提供作为参考。场景示例之后的部分将更详细地介绍这些步骤。

  1. FROM:在FROM子句的前两个表之间进行笛卡尔积(交叉连接),结果生成虚拟表VT1。

  2. ON:对 VT1 应用 ON 滤波器。仅将<join_condition>TRUE 的行插入到 VT2。

  3. OUTER (join):如果指定了 OUTER JOIN(与 CROSS JOIN 或 INNER JOIN 相对),则将保留表中的行或未找到匹配的表添加到 VT2 的行中作为外行,生成VT3。如果 FROM 子句中出现两个以上的表,则在最后一个连接的结果和 FROM 子句中的下一个表之间重复应用步骤 1 到 3,直到处理完所有表。

  4. WHERE:将 WHERE 过滤器应用于 VT3。仅将<where_condition>TRUE 的行插入到 VT4。

  5. GROUP BY:VT4 中的行根据 GROUP BY 子句中指定的列列表分组排列。产生VT5。

  6. 立方体 | ROLLUP:将超组(组的组)添加到来自 VT5 的行,生成 VT6。

  7. HAVING:HAVING 过滤器应用于 VT6。仅将<having_condition>TRUE 的组插入到 VT7。

  8. SELECT:处理SELECT列表,生成VT8。

  9. DISTINCT:从 VT8 中删除重复的行。产生VT9。

  10. ORDER BY:VT9 中的行根据 ORDER BY 子句中指定的列列表进行排序。生成游标 (VC10)。

  11. TOP:从VC10的开头选择指定的行数或百分比。表 VT11 生成并返回给调用者。



因此,(INNER JOIN) ON 将在应用 WHERE 子句之前过滤数据(此处将自行减少 VT 的数据计数)。随后的连接条件将使用过滤后的数据执行,从而提高性能。之后,只有 WHERE 条件会应用过滤条件。

(在 ON / WHERE 中应用条件语句在少数情况下不会产生太大影响。这取决于您连接了多少表以及每个连接表中可用的行数)

于 2009-12-22T06:24:07.403 回答
70

隐式连接 ANSI 语法较旧,不​​太明显,不推荐使用。

此外,关系代数允许WHERE子句和中的谓词可互换INNER JOIN,因此即使是INNER JOIN带有WHERE子句的查询也可以让优化器重新排列谓词。

我建议您以最易读的方式编写查询。

有时这包括制作INNER JOIN相对“不完整”的标准并将一些标准WHERE简单地放入其中以使过滤标准列表更易于维护。

例如,而不是:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

写:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

但这当然取决于。

于 2009-06-19T16:23:45.003 回答
34

一旦您需要开始向查询中添加更多表,隐式连接(这是您的第一个查询所称的)变得更加混乱、难以阅读和难以维护。想象一下在四个或五个不同的表上执行相同的查询和连接类型……这是一场噩梦。

使用显式连接(您的第二个示例)更具可读性和易于维护。

于 2009-06-19T16:19:14.940 回答
28

我还要指出,使用旧语法更容易出错。如果您使用不带 ON 子句的内部连接,则会出现语法错误。如果您使用较旧的语法并忘记 where 子句中的连接条件之一,您将获得交叉连接。开发人员通常通过添加 distinct 关键字(而不是修复联接,因为他们仍然没有意识到联接本身已损坏)来解决此问题,这似乎可以解决问题,但会大大减慢查询速度。

此外,如果您在旧语法中有交叉连接,维护人员将如何知道您是否打算拥有一个(在某些情况下需要交叉连接)或者它是否应该修复?

让我指出这个问题,看看为什么使用左连接时隐式语法不好。 Sybase *= 到 Ansi 标准,同一个内表有 2 个不同的外表

另外(个人咆哮),使用显式连接的标准已有 20 多年的历史,这意味着隐式连接语法已经过时了 20 年。你会使用已经过时 20 年的语法编写应用程序代码吗?为什么要写数据库代码呢?

于 2009-06-19T16:46:59.837 回答
13

它们具有不同的人类可读含义。

但是,根据查询优化器的不同,它们对机器可能具有相同的含义。

您应该始终编码可读。

也就是说,如果这是一个内置关系,则使用显式连接。如果要匹配弱相关数据,请使用 where 子句。

于 2009-06-19T16:20:49.900 回答
12

SQL:2003 标准更改了一些优先规则,因此 JOIN 语句优先于“逗号”连接。这实际上可以根据设置方式更改查询的结果。当 MySQL 5.0.12 切换到遵守标准时,这会给一些人带来一些问题。

因此,在您的示例中,您的查询将相同。但是如果你添加了第三个表: SELECT ... FROM table1, table2 JOIN table3 ON ... WHERE ...

在 MySQL 5.0.12 之前,table1 和 table2 将首先连接,然后是 table3。现在(5.0.12 及更高版本),首先连接 table2 和 table3,然后连接 table1。它并不总是改变结果,但它可以而且你甚至可能没有意识到这一点。

我不再使用“逗号”语法,选择您的第二个示例。无论如何,它更具可读性,JOIN 条件与 JOIN 一起使用,而不是分成单独的查询部分。

于 2009-06-19T17:28:56.930 回答
5

我知道您在谈论 MySQL,但无论如何:在 Oracle 9 中,显式连接和隐式连接会生成不同的执行计划。已在 Oracle 10+ 中解决的 AFAIK:不再有这样的区别。

于 2009-06-19T17:03:42.483 回答
3

如果您经常编写动态存储过程,那么您会爱上您的第二个示例(使用 where)。如果您有各种输入参数和大量变形混乱,那么这是唯一的方法。否则,它们都将运行相同的查询计划,因此经典查询绝对没有明显差异。

于 2019-07-09T16:23:52.087 回答
2

ANSI 连接语法肯定更便携。

我正在升级 Microsoft SQL Server,我还要提一下,2005 SQL Server 及更高版本不支持 SQL Server 中外部连接的 =* 和 *= 语法(没有兼容模式)。

于 2009-06-19T16:50:12.830 回答
0

对于隐式连接,我有两点(第二个示例):

  1. 告诉数据库你想要什么,而不是它应该做什么。
  2. You can write all tables in a clear list that is not cluttered by join conditions. Then you can much easier read what tables are all mentioned. The conditions come all in the WHERE part, where they are also all lined up one below the other. Using the JOIN keyword mixes up tables and conditions.
于 2020-11-04T14:49:41.313 回答