为简单起见,假设所有相关字段都是 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
中的工作方式是否相同?
INNER JOIN
是您应该使用的 ANSI 语法。
它通常被认为更具可读性,尤其是当您连接大量表时。
每当需要时,它也可以很容易地用 OUTER JOIN
替换。
WHERE
语法更面向关系模型。
两个表的结果JOIN
是应用过滤器的表的笛卡尔积,该过滤器仅选择连接列匹配的那些行。
使用 WHERE
语法更容易看到这一点。
至于您的示例,在 MySQL 中(通常在 SQL 中),这两个查询是同义词。
另外,请注意 MySQL 也有一个 STRAIGHT_JOIN
子句。
使用此子句,您可以控制 JOIN
顺序:在外循环中扫描哪个表,在内循环中扫描哪个表。
您无法在 MySQL 中使用 WHERE
语法来控制它。
其他人指出 INNER JOIN
有助于人类的可读性,这是重中之重,我同意。
让我尝试解释一下为什么连接语法更具可读性。
一个基本的 SELECT
查询是这样的:
SELECT stuff
FROM tables
WHERE conditions
SELECT
子句告诉我们要返回的什么; FROM
子句告诉我们 从哪里 获取它,而 WHERE
子句告诉我们 哪些 我们得到它。
JOIN
是关于表的声明,它们是如何绑定在一起的(从概念上讲,实际上是一个表)。
任何控制表的查询元素(我们从中获取内容)在语义上都属于 FROM
子句(当然,这也是 JOIN
元素所在的位置)。将连接元素放入 WHERE
子句将 which 和 where-from 混为一谈,这就是首选 JOIN
语法的原因。
在 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
Inside Microsoft® SQL Server™ 2005 T-SQL Querying
(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 子句几乎最后处理。
每个步骤都会生成一个虚拟表,用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或外部查询)不可用。只有最后一步生成的表才会返回给调用者。如果查询中未指定某个子句,则简单地跳过相应的步骤。
逻辑查询处理阶段的简要说明
如果这些步骤的描述目前似乎没有多大意义,请不要太担心。这些提供作为参考。场景示例之后的部分将更详细地介绍这些步骤。
FROM:在FROM子句的前两个表之间进行笛卡尔积(交叉连接),结果生成虚拟表VT1。 ON:对 VT1 应用 ON 滤波器。只有
(在 ON / WHERE 中应用条件语句在少数情况下不会产生太大影响。这取决于您连接了多少表以及每个连接表中可用的行数)
隐式连接 ANSI 语法较旧,不太明显,不推荐使用。
此外,关系代数允许 WHERE
子句和 INNER JOIN
中的谓词可互换,因此即使是带有 WHERE
子句的 INNER JOIN
查询也可以让优化器重新排列谓词。
我建议您以最易读的方式编写查询。
有时这包括使 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
但这当然取决于。
一旦您需要开始向查询中添加更多表,隐式连接(这是您的第一个查询所称的)变得更加混乱、难以阅读和难以维护。想象一下在四个或五个不同的表上执行相同的查询和连接类型……这是一场噩梦。
使用显式连接(您的第二个示例)更具可读性和易于维护。
我还要指出,使用旧语法更容易出错。如果您使用不带 ON 子句的内部连接,则会出现语法错误。如果您使用较旧的语法并忘记 where 子句中的连接条件之一,您将获得交叉连接。开发人员通常通过添加 distinct 关键字(而不是修复联接,因为他们仍然没有意识到联接本身已损坏)来解决此问题,这似乎可以解决问题,但会大大减慢查询速度。
此外,如果您在旧语法中有交叉连接,维护人员将如何知道您是否打算拥有一个(在某些情况下需要交叉连接)或者它是否是应该修复的意外?
让我指出这个问题,看看为什么使用左连接时隐式语法不好。 Sybase *= to Ansi Standard with 2 different outer tables for same inner table
另外(个人咆哮),使用显式连接的标准已有 20 多年的历史,这意味着隐式连接语法已经过时了 20 年。你会使用已经过时 20 年的语法编写应用程序代码吗?为什么要写数据库代码呢?
HAVING
编写 SQL。我还注意到您不使用 NATURAL JOIN
,即使我认为它已使 INNER JOIN
“过时”。是的,你有你的理由(这里不需要再次陈述!):我的观点是,那些喜欢使用旧语法的人也有他们的理由,而且语法的相对年龄几乎没有相关性。
它们具有不同的人类可读含义。
但是,根据查询优化器的不同,它们对机器可能具有相同的含义。
您应该始终编码可读。
也就是说,如果这是一个内置关系,则使用显式连接。如果要匹配弱相关数据,请使用 where 子句。
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 一起使用,而不是分成单独的查询部分。
我知道您在谈论 MySQL,但无论如何:在 Oracle 9 中,显式连接和隐式连接会生成不同的执行计划。已在 Oracle 10+ 中解决的 AFAIK:不再有这样的区别。
如果您经常编写动态存储过程,那么您会爱上您的第二个示例(使用 where)。如果您有各种输入参数和大量变形混乱,那么这是唯一的方法。否则,它们都将运行相同的查询计划,因此经典查询绝对没有明显差异。
ANSI 连接语法肯定更便携。
我正在升级 Microsoft SQL Server,我还要提一下,2005 SQL Server 及更高版本不支持 SQL Server 中外部连接的 =* 和 *= 语法(没有兼容模式)。
*=
和 =*
从来都不是 ANSI,也从来都不是一个好的表示法。这就是为什么需要 ON 的原因——对于没有子选择的 OUTER JOIN(它是同时添加的,因此在 CROSS 和 INNER JOIN 中实际上不需要它们。)
对于隐式连接,我有两点(第二个示例):
告诉数据库你想要什么,而不是它应该做什么。您可以将所有表写在一个不受连接条件影响的清晰列表中。然后你可以更容易地阅读所有提到的表格。条件都出现在 WHERE 部分,它们也都排成一行。使用 JOIN 关键字会混淆表和条件。
Oracle
、SQL Server
、MySQL
和PostgreSQL
— 是的。对于其他系统,可能也是如此,但您最好检查一下。WHERE
子句中使用逗号和连接条件也在 ANSI 标准中。@Bill Karwin
:JOIN
关键字在过去似乎不是专有标准的一部分。它仅在版本9
中进入Oracle
,在版本7.2
中进入PostgreSQL
(均在2001
中发布)。此关键字的出现是ANSI
标准采用的一部分,这就是为什么此关键字通常与ANSI
相关联,尽管后者也支持逗号作为CROSS JOIN
的同义词。WHERE
子句中使用逗号和条件进行连接(如您所说,没有条件,连接相当于交叉连接)。 ANSI SQL-92 添加了JOIN
关键字和相关语法,但仍支持逗号样式语法以实现向后兼容性。