INNER JOIN ON vs WHERE子句

JCC*_*CyC 893 mysql sql join inner-join

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

你可以做:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)
Run Code Online (Sandbox Code Playgroud)

要不然:

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

这两个是以同样的方式工作MySQL吗?

Qua*_*noi 687

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

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

OUTER JOIN只要有需要,它也可以很容易地替换.

WHERE语法更关系模型为主.

两个表格的结果JOIN是应用过滤器的表的笛卡尔积,其仅选择具有匹配的连接列的那些行.

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

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

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

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

你无法使用WHERE语法在MySQL中控制它.

  • FWIW,在`WHERE`子句中使用带有连接条件的逗号也在ANSI标准中. (12认同)
  • 谢谢,Quassnoi.你的ans里有很多细节; 公平地说"是的,那些查询是等价的,但你应该使用内连接,因为它更易读,更容易修改"? (9认同)
  • 尽管如此,ANSI SQL-89在`WHERE`子句中使用逗号和条件指定了连接(没有条件,连接相当于交叉连接,如你所说).ANSI SQL-92添加了`JOIN`关键字和相关语法,但后向兼容性仍支持逗号样式语法. (9认同)
  • @allyourcode:对于`Oracle`,`SQL Server`,`MySQL`和`PostgreSQL` - 是的.对于其他系统,也可能,但你最好检查一下. (8认同)
  • @ArvindSridharan:没有 (3认同)
  • `@Bill Karwin`:`JOIN` 关键字直到最近才成为专有标准的一部分。它仅在版本“9”中进入“Oracle”,并在版本“7.2”中进入“PostgreSQL”(均于“2001”发布)。该关键字的出现是“ANSI”标准采用的一部分,这就是为什么该关键字通常与“ANSI”相关联,尽管后者也支持逗号作为“CROSS JOIN”的同义词。 (2认同)
  • InterBase 4.0 是商业 RDBMS 实现的一个示例,早在 1994 年就支持“JOIN”语法。 (2认同)
  • 请注意,隐式连接与显式连接并不相同,当“没有任何改变”时,隐式连接偶尔会让您感到惊讶,特别是在处理空值(生产中的错误)时;如果您想连接表,请明确地执行此操作(连接... on...)并避免自己头痛 (2认同)
  • 使用“join on”而不是“WHERE”是否会提高性能? (2认同)
  • 隐式连接 (,) 与 CROSS JOIN 和 (INNER) JOIN ON 1=1 完全相同,只是它的优先级较低。(OUTER) LEFT/RIGHT/FULL JOIN 与 (INNER) JOIN 不同(它们可以添加带有 NULL 的额外行)。如果您不使用带有 WHERE 的子选择,则必须在 OUTER JOIN 之前在 JOIN 中使用 ON。(@BlackTigerX 是错误的。) (2认同)

Car*_*ter 173

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

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

SELECT stuff
FROM tables
WHERE conditions
Run Code Online (Sandbox Code Playgroud)

SELECT子句告诉我们什么,我们正在找回; FROM子句告诉我们从哪里获取它,WHERE子句告诉我们我们得到了哪些.

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

  • 感谢您澄清为什么内部联接是首选卡尔.我认为你的ans隐含在其他的中,但显式通常更好(是的,我是Python粉丝). (6认同)
  • ON和WHERE的语义意味着对于最后一次OUTER JOIN*之后的JOIN,它与您使用的*无关.虽然您将ON表征为JOIN的一部分,但它也是*在笛卡尔积之后的过滤.**ON和WHERE都过滤笛卡尔积.但是在最后的OUTER JOIN之前,必须使用ON或带有WHERE的子选择*.(JOIN不是"on"列对.任何两个表都可以在任何条件下JOIN.这只是一种解释JOINs ON特定列相等的方法.) (2认同)
  • 即使您使用 WHERE 达到与 INNER JOIN 相同的效果,您也会在查询的 FROM 部分提及您的两个表。所以基本上你仍然暗示你在 FROM 子句中获取数据的位置,所以我想你不能说它一定“混淆了which和where-from” (2认同)

raf*_*een 133

在ON/WHERE中应用条件语句

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


参考:Microsoft®SQLServer™2005内部T-SQL查询
发布者:Microsoft Press
Pub日期: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>
Run Code Online (Sandbox Code Playgroud)

与其他编程语言不同的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并将其返回给调用者.



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

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

  • *"因此,(INNER JOIN)ON将在应用WHERE子句之前过滤数据(此处VT的数据计数将减少)."*不一定.这篇文章是关于*logical*处理的顺序.当你说一个特定的实现会在另一件事之前做一件事时,你在谈论*实现的*处理顺序.允许实现进行他们喜欢的任何优化,只要结果与实现遵循逻辑顺序的结果相同即可.Joe Celko在Usenet上写了很多关于这个的文章. (10认同)

Cad*_*oux 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
Run Code Online (Sandbox Code Playgroud)

写:

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
Run Code Online (Sandbox Code Playgroud)

但这当然取决于它.

  • 你的第一个片段肯定会伤害我的大脑.有人真的这样做吗?如果我遇到那样做的人,我可以打败他吗? (16认同)
  • @allyourcode:虽然在INNER JOIN中很少见到这种类型的连接语法,但是对于RIGHT JOIN和LEFT JOINS来说很常见 - 在连接谓词中指定更多细节消除了对子查询的需要并防止外部连接被无意中转动进入INNER JOINs.(虽然我同意对于INNER JOIN,我几乎总是在WHERE子句中放入c.State ='NY') (14认同)
  • 我找到了最有意义的标准.如果我正在加入一个时间上一致的快照查找表(并且我没有强制选择有效日期的视图或UDF),我将在连接中包含生效日期而不是在WHERE中,因为它更少可能会意外地被删除. (3认同)
  • @allyourcode 我肯定会这么做!我同意凯德的观点..我很好奇是否有[体面的理由不这样做](http://stackoverflow.com/questions/25272658/position-of-inner-join-filtering-conditions-in- a-query-on-or-where-子句) (2认同)

mat*_*t b 30

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

使用显式连接(第二个示例)更易读,易于维护.

  • 我不能不同意.JOIN语法非常冗长,难以组织.我有很多查询使用WHERE子句连接加入5,10,甚至15个表,它们是完全可读的.使用JOIN语法重写这样的查询会导致乱码.这只是表明这个问题没有正确的答案,而且更多地取决于你对什么感到满意. (45认同)
  • 诺亚,我想你可能在这里少数. (31认同)
  • 我认为任何理智的DBMS都会将两个查询转换为相同的执行计划; 但实际上每个DBMS都是不同的,唯一可以确定的方法是实际检查执行计划(即,你必须自己测试). (5认同)
  • 我个人会因为简洁而可读性 (3认同)
  • 我得到亚特和诺亚+1.我喜欢多样性:).我可以看到诺亚来自哪里; 内部联接不会为语言添加任何新内容,而且肯定更加冗长.另一方面,它可以使你的'where'条件更短,这通常意味着它更容易阅读. (2认同)

HLG*_*GEM 25

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

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

让我指出这个问题,看看为什么如果你使用左连接,隐式语法是坏的. Sybase*=到Ansi Standard,具有2个不同的外部表用于相同的内部表

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

  • @HLGEM:虽然我完全同意显式JOIN更好,但有些情况下您只需要使用旧语法.一个现实世界的例子:ANSI JOIN仅在2001年发布的版本9i中进入Oracle,直到仅一年前(标准发布之后的16年),我不得不支持一堆8i安装,我们有发布关键更新.我不想保留两组更新,因此我们针对包括8i在内的所有数据库开发并测试了更新,这意味着我们无法使用ANSI JOIN. (3认同)
  • "你为什么要编写[20岁]的数据库代码?" - 我注意到**你使用`HAVING`编写SQL,因为SQL开始支持派生表,所以它已经过时了.我也注意到你不使用`NATURAL JOIN`,即使我认为它已经使'INNER JOIN`'过时了'.是的,你有理由(不需要在这里再说一遍!):我的观点是,那些喜欢使用旧语法的人也有他们的理由,语法的相对年龄几乎没有相关性. (2认同)

Joh*_*zen 12

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

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

您应始终编写可读代码.

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


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


Joã*_*cus 5

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