INNER JOIN ON vs WHERE子句

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

你可以这样做:

SELECTtable1.this, table2.that, table2.somethingelseFROMtable1, table2WHEREtable1.foreignkey = table2.primarykeyAND (some other conditions)

否则:

SELECTtable1.this, table2.that, table2.somethingelseFROMtable1 INNER JOIN table2ON table1.foreignkey = table2.primarykeyWHERE(some other conditions)

这两个在MySQL中以相同的方式工作吗?

688607 次浏览

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

它通常被认为更具可读性,特别是当您连接许多表时。

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

WHERE语法更面向关系模型。

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

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

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

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

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

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

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

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

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

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

您应该始终编写可读的代码。

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

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

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

我建议您以尽可能易读的方式编写查询。

有时这包括使INNER JOIN相对“不完整”,并将一些标准放在WHERE中,只是为了使过滤标准列表更容易维护。

例如,而不是:

SELECT *FROM Customers cINNER JOIN CustomerAccounts caON ca.CustomerID = c.CustomerIDAND c.State = 'NY'INNER JOIN Accounts aON ca.AccountID = a.AccountIDAND a.Status = 1

写:

SELECT *FROM Customers cINNER JOIN CustomerAccounts caON ca.CustomerID = c.CustomerIDINNER JOIN Accounts aON ca.AccountID = a.AccountIDWHERE c.State = 'NY'AND a.Status = 1

但这取决于,当然。

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

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

SELECT stuffFROM tablesWHERE conditions

SELECT子句告诉我们什么我们回来了;FROM子句告诉我们在哪里我们得到了它,WHERE子句告诉我们其中我们得到了。

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

任何控制表的查询元素-我们从中获取内容-在语义上都属于FROM子句(当然,这就是JOIN元素所在的位置)。将连接元素放入WHERE子句会合并其中来自何处,这就是为什么首选JOIN语法。

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

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

让我指出这个问题,看看为什么使用左连接时隐式语法不好。赛贝斯*=安西标准与2个不同的外部表相同的内部表

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

ANSI连接语法肯定更具可移植性。

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

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

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

因此,在您的示例中,您的查询将以相同的方式工作。但是如果您添加了第三个表:选择…从表1,表2加入表3…在哪里…

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

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

在ON/WHERE中应用条件语句

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


参考:Microsoft®SQLServer™2005 T-SQL查询
出版社:Microsoft Press
发布日期:2006年3月7日
打印ISBN-10:0-7356-2313-9
打印ISBN-13:978-0-7356-2313-2
页数:640

在Microsoft®SQLServer™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,第一个被处理的子句是OF子句,而首先出现的SELECT子句几乎是最后处理的。

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

逻辑查询处理阶段简述

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

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

  2. ON:ON过滤器应用于VT1。只有<join_condition>为TRUE的行才会插入到VT2。

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

  4. WHERE:WHERE过滤器应用于VT3。只有<where_condition>为TRUE的行才会插入到VT4。

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

  6. CUBE|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中应用条件语句在少数情况下不会有太大区别。这取决于您已经连接了多少个表以及每个连接表中可用的行数)

如果你经常编写动态存储过程,你会爱上你的第二个例子(使用where)。如果你有各种输入参数和大量变形混乱,那么这是唯一的方法。否则,它们都将运行相同的查询计划,所以在经典查询中肯定没有明显的区别。

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

  1. 告诉数据库你想要什么,而不是它应该做什么。
  2. 您可以将所有表写入一个清晰的列表中,而不会被连接条件弄乱。然后您可以更轻松地阅读所有提到的表。条件都在WHERE部分,它们也都在另一个下面排列。使用JOIN关键字混合表和条件。