为什么SQL ANSI-92标准没有比ANSI-89更好地被采用?

113

我在每个公司工作时都发现,人们仍然使用 ANSI-89 标准编写他们的 SQL 查询:

select a.id, b.id, b.address_1
from person a, address b
where a.id = b.id

而不是ANSI-92标准:

select a.id, b.id, b.address_1
from person a
inner join address b
on a.id = b.id

对于这样一个非常简单的查询,可读性差异不大,但是对于大型查询,我发现将我的连接条件与表格列出在一起,可以让我更容易看到我可能在连接中遇到问题的地方,并让我将所有筛选器放在WHERE子句中。更何况,我感觉在Oracle中,外部连接比使用(+)语法更直观。

当我试图向人们传教ANSI-92时,是否有使用ANSI-92比使用ANSI-89具有明显性能优势的具体证据?我会自己尝试,但是我们这里的Oracle设置不允许我们使用EXPLAIN PLAN - 不想让人们试图优化他们的代码,不是吗?


7
SQL-92连接符号的一个主要优点是,它提供了一种标准且相对合理的方法来编写LEFT OUTER JOIN及其变体。每个数据库管理系统都有自己的语法变体(通常很糟糕;实际上我认为没有例外的不好),并且常常具有稍微不同的语义。SQL-92解决了这个问题,新的符号仅从这个角度来看就值得使用。我认为一旦习惯了,这种方式更清晰易懂。需要一些适应时间,但不难,一旦转变,就不会再回去了。 - Jonathan Leffler
语义学,谢曼蒂克,反谢曼蒂克! - Sam
我来晚了,但似乎没有人指出Oracle本身建议使用FROM子句OUTER JOIN语法而不是Oracle连接运算符 - bornfromanegg
我已经添加了一个新的答案,它非常更新和直接,清晰地澄清了其他答案中的误解。请参考 https://dev59.com/DXRC5IYBdhLWcg3wVvYt#47720615。 - Evan Carroll
92个连接现在可能被更好地采用了,因为您的问题是从2008年提出的。我以前从未听说过“ANSI 92 JOIN”或“ANSI 89 JOIN”,而且我认为“ANSI-JOIN”是旧的89语法。我先学习了92个连接,但是在阅读了postgres经常将连接转换为89形式,并且它们不会像之前那样强制查询规划器进入某个连接顺序后,我开始更多地使用它们。现在,我更喜欢89语法,因为我体验到了更好的性能,而且语法更加简洁。 - ADJenks
16个回答

84

根据 Peter Gulutzan 和 Trudy Pelzer 的《SQL 性能调优》一书,对于他们测试的六到八种 RDBMS 品牌,无论是 SQL-89 还是 SQL-92 风格的连接,在优化或执行查询时都没有差别。可以认为大多数 RDBMS 引擎在优化或执行查询之前会将语法转换为内部表示,因此人类可读的语法并不重要。

我也试图宣传 SQL-92 语法。16 年后,现在是时候让人们开始使用它了!所有品牌的 SQL 数据库现在都支持它,所以没有理由继续使用非标准的 Oracle 语法 (+) 或 Microsoft/Sybase 语法 *=

至于为什么很难改掉开发者社区使用 SQL-89 的习惯,我只能假设有一个大型的“金字塔底部”的程序员群体,他们通过复制和粘贴编写代码,使用古老的书籍、杂志文章或其他代码库中的例子,这些人不是抽象地学习新的语法。一些人按模式匹配,一些人靠死记硬背。

然而,我逐渐看到人们使用 SQL-92 语法的频率比我以前看到的要高。自从1994年以来,我一直在网上回答 SQL 问题。


6
我完全同意。我与许多SQL编码人员一起工作,他们学习了15年或更久的SQL(就像我自己一样),并且对他们开始工作以来的任何创新一无所知。他们也缺乏任何发现的兴趣。 - Tony Andrews
8
同意,但需要补充说明,在某些场景下,使用旧的 ANSI-89 Join 语法会产生错误结果...特别是在外连接时,如果在连接的"外部"一侧存在与连接无关的列的条件过滤谓词时。 - Charles Bretana
1
我不了解 MS SQL 的具体内部情况,但这是 SQL-92 语法值得采用的好的轶事证明。对我来说很有效! - Bill Karwin
2
庞大?请发布你的工作成果。我最好的猜测是这不是一个苹果和苹果之间的比较,但请不要回复“是的,它就是”。只需回复一个我们可以重现的测试用例,版本,补丁级别等信息即可。 - Mark Brady
2
此外,“轶事”证据本身并不是科学的衡量标准。它总是需要保留一定的怀疑态度。 - Bill Karwin
显示剩余5条评论

17

我认为ANSI092标准包含了一些相当丑陋的语法。自然连接是其中之一,USING从句是另一个。在我看来,向表中添加列不应该破坏代码,但自然连接会以最恶劣的方式破坏代码。"最好"的破坏方式是编译错误。例如,如果你在某个地方使用SELECT *,添加列可能无法编译。接下来最好的失败方式是运行时错误。它更糟糕,因为你的用户可能会看到它,但它仍然会给你一个很好的警告,说明你已经破坏了某些东西。如果你使用ANSI92并编写带有自然连接的查询,它不会在编译时破坏,也不会在运行时破坏,查询将突然开始产生错误的结果。这些类型的错误是隐匿的。报告出现问题,潜在的财务披露不正确。

对于那些不熟悉自然连接的人。它们将两个表连接在每个表中都存在的列名上。当你拥有4列键并且厌倦了输入时,这真的很酷。问题在于Table1具有名为DESCRIPTION的预先存在的列,并且您向Table2添加了一个新列,例如,我不知道,像DESCRIPTION这样的无害名称,现在您正在连接两个表中的VARCHAR2(1000)自由形式字段。

使用USING从句还可能导致完全的歧义,除了上述问题。在另一个SO post中,有人展示了这个ANSI-92 SQL,并请求帮助阅读它。

SELECT c.* 
FROM companies AS c 
JOIN users AS u USING(companyid) 
JOIN jobs AS j USING(userid) 
JOIN useraccounts AS us USING(userid) 
WHERE j.jobid = 123

这完全是含糊不清的。我在公司和用户表中都放置了UserID列,但没有投诉。如果公司中的UserID列是修改该行的最后一个人的ID呢?

说真的,有人能解释为什么这种歧义是必要的吗?为什么它直接内置在标准中?

我认为Bill是正确的,有很多开发人员通过复制/粘贴来编码。事实上,当涉及到ANSI-92时,我也可以承认自己有点像其中之一。我看到的每个示例都显示了多个连接嵌套在括号中。老实说,这使得在sql中挑选表格变得非常困难。但是,然后一个SQL92的布道者解释说,这实际上会强制执行连接顺序。天哪...所有那些我见过的复制粘贴者现在实际上正在强制执行连接顺序 - 这是95%的时间最好留给优化器完成的工作,尤其是复制粘贴者。

当Tomalak说:

人们不会仅仅因为新语法存在而切换

它必须给我带来一些东西,我没有看到任何好处。即使有好处,负面影响也太大了,无法忽视。


3
我倾向于使用ON,因为它比USING或NATURAL JOIN更不含糊。至于括号,学习Microsoft Access上的“SQL”的人会使用它,因为如果省略它们,Access会发出警告。(引号表示需要使用手指打出的引号) - Powerlord
2
我从不使用NATURAL JOIN,原因就像你提到的那样。如果你以模糊的方式使用USING,关系型数据库管理系统应该会给你一个错误提示。例如MySQL就是这样的。当程序员使用动态SQL时,编译时失败是不可能的。 - Bill Karwin
3
如果你不了解你的数据,不能在适当的时候使用自然连接或 USING,那么你可能不应该为其编写 SQL。对于无知给予负分。 - Rob
6
在我看来,自然连接和使用SELECT *语句(然后发现客户端依赖于字段顺序)属于同一类别——这只会让人感觉有些不够严谨。请注意,这里的翻译旨在保留原意并尽可能简洁易懂,不含解释或其他额外内容。 - Basic
2
你所描述的自然连接问题是与你的数据库设计有关,而不是标准本身的问题。你应该了解你的数据,并为列名建立标准。 - Travis
显示剩余6条评论

14

有几个原因可以解释:

  • 人们出于惯性而这么做。
  • 人们懒得打更多的字,因此喜欢“旧式”的连接方式。
  • 初学者经常无法理解 SQL-92 连接语法。
  • 人们不会改用新的语法,只是因为新语法存在。
  • 人们不知道新(如果你愿意这样称呼)语法的好处,主要在于可以在进行外部连接之前过滤表格,而不是在 WHERE 从句中进行过滤。

就我个人而言,我所有的连接都用 SQL-92 语法,并尽可能将代码转换。这是更清晰、更易读和功能更强大的方法。但很难说服一个人使用新风格,当他们认为它会增加打字的工作量,而且不会改变查询结果时。


3
对许多人来说,甚至看SQL都让他们头疼。修改任何已经运行的代码都存在引入错误的风险,特别是当程序员闭着眼睛修改时。 :-) - Bill Karwin
嗯...对我来说,即使查看复杂的正则表达式也毫无压力。SQL也伤不了我。;-) - Tomalak
初学者经常会遇到问题... 好吧,这是一个卖点。 - Mark Brady
3
由于某种原因,我不确定那是一条“支持”还是“反对”的评论……也许我的讽刺检测器出了问题。 - Tomalak

11

对于以上关于NATURAL JOIN和USING的帖子的回应。

为什么您会需要使用它们——它们在ANSI-89中不可用,并作为ANSI-92的快捷方式添加。

我从不将联接留给机会,总是指定表/别名和id。

对我来说,唯一的选择就是ANSI-92。它更冗长,语法不受ANSI-89追随者的喜欢,但它将您的JOIN与FILTERING清晰地分开。


我认为NATURAL JOIN不是一种快捷方式,而是在关系型数据库中进入面向对象数据库编程的一种过渡。 - Armand
自然连接非常晦涩和容易出错。避免使用它们,好好休息。 - The Impaler

5
首先需要说明的是,在 SQL Server 中,外连接语法(*=)并不总是能正确地返回结果。有时会将其解释为交叉连接而不是外连接。因此,这就是停止使用它的一个很好的理由。此外,该外连接语法已被弃用,并且在 SQL Server 2008 之后的版本中将不再存在。你仍然可以执行内连接,但为什么要这样做呢?它们不够清晰,维护起来也更加困难。你不容易知道哪部分是连接的一部分,哪部分只是where条件子句。
我认为你不应该使用旧语法的一个原因是,了解连接以及它们所做和不做的事情是写 SQL 代码的任何人必须掌握的关键步骤。不应该在不充分理解连接的情况下编写任何 SQL 代码。如果你充分理解连接,你可能会得出结论,ANSI-92 语法更加清晰、易于维护。我从未遇到过一个 SQL 专家不喜欢 ANSI-92 语法而偏爱旧语法的。
大多数我遇到或与之打交道的使用旧代码的人,真的不理解连接,因此在查询数据库时会遇到麻烦。这是我的个人经验,所以我并不是说这总是正确的。但作为一名数据专家,多年来我必须修复太多这种问题,因此我相信这种说法。

1
很高兴见到你。很荣幸成为你的第一个。 - Mark Brady

4

我在学校时学习的是ANSI-89,并在工业领域工作了几年。然后离开了令人惊叹的DBMS世界,有8年没接触过。但我回来后发现教授的都是新的 ANSI 92 规范。 我学会了 JOIN ON 语法,现在我真正教 SQL 并推荐使用新的 JOIN ON 语法。

但我看到的问题是,在 ANSI 92 连接规范下,相关子查询似乎不合理。当连接信息包含在 WHERE 中且相关子查询在 WHERE 中“连接”时,一切似乎都正确和一致。 在 ANSI 92 表连接条件不在 WHERE 中而子查询“连接”在WHERE中时,语法似乎不一致。 另一方面,试图“修复”此不一致可能只会让情况更糟。


另一方面,你可能根本不应该编写相关子查询,因为它们会影响性能。它们就像将光标放入查询中一样。呃。 - HLGEM

3

我有一个查询,最初是为不支持SQL 92连接语法的SQL Server 6.5编写的,即

select foo.baz
from foo
  left outer join bar
  on foo.a = bar.a

相反,被写成了

select foo.baz
from foo, bar
where foo.a *= bar.a

查询已经存在一段时间了,相关的数据积累使得查询运行速度过慢,大约需要90秒才能完成。当这个问题出现时,我们已经升级到了SQL Server 7。
在尝试索引和其他东西之后,我将连接语法更改为符合SQL 92标准。查询时间降至3秒。
有很好的理由进行切换。
转载自这里

3
我不确定答案,但这是一场宗教战争(虽然规模比Mac-Pc或其他战争小)。猜测直到最近,Oracle(也许其他供应商也是如此)没有采用ANSI-92标准(我认为是在Oracle v9左右),因此,对于仍在使用这些版本(或希望代码可在使用这些版本的服务器之间移植)的公司的DBA / Db开发人员来说,他们必须坚持使用旧标准...这真是遗憾,因为新的连接语法更易读,而旧语法在几种已知情况下会生成错误(不正确)的结果。
具体而言,在“外部”连接表的非连接相关列上有条件过滤谓词时的外部连接。

是的,Oracle 9i;发布于2001年。有点晚了! - user565869
1
MS SQL在1995年添加了INNER JOIN,但直到1996年才添加了LEFT JOIN。MySQL自3.23版本以来就支持它,该版本于1999年发布;PostgreSQL自7.2版本以来就支持它,该版本于2002年发布。对于Teradata,一些随意的谷歌搜索并没有给我答案。 - user565869
是的,这正是在1991年证明了Oracle优越性的谴责:像MS Access这样使用“Left”和“Right”语法的数据库系统并不符合ANSI标准SQL。像那样发布SQL是让其他人嘲笑你的机会。有点像现在的Twitter和Facebook。 - david

2

惯性和实用性。

ANSI-92 SQL就像打字。在某种理论上,它可能会让一切变得更好,但是现在我可以通过四个手指盯着键盘打得更快。如果我需要使用ANSI-92 SQL来解决ANSI-89 SQL无法解决的问题,那么我会使用它。(事实上,我在Access中使用它。)如果一直使用它可以帮助我更快地解决现有的问题,我会花时间去学习它。但是我可以毫不费力地使用ANSI-89 SQL而不必考虑语法。我受雇于解决问题-思考SQL语法是浪费我的时间和雇主的金钱。

总有一天,年轻的弟子,你会为自己使用ANSI-92 SQL语法辩护,对抗那些唠叨你应该使用SQL3(或其他版本)的年轻人。然后你会明白。:-)


2
这里描述的方法听起来非常像“等它坏了再修”的思路,避免预防性维护的想法。是的,你得到报酬是为了解决问题,但你也得到报酬是为了为公司提供更多价值。在你的情况下,ANSI-89在短期内提供了更大的价值,但从长远来看,不投资ANSI-92将是更昂贵的选择。 - Travis
坚持一直做同样的事情会给那些在你离开后需要维护你的代码的可怜人带来代价。这并不意味着你应该转向当下流行的技术,但采用最佳实践几乎总能在可维护性方面得到回报。 - user565869
我不会转向Excel(在那里,您可以用鼠标三次点击完成任何操作),因为我已经记住了我需要的数千个Lotus 123命令,并且我使用它们很舒适!哈! - Charles Bretana

2
以下是SQL-89和SQL-92的一些比较,并澄清了其他答案中的一些误解。
1. “自然连接”是一个可怕的想法。它们是隐式的,需要关于表的元信息。SQL-92并不要求使用它们,所以可以简单地忽略它们。它们与本讨论无关。
2. 使用“USING”是一个好主意,它有两个效果:
- 它仅从等值连接的结果集中产生一列。 - 它强制执行一个合理和明智的约定。在SQL-89中,你会发现人们在两个表上都写了“id”列。当你联接这些表时,这就变得模糊不清了,需要显式别名。此外,联接上的“id”几乎肯定具有不同的数据。如果你将人员与公司进行联接,现在你必须将一个“id”别名为“person_id”,将一个“id”别名为“company_id”,否则联接将产生两个不确定的列。使用全局唯一标识符作为表的代理键是标准用“USING”奖励的约定。
3. SQL-89语法是一个隐式的“CROSS JOIN”。一个“CROSS JOIN”不会减少集合,而是隐式增加它。FROM T1,T2与FROM T1 CROSS JOIN T2相同,这将产生一个笛卡尔连接,通常不是你想要的结果。将选择性减少到一个远离的“WHERE”条件意味着在设计过程中更容易犯错误。
4. SQL-89的“,”和SQL-92的显式“JOIN”优先级不同。“JOIN”的优先级更高。更糟糕的是,一些数据库(如MySQL)长时间以来都没有正确处理这个问题。因此,混合使用这两种风格是一个坏主意,而今天更受欢迎的风格是SQL-92风格。

  1. 如果你学习关系模型,你会欣赏到自然连接不仅是一个伟大的想法,而且它是你所需要的唯一类型的连接。
  2. 见1即可,不需要其他。
  3. 不管语法如何(两者都是SQL-92语法),关系运算符是乘积(乘法),也就是说并不是真正的连接(笛卡尔积甚至不是一种连接)。
  4. 见1即可,不需要其他。
- onedaywhen
需要注意的一点是,当查询中存在对列的直接控制时,自然连接仍然很有用。尤其是在连接两个子查询时。当然,在这种情况下,它只是语法糖,但它有助于提高查询中的“信噪比”。 - Ian T. Small
当你说“自然连接是一个可怕的想法”时,你得到了我的投票。是的,它们设计得很糟糕,因为它们假设每对表之间最多只有一个外键。现实世界的数据库并不遵循这个假设,我认为应该从SQL中删除自然连接,就像臭名昭著的UNION JOIN在SQL-2003中被删除一样。我敢打赌没有人再记得它们了(除非你使用HyperSQL,当然)。 - The Impaler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接