INNER JOIN ON vs WHERE子句

时间:2009-06-19 16:16:24

标签: sql mysql join inner-join

为简单起见,假设所有相关字段均为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中以相同的方式工作吗?

12 个答案:

答案 0 :(得分:652)

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

通常认为它更具可读性,尤其是当您加入大量表格时。

只要有需要,它也可以用OUTER JOIN轻松替换。

WHERE语法更加面向关系模型。

两个表JOIN ed的结果是应用了过滤器的表的笛卡尔积,它只选择那些匹配连接列的行。

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

至于你的例子,在MySQL(以及一般的SQL)中,这两个查询是同义词。

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

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

您无法使用WHERE语法在MySQL中控制此操作。

答案 1 :(得分:163)

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

基本的SELECT查询是:

SELECT stuff
FROM tables
WHERE conditions

SELECT子句告诉我们我们要回来的; FROM子句告诉我们 where 我们从中得到它,而WHERE子句告诉我们我们得到的

JOIN是关于表的声明,它们是如何绑定在一起的(从概念上讲,实际上是绑定在一个表中)。任何控制表的查询元素 - 我们从中获取东西 - 在语义上属于FROM子句(当然,这是JOIN元素所在的位置)。将join-elements放入WHERE子句会将哪个 where-from 混为一谈;这就是首选JOIN语法的原因。

答案 2 :(得分:125)

在ON / WHERE中应用条件语句

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


参考:内部Microsoft®SQLServer™2005 T-SQL查询
出版商:微软出版社
发布日期: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子句几乎最后处理。

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

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

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

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

  2. ON:ON滤波器应用于VT1。只有<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. 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并将其返回给调用者。

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

    (在ON / WHERE中应用条件语句在少数情况下不会产生太大差异。这取决于您加入的表数和每个连接表中可用的行数)

答案 3 :(得分:60)

隐式连接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

但这当然取决于。

答案 4 :(得分:29)

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

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

答案 5 :(得分:25)

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

另外,对于维护,如果您使用旧语法进行交叉连接,维护者将如何知道您是否有一个(有需要交叉连接的情况)或者是否应该修复意外? / p>

让我指出这个问题,看看为什么如果你使用左连接,隐式语法是坏的。 Sybase *= to Ansi Standard with 2 different outer tables for same inner table

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

答案 6 :(得分:12)

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

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

您应始终编码以便可读。

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

答案 7 :(得分:11)

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相关,而不是单独的查询部分。

答案 8 :(得分:4)

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

答案 9 :(得分:1)

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

我正在进行Microsoft SQL Server的升级,我还要提到的是,对于2005 sql server及更高版本,不支持SQL Server中外连接的= *和* =语法(没有兼容模式)。

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

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