为了简单起见,假设所有相关字段都是NOT NULL。
你可以做到:
1 2 3 4 5 6 7
| SELECT
table1.this, table2.that, table2.somethingelse
FROM
table1, table2
WHERE
table1.foreignkey = table2.primarykey
AND (SOME other conditions) |
否则:
1 2 3 4 5 6 7
| SELECT
table1.this, table2.that, table2.somethingelse
FROM
table1 INNER JOIN table2
ON table1.foreignkey = table2.primarykey
WHERE
(SOME other conditions) |
这两种方法在MySQL中的作用相同吗?
- @马可:给你
- 与FROM行上的多个表相比,SQL左联接可能重复?
- 如果我理解正确,第一个变量是ansi sql-89隐式语法,第二个变量是ansi sql-92显式联接语法。两者都将导致一致的SQL实现结果,并且在完成良好的SQL实现中都将导致相同的查询计划。我个人更喜欢SQL-89语法,但许多人更喜欢SQL-92语法。
- @Mikkorantalainen——这不是评论,而是答案。这个问题有14个答案,其中3个超过50分。你的评论补充了什么?在我看来,这是一个关于SQL-89和SQL-92的巨魔
- @霍根我指出了不同语法的官方名称。所有的答案都没有明确的拼写出全名,所以我决定添加这些作为评论。然而,我的评论没有回答实际问题,所以我补充说,作为评论,而不是作为答案。(高投票数的答案有"inner join is ansi syntax"和"implicit join ansi syntax is older"这样的说法,因为这两种语法都是不同的ansi语法,所以根本就没有说明任何内容。)
INNER JOIN是您应该使用的ANSI语法。
它通常被认为更具可读性,尤其是当您连接许多表时。
当需要时,也可以很容易地用OUTER JOIN替换。
WHERE语法更面向关系模型。
两个表的结果JOINed是应用过滤器的表的笛卡尔积,过滤器只选择那些连接列匹配的行。
使用WHERE语法更容易看到这一点。
对于您的示例,在MySQL中(通常在SQL中),这两个查询是同义词。
另外,mysql还有一个STRAIGHT_JOIN子句。
使用这个子句,您可以控制JOIN顺序:在外部循环中扫描哪个表,在内部循环中扫描哪个表。
在MySQL中,您不能使用WHERE语法来控制它。
- 谢谢,奎斯诺。您的ANS中有很多详细信息;公平地说,"是的,这些查询是等价的,但是您应该使用内部联接,因为它更可读,更容易修改"?
- @所有代码:对于Oracle,SQL Server,MySQL,PostgreSQL-是。对于其他系统,可能也是,但最好检查一下。
- fwiw,在WHERE条款中使用带有连接条件的逗号也在ANSI标准中。
- @Bill Karwin:JOIN关键字直到最近才成为专有标准的一部分。它只在版本9中进入Oracle,在版本7.2中进入PostgreSQL(均在版本2001中发布)。这个关键字的出现是ANSI标准采用的一部分,这就是为什么这个关键字通常与ANSI关联,尽管后者也支持逗号作为CROSS JOIN的同义词。
- 尽管如此,ANSISQL-89指定的联接要用一个WHERE子句中的逗号和条件来完成(如果没有条件,联接就相当于交叉联接,如您所说)。ANSISQL-92添加了JOIN关键字和相关语法,但为了向后兼容,仍然支持逗号样式的语法。
- Interbase 4.0是早在1994年就支持JOIN语法的商业RDBMS实现的一个例子。
- 请注意,隐式联接和显式联接不一样,隐式联接在"无任何更改"时会偶尔让您吃惊,尤其是在处理空值(生产中的错误)时;如果要联接表,请显式(联接…在……)避免你自己头痛
- @blacktigerx:你能更具体地说一下隐式和显式连接"不一样"吗?
- 同样,当使用空值时,通常在多个列上使用联接,其中一些值为空,根据使用的联接,您将得到不同的结果(例如:空与非空)
- 使用join on而不是使用WHERE是否会提高性能?
- @Arvindsridharan:不
- 隐式联接(,)与1=1上的交叉联接和(内部)联接完全相同,但优先级较低。(外部)左/右/完全联接与(内部)联接不同(它们可以添加具有空值的额外行)。如果不将嵌套select与where一起使用,则必须在外部联接之前使用in join。(@blacktigerx错误。)
- 这些不是MySQL的同义词,如果您为早期的MySQL版本显式地提供与优化的顺序,MySQL的优化器将选择您的连接顺序。
- @PlexQ:你是说来自OP的两个查询会产生不同的计划吗?
其他人指出,内部连接有助于人类的可读性,这是一个首要任务;我同意。让我试着解释一下为什么连接语法更易读。
基本的选择查询是:
1 2 3
| SELECT stuff
FROM TABLES
WHERE conditions |
select子句告诉我们要返回什么;from子句告诉我们从哪里获取它,where子句告诉我们要获取哪些。
join是一个关于表的语句,它们是如何绑定在一起的(实际上,在概念上,是如何绑定到一个表中)。任何控制表的查询元素(我们从中获取数据)在语义上都属于FROM子句(当然,这就是连接元素的位置)。将联接元素放入WHERE子句会混淆WHO和WHERE FROM;这就是为什么优先使用联接语法的原因。
- 感谢您澄清为什么选择内部加入Carl。我认为您的ANS是隐式的,但显式通常更好(是的,我是一个Python爱好者)。
- on和where的语义意味着,对于最后一个外部联接之后的联接,使用哪个并不重要。虽然您将上的特性化为联接的一部分,但它也是笛卡尔积之后的过滤。在笛卡尔积上和在何处过滤。但在最后一个外部联接之前必须使用ON或带WHERE的子集。(联接不是"on"列对。任何两个表都可以在任何条件下联接。这只是一种解释列相等的连接的方法。)
在on/where中应用条件语句
这里我已经解释了逻辑查询处理步骤。
参考资料:微软内部?SQL Server?2005 T-SQL查询发布者:微软出版社发布日期:2006年3月7日打印ISBN-10:0-7356-2313-9打印ISBN-13:978-0-7356-2313-2页数:640
在微软内部?SQL Server?2005 T-SQL查询
1 2 3 4 5 6 7 8 9
| (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。
打开:打开过滤器应用于VT1。仅将为真的行插入到vt2。
外部(联接):如果指定了外部联接(与交叉联接或内部联接相反),则将保留表中的行或找不到匹配项的表中的行作为外部行添加到vt2中的行中,生成vt3。如果FROM子句中出现两个以上的表,则在最后一个联接的结果和FROM子句中的下一个表之间重复应用步骤1到3,直到处理完所有表。
其中:将where过滤器应用于vt3。仅将为真的行插入到vt4。
group by:根据group by子句中指定的列列表,将vt4中的行分组排列。生成VT5。
cube汇总:将超级组(组组组)添加到vt5的行中,生成vt6。
HAVING:HAVING滤波器应用于VT6。只有为真的组才插入到VT7中。
选择:处理选择列表,生成vt8。
distinct:从vt8中删除重复的行。生成VT9。
ORDER BY:VT9中的行根据ORDER BY子句中指定的列列表进行排序。生成光标(VC10)。
顶部:从VC10的开头选择指定的行数或百分比。表VT11生成并返回给调用者。
因此,(inner join)on将在应用where子句之前过滤数据(vt的数据计数将在这里减少)。随后的连接条件将使用过滤后的数据执行,从而提高性能。之后,只有Where条件将应用筛选条件。
(在on/where中应用条件语句在少数情况下不会有太大的区别。这取决于已联接的表数和每个联接表中可用的行数)
- "因此,(inner join)on将在应用where子句之前过滤数据(vt的数据计数将在这里减少)。"不一定。本文是关于处理的逻辑顺序的。当您说一个特定的实现将先做一件事,然后再做另一件事时,您所说的是已实现的处理顺序。只要结果与实现遵循逻辑顺序的结果相同,就允许实现进行他们喜欢的任何优化。JoeCelko在Usenet上写了很多关于这个的文章。
- @上的rafidheen(内部连接)将过滤数据…在应用WHERE子句之前…这提高了性能。"很好。"之后,只有Where条件将应用筛选条件"HAVING子句呢?
- @詹姆斯,拉菲丁的说法是错误的。请参见手册中的"连接优化"。还有我在这一页上的其他评论。(和Mikesherrill'CatRecall's.)这样的"逻辑"描述描述了结果值,而不是实际计算结果值的方式。而且这种实现行为并不能保证不会改变。
隐式join-ansi语法较旧,不太明显,不推荐使用。
此外,关系代数允许WHERE子句和INNER JOIN子句中谓词的互换性,因此即使是WHERE子句中的INNER JOIN查询也可以由优化器重新安排谓词。
我建议您以最易读的方式编写查询。
有时,这包括使INNER JOIN相对"不完整",并将一些标准放入WHERE中,以使筛选标准列表更容易维护。
例如,而不是:
1 2 3 4 5 6 7 8
| 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 |
写:
1 2 3 4 5 6 7 8
| 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 |
当然,这要看情况而定。
- 你的第一个片段肯定更伤我的大脑。有人真的这么做吗?如果我遇到这样的人,我可以打他的头吗?
- 我找到了最有意义的标准。如果我要加入一个临时一致的快照查找表(并且我没有强制选择有效日期的视图或UDF),我将在联接中包括有效日期,而不是在何处,因为它不太可能被意外删除。
- @allyourcode:虽然很少在内部联接中看到这种类型的联接语法,但是对于右联接和左联接来说这是很常见的——在联接谓词中指定更详细的内容可以消除对子查询的需要,并防止您的外部联接无意中变成内部联接。(尽管我同意,对于内部连接,我几乎总是将c.state='ny'放在where子句中)
- @好吧,我一定会的!我同意凯德的观点。我很好奇是否有正当的理由不
隐式连接(这就是第一个查询的名称)一旦需要向查询中添加更多表,就会变得更加混乱、难以读取和难以维护。想象一下,在四个或五个不同的表上执行相同的查询和连接类型…这是一场噩梦。
使用显式连接(您的第二个示例)更易于阅读和维护。
- 我再也不能不同意了。连接语法非常复杂,难以组织。我有大量的查询使用WHERE子句连接5、10、甚至15个表,它们是完全可读的。使用连接语法重写这样的查询会导致混乱。这表明这个问题没有正确的答案,这更多地取决于你对什么感到满意。
- 诺亚,我想你可能是这里的少数人。
- 我给马特和诺亚+1。我喜欢多样性:)。我可以看到noah来自哪里;inner join并没有为语言添加任何新的内容,而且肯定更冗长。另一方面,它可以使你的"在哪里"的条件更短,这通常意味着它更容易阅读。
- 我个人喜欢可读性而不是简洁性。
- 是否有任何性能改进?它会是不同的性能虎钳吗?
- 我假设任何一个健全的DBMS都会将这两个查询转换成同一个执行计划;然而实际上,每个DBMS都是不同的,唯一确定的方法是实际检查执行计划(即,您必须自己测试)。
- 正如@rafidheen在另一个答案(具有SQL执行的详细顺序的答案)中建议的那样,一次过滤一个联接,与3个或更多表的完全笛卡尔联接相比,在反向应用where过滤器的情况下,减少联接操作的大小,这是真的吗?如果是这样,它将建议join提供性能改进(以及左/右联接的优点,另一个答案也指出了这一点)。
- @詹姆斯不,这不是真的。阅读手册中有关优化的内容,阅读教科书中有关逻辑和物理关系查询优化的内容。
我还将指出,使用旧语法更容易出错。如果使用不带ON子句的内部联接,将出现语法错误。如果您使用旧的语法,并且忘记了WHERE子句中的一个联接条件,那么您将得到一个交叉联接。开发人员经常通过添加distinct关键字来解决这个问题(而不是修复join,因为他们仍然没有意识到join本身被破坏),这看起来可以解决问题,但会大大降低查询速度。
另外,对于维护,如果在旧语法中有交叉连接,那么维护人员如何知道您打算使用交叉连接(在需要交叉连接的情况下)还是应该修复的事故?
让我指出这个问题,看看如果使用左联接,隐式语法为什么不好。Sybase*=符合ANSI标准,同一内表有两个不同的外表
另外(这里的personal rant),使用显式连接的标准已经超过20年了,这意味着隐式连接语法已经过时20年了。您会使用已经过时20年的语法编写应用程序代码吗?为什么要编写数据库代码?
- @hlgem:虽然我完全同意显式连接更好,但有时您只需要使用旧语法。一个现实世界的例子:AnsiJoin只在2001年发布的9i版本中进入了Oracle,直到一年前(从标准发布之日起16年),我不得不支持一系列8i安装,为此我们必须发布关键的更新。我不想维护两组更新,所以我们针对包括8i在内的所有数据库开发和测试了更新,这意味着我们无法使用ansi连接。
- +1有趣的一点是,当您指出没有内部连接的sintax更容易出错。当你说"…使用显式连接的标准是17岁"时,我对你的最后一句话感到困惑,那么你是否建议使用inner join关键字?
- @marco demaio,是的,总是使用内部联接或联接(这两个是相同的),或者左联接或右联接或交叉联接,永远不要使用隐式逗号联接。
- "为什么要编写[20年前]的数据库代码?"-我注意到您使用HAVING编写SQL,它自SQL开始支持派生表以来就"过时了"。我还注意到你不使用NATURAL JOIN,尽管我认为它已经使INNER JOIN过时了。是的,你有你的理由(不需要在这里再次陈述!):我的观点是,那些喜欢使用旧语法的人也有他们的原因,而且语法的相对年龄几乎没有关系。
- 我使用的数据库不支持自然联接。我不明白为什么已经被派生表替换了,请解释一下,我真的很感兴趣。然而,我从未见过任何使用隐式连接的人给出使用它们的理由,但事实并非如此,"这是我所习惯的。"如果您有一个,我想听听。对于隐式连接和内部连接,我不知道有什么技术优势。存在技术上的缺点,因为隐式联接可能会由于无法识别的意外交叉联接(使用内部联接语法是不可能的)而导致错误的结果集。
- @有一天,新接触SQL的人避免隐式连接特别重要,因为他们不理解连接概念,所以他们就是得到错误结果的人。而且您仍然在WHERE子句中编写联接条件,所以除了编写join这个词所需的时间之外,您什么都不节省。考虑到稍后在查询中编写条件,在添加联接时,实际上在查询中上下跳跃添加WHERE子句所需的时间可能比在查询中上下跳跃所需的时间要短。我太累的争论是愚蠢的。与隐式连接相比,您可以编写连接,也可以编写连接。
- 虽然你真的想从我这里得到信息,但不是在找理由。但我坚持认为"过时20年"是一个糟糕的辩护,并坚持我最初的评论。
- 标准中还有哪些地方(请告诉我哪里没有)。显然,没有过时的。另外,"而不是修复连接"向我展示了一个开发人员,他应该远离DBMS,通常远离DBMS。
- 有一天,"句法的相对年龄"具有"相关性"。通常,较新的语法/功能意味着比功能上相同的旧语法更高效和/或更可读(在本例中,主要是后者,但正如@hlgem所提到的,甚至前者,在键入时减少跳跃)。
- @J&252;rgen A.erhard:语法(/feature)"仍然在标准"(/supported)中的事实是使用它的一个糟糕的防御。有无数的例子说明旧语法(/feature)"仍然在标准"(/supported)中,主要是为了向后兼容(如果不只是这样)。此外,hlgem从未说过,WHERE形式不再是标准形式,仅仅是JOIN形式(大多数情况下,而且大多数账户都是"更好的")已经存在了足够的时间来显著减少(如果不是的话)使用旧形式的原因(即使用遗留代码)。
它们有着不同的人类可读的含义。
但是,根据查询优化器的不同,它们对机器可能具有相同的含义。
您应该始终编写可读的代码。
也就是说,如果这是一个内置关系,请使用显式联接。如果要匹配弱相关数据,请使用WHERE子句。
SQL:2003标准更改了一些优先规则,因此join语句优先于"逗号"联接。这实际上可以根据查询的设置方式更改查询结果。当MySQL5.0.12转向遵循标准时,这给一些人带来了一些问题。
所以在您的示例中,您的查询也会工作相同。但是如果您添加了第三个表:选择…从表1,表2加入表3…在哪里?
在MySQL5.0.12之前,首先将表1和表2连接起来,然后是表3。现在(5.0.12及以上),首先连接表2和表3,然后连接表1。它并不总是改变结果,但它可以,你甚至可能没有意识到。
我不再使用"逗号"语法,选择第二个例子。无论如何,它的可读性要高得多,连接条件是与连接一起的,而不是分成单独的查询部分。
- 标准SQL没有更改。MySQL是错误的,现在是正确的。参见mysql手册。
我知道你在谈论MySQL,但无论如何:在Oracle9中,显式联接和隐式联接将生成不同的执行计划。已经在Oracle10+中解决的afaik:再也没有这样的区别了。
ansi-join语法绝对更易于移植。
我将对Microsoft SQL Server进行升级,我还将提到2005 SQL Server及更高版本不支持SQL Server中外部联接的=*和*=语法(没有兼容模式)。
- 即使在SQL Server 2000中,=和=也可能给出错误的结果,不应使用。
- *=和=*不是ANSI,也不是一个好的符号。这就是为什么需要启用的原因——对于没有子选择的外部联接(同时添加了子选择,因此在交叉联接和内部联接中实际上不需要它们)。