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


107

在我工作的每家公司中,我发现人们仍在按照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及其变体。每个DBMS都有自己的变体语法(通常很差;实际上,我认为,注解毫无疑问是不好的),并且语义通常略有不同。SQL-92修复了该问题,仅凭这些理由就值得使用新的表示法。我认为,一旦您习惯了,它就会变得更加清晰。这需要一点时间来适应,但是并不难,一旦转换,就没有回头路了。
乔纳森·勒夫勒

语义,语义,反语义!
山姆

我有点迟到了这里,但没有人似乎已经指出,甲骨文本身建议您使用FROM子句OUTER JOIN语法,而不是甲骨文携手运营商
bornfromanegg

我添加了一个新的答案,该答案非常最新且更直接,此处答案中的其他误解也很清楚stackoverflow.com/a/47720615/124486
Evan Carroll

Answers:


77

根据Peter Gulutzan和Trudy Pelzer撰写的“ SQL Performance Tuning”,在他们测试的六个或八个RDBMS品牌中,SQL-89与SQL-92样式连接的优化或性能没有差异。可以假设大多数RDBMS引擎在优化或执行查询之前将语法转换为内部表示形式,因此人类可读的语法没有任何区别。

我还尝试传播SQL-92语法。它获得批准已经16年了,现在是人们开始使用它的时候了!而且所有品牌的SQL数据库现在都支持它,因此没有理由继续使用非标准的(+)Oracle语法或*=Microsoft / Sybase语法。

至于为什么很难打破SQL-89开发人员社区的习惯,我只能假设有大量的“金字塔底层”的程序员使用书本,杂志文章,或其他代码库,这些人不会抽象地学习新语法。有些人模式匹配,有些人死记硬背。

不过,我逐渐发现人们使用SQL-92语法的频率比以前高。自1994年以来,我一直在线回答SQL问题。


6
我完全同意。我与许多SQL编码器一起工作,他们在15年前或更早地学习了他们的SQL(就像我自己做的那样),自从他们第一次创建以来就不了解任何创新。他们也没有兴趣寻找答案。
托尼·安德鲁斯

8
同意,但要补充一点,在有据可查的情况下,旧的ANSI-89 Join语法会产生错误的结果...特别是在联接的“外部”侧非联接相关列上有条件过滤谓词的外部联接。
查尔斯·布雷塔纳

1
我不知道MS SQL的特定内部结构,但这是SQL-92语法值得的传闻。为我工作!
Bill Karwin

2
大块的?请发布您的作品。我最好的选择是,它不是一个苹果,苹果的比较,但不回应“哦,是的,它是”与一个测试用例不仅仅是应对我们可以复制,版本,补丁级别等

2
此外,“轶事”证据无论如何都不是科学的手段。它总是与一粒盐一起服用。
Bill Karwin

16

ANSI092标准包括一些相当令人讨厌的语法。自然联接是一个,而USING子句是另一个。恕我直言,向表中添加列不应破坏代码,但NATURAL JOIN的破坏方式最为严重。打破错误的“最佳”方法是编译错误。例如,如果您在某处选择*,则添加列可以无法编译。下一个失败的最佳方法是运行时错误。更糟的是,因为您的用户可能会看到它,但是它仍然会向您发出警告,告知您已损坏某些东西。如果您使用ANSI92并使用NATURAL Joins编写查询,则它不会在编译时中断,也不会在运行时中断,查询将突然开始产生错误的结果。这些类型的错误是阴险的。报告有误,可能是财务披露不正确。

对于不熟悉NATURAL Joins的用户。它们在两个表中存在的每个列名上连接两个表。当您有4列键并且讨厌键入时,这真的很酷。当Table1有一个预先存在的列,名称为DESCRIPTION,而您在Table2中添加了一个新列,这个问题就出现了,哦,我不知道,mmm,DESCRIPTION之类的东西是无害的,现在您要在VARCHAR2上连接两个表(1000)字段为自由格式。

除了上述问题之外,USING子句还可能导致完全的歧义。在另一篇SO帖子中,有人展示了此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%的时间留给了优化人员,尤其是复制/粘贴人员。

托玛拉克说得对,

人们不会仅仅因为有新语法就改用新语法

它必须给我一些东西,但我看不到任何好处。如果存在上行空间,那么负面因素就太大了,不容忽视。


3
我倾向于使用ON,因为它比USING或NATURAL JOIN不太明确。至于括号,如果您省略它们,则在Microsoft Access上学习“ SQL”的人会将其用作Access的抱怨。(SQL周围的引号应该是手指引号。)
Powerlord

1
咳嗽什么是USING子句?;-)我来自SQL Server部分,因此这并不是我真正关心的问题。正如R. Bemrose所说,有ON子句,工作得很好,永远不会给我留下我无法用语法表达的联接。无需使我的数据库设计适应查询语法以节省一些输入。
Tomalak

3
如果您不足够了解数据,无法在适当的时候使用NATURAL JOIN或USING,则可能不应该为此编写SQL。-1为无知。
罗布

4
恕我直言,自然加入与SELECT *(然后发现客户依赖现场订单)放在同一个包中-感觉有点草率
基本的

2
用自然联接描述的问题是数据库设计的问题,而不是标准的问题。您应该了解自己的数据,并为列名建立了标准。
特拉维斯

14

我想到几个原因:

  • 人们这样做是出于习惯
  • 人们很懒惰,喜欢“旧式”联接,因为他们涉及的打字少
  • 初学者经常遇到麻烦,无法绕过SQL-92连接语法
  • 人们不会仅仅因为有新语法就改用新语法
  • 人们没有意识到新的语法(如果您要称呼它)所带来的好处,主要是它使您能够在进行外部联接之前过滤表,而在拥有WHERE子句的情况下却不能在此之后进行过滤。

就我而言,我使用SQL-92语法进行所有联接,并在可能的地方转换代码。这是一种更清洁,更易读且功能强大的方法。但是很难说服某人使用这种新样式,因为他们认为这样做会给他们带来更多打字工作,同时又不改变查询结果而对他们造成伤害。


3
对于许多人来说,完全看待SQL会对他们有害。更改任何有效代码都会带来引入错误的风险,尤其是在编码人员避开视线的情况下。:-)
Bill Karwin

嗯...对我来说,即使看复杂的正则表达式也完全没有害处。SQL不会伤害我。;-)
Tomalak

“初学者经常遇到问题...”那是一个卖点

2
由于某种原因,我不确定这是“赞成”还是“反对”的评论……也许我的讽刺探测器坏了。
Tomalak

10

为了响应上面的NATURAL JOIN和USING帖子。

为什么您会看到需要使用它们的原因-它们在ANSI-89中不可用,而是为ANSI-92添加的,因为我只能将其视为快捷方式。

我永远不会加入机会,总是会指定表/别名和ID。

对我而言,唯一可行的方法是ANSI-92。它更加冗长,并且ANSI-89的追随者不喜欢该语法,但是它巧妙地将JOINS与FILTERING分开。


我没有将NATURAL JOIN视为捷径,而是将关系数据库中的面向对象DB编程的Segway方式。
Armand 2014年

5

首先,我要说的是,在SQL Server中,外部联接语法(* =)始终无法给出正确的结果。有时,它将其解释为交叉联接而不是外部联接。因此,有充分的理由停止使用它。而且,外部联接语法是不推荐使用的功能,在SQL Server 2008之后的下一版本的SQL Server中将不可用。您仍然可以进行内部联接,但是到底为什么有人要这样做?它们还不清楚,并且很难维护。您不容易知道什么是连接的一部分,而实际上只是where子句。

我相信您不应该使用旧语法的原因之一是,理解联接以及联接的作用和不作用是对将要编写SQL代码的任何人的关键一步。在不完全了解联接的情况下,您不应编写任何SQL代码。如果您对它们的理解很好,您可能会得出结论,即ANSI-92语法更清晰,更易于维护。我从未见过一位SQL专家,他没有使用ANSI-92语法优先于旧语法。

我遇到或与之打交道的大多数人都使用旧代码,确实不了解联接,因此在查询数据库时遇到麻烦。这是我的个人经历,所以我并不是说这总是对的。但是作为一名数据专家,多年来,我不得不修复过多的垃圾,以至于不敢相信。


1
很高兴认识你。高兴成为您的第一个。

4

我在学校学习ANSI-89,并在行业工作了几年。然后,我离开了神话般的DBMS世界8年了。但是后来我回来了,正在教授这种新的ANSI 92知识。我已经学习了Join On语法,现在我实际上在教SQL,我推荐新的JOIN ON语法。

但是从ANSI 92连接的角度来看,我看到的相关子查询的缺点似乎没有意义。当WHERE中包含联接信息并且WHERE中“联接”了相关的子查询时,一切似乎都是正确且一致的。在ANSI 92表联接条件不在WHERE和子查询“联接”中,语法似乎不一致。另一方面,尝试“修复”这种不一致可能会使情况变得更糟。


另一方面,您可能根本不编写任何相关子查询,因为它们是性能猪。它们就像在您的查询中添加了一个游标。啊。
HLGEM 2014年

3

我不确定答案如何。.这是一场宗教战争(比Mac-Pc或其他同类武器度低)

猜测是直到最近,Oracle(以及其他供应商)也未采用ANSI-92标准(我认为它是在Oracle v9中或大约在Oracle v9中),因此,对于在这样的公司工作的DBA / Db开发人员而言仍在使用这些版本,(或希望将代码移植到可能正在使用这些版本的服务器之间,因此它们必须遵循旧标准...

确实很可惜,因为新的联接语法更具可读性,而旧的语法会在一些有据可查的情况下产生错误(错误)的结果。

  • 具体来说,当联接的“外部”侧的表中与联接无关的列上存在条件过滤谓词时,外部联接。

是的,Oracle 9i;2001年发行。参加聚会有点晚了!

1
MS SQL INNER JOIN于1995年添加,但LEFT JOIN直到1996 年才添加。MySQL至少从1999年发布的3.23版本开始支持它。PostgreSQL至少从7.2开始,于2002年发布。一些偶然的谷歌搜索使我对Teradata没有任何答案。

是的,这就是谴责,在1991年证明了Oracle的优越性:使用“ Left”和“ Right”语法的MS Access之类的数据库系统不是ANSI标准SQL。这样发布SQL是其他人嘲笑您的机会。现在有点像Twitter和Facebook。
大卫

2

惯性和实用性。

ANSI-92 SQL就像触摸输入。从某种理论上讲,有一天它可能会使一切变得更好,但是现在我可以用四根手指打字快得多。我需要倒退才能前进,但不能保证会有回报。

编写SQL大约占我工作的10%。如果我需要ANSI-92 SQL来解决ANSI-89 SQL无法解决的问题,那么我将使用它。(实际上,我在Access中使用了它。)如果一直使用它可以帮助我更快地解决现有问题,那么我会花时间来吸收它。但是我无需考虑语法就可以编写出ANSI-89 SQL。我得到解决问题的报酬-思考SQL语法浪费了我的时间和雇主的钱。

有朝一日,年轻的蚱hopper,您将捍卫您对ANSI-92 SQL语法的使用,以防止年轻人抱怨您应该使用SQL3(或其他方式)。然后您将了解。:-)


2
这里描述的方法听起来像“打破时修复它”的思想流派,避开了预防性维护的想法。您可以得到解决问题的报酬,是的,但是您也得到报酬为公司提供更多价值。在您的情况下,ANSI-89在短期内可提供更大的价值,但从长远来看,不花时间在ANSI-92上将是更昂贵的选择。
特拉维斯

坚持做自己总是要付出的代价,可怜的家伙必须在离开后维护您的代码。这并不意味着您应该切换到本月的风格,但是采用最佳实践几乎总是会带来可维护性的回报。

我不会切换到Excel(在这里您只需单击三下鼠标就可以执行任何操作),因为我已经记住了我需要的成千上万个Lotus 123命令,并且我很乐意使用它们!哈哈!
查尔斯·布雷塔纳'19

2

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

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秒。

有充分的理由进行切换。

这里转贴。


1

我可以从普通开发人员的角度回答,知道足够的SQL来理解这两种语法,但每次需要时仍会搜索插入的确切语法... :-P(我整天不使用SQL ,只是不时解决一些问题。)

好吧,实际上,我发现第一种形式更直观,在两个表之间没有明显的层次结构。我从可能的旧书中学到了SQL的事实,显示了第一种形式,可能无济于事... ;-)
而我在sql select上找到的第一个参考在Google搜索中(它为我返回了大部分法语答案...) )首先显示较旧的表格(然后说明第二个表格)。

只是对“为什么”问题给出一些提示... ^ _ ^我应该读一本关于该主题的现代书籍(与DB无关)。如果有人有建议...


1

我不能代表所有学校,但是在我的大学里,当我们在学习课程的SQL模块时,他们没有教ANSI-92,而是教ANSI-89,当时是在旧的VAX系统上!直到开始使用Access设计器构建了一些查询,然后深入研究SQL代码之前,我才开始接触Access-ANSI-92。意识到我不知道它是如何完成联接的,也不知道我开始更深入地研究语法的含义,以便我能理解它。

鉴于在许多情况下可用的文档不是很直观,并且人们倾向于坚持自己所知道的,并且在许多情况下,他们不努力学习超出其完成工作所需要的知识,因此不难看出为什么采用它需要这么长时间。

当然,有些技术传福音者喜欢修补和理解,并且往往是那些采用“更新”原理并尝试转换其余原理的类型。

奇怪的是,在我看来,很多程序员都辍学了,不再前进。认为这是他们所教的,所以就是这样做的。直到您脱下眨眼间,您才意识到学校只是在教您基础知识,并给您足够的知识来自己学习其余的知识,而实际上您只是勉强摸索了所学知识。现在,继续这条路是您的工作。

当然,那只是我根据我的经验得出的意见。


不只是程序员。在许多领域,很难说服人们在职业生涯中建立后再接受培训。当然,我认为在每个领域中都有杰出的个人以相同的比例。
Bill Karwin

2
很难说服某人从成功的事物转换为没有益处的事物。我们房屋的.net一侧已从1.0更改为3.5,并且介于零之间的每一步都通过cajoling。每个新版本都更好。这里不能说同样的话。

1

1)编写OUTER JOIN的标准方法,而* =或(+)=

2)自然加入

3)依赖于数据库引擎,ANSI-92趋于最佳。

4)手动优化:

假设我们有下一个语法(ANSI-89):

(1)select * from TABLE_OFFICES to,BIG_TABLE_USERS btu
where to.iduser=tbu.iduser and to.idoffice=1

它可以写成:

(2)select * from TABLE_OFFICES to
inner join BIG_TABLE_USERS btu on to.iduser=tbu.iduser
where to.idoffice=1

还可以作为:

(3)select * from TABLE_OFFICES to
inner join BIG_TABLE_USERS btu on to.iduser=tbu.iduser and to.idoffice=1

它们(1),(2),(3)都返回相同的结果,但是它们的优化方式不同,这取决于数据库引擎,但是大多数都这样做:

  • (1)其取决于数据库引擎决定的优化。
  • (2)它同时连接两个表,然后在每个办公室进行过滤。
  • (3)它使用idoffice过滤BIG_TABLE_USERS,然后联接两个表。

5)更长的查询不会那么混乱。


1

根据我在老少少少的程序员和学员以​​及应届毕业生中的实践经验,人们使用ANSI-89的原因:

  • 他们从看到的现有代码(而不是书籍)中学习SQL,并从代码中学习ANSI-89
  • ANSI-89,因为键入较少
  • 他们不考虑它,而是使用一种或另一种样式,甚至都不知道这两种样式是新旧样式,也不在乎
  • 不存在代码也是与下一个程序员保持沟通的一种沟通的想法。他们认为他们与计算机交谈,而计算机不在乎。
  • “干净的编码”的艺术是未知的
  • 特别是编程语言和SQL的知识如此贫乏,以至于他们将其他地方找到的内容复制并粘贴在一起
  • 个人喜好

我个人更喜欢ANSI-92,有时我更改以ANSI-89语法显示的每个查询只是为了更好地理解手头的SQL语句。但是我意识到与我一起工作的大多数人不够熟练,无法在许多表上编写联接。他们尽可能地编写代码,并使用第一次遇到SQL语句时所记住的内容。


1

这里有几点比较SQL-89和SQL-92并清除了其他答案中的一些误解。

  1. NATURAL JOINS是一个可怕的想法。它们是隐式的,并且需要有关表的元信息。SQL-92不需要使用它们,因此只需忽略它们即可。它们与该讨论无关。
  2. USING 是个好主意,它有两个作用:
    1. 它仅通过等值连接在结果集上产生一列。
    2. 它强制执行健全和合理的约定。在SQL-89中,您需要有人id在两个表上编写该列。加入表后,这变得模棱两可,并且需要显式别名。此外,id联接上的s几乎可以肯定具有不同的数据。如果你加入的人的公司,你现在必须别名一个idperson_id,和一个idcompany_id,没有这些连接将产生两个暧昧列。表格的替代键使用全局唯一标识符是标准奖励的惯例USING
  3. SQL-89语法是隐式的CROSS JOIN。A 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

0

Oracle完全没有实现ANSI-92。我遇到了几个问题,尤其是因为Oracle Apps中的数据表非常具有列。如果联接中的列数超过约1050列(在Apps中非常容易做到),那么您将收到此虚假错误,这绝对没有逻辑意义:

ORA-01445: cannot select ROWID from a join view without a key-preserved table.

重新编写查询以使用旧样式的联接语法会使问题消失,这似乎直接将责任归咎于ANSI-92联接的实现。

在遇到这个问题之前,我一直是ASNI-92的坚定推动者,因为它具有减少意外交叉联接的机会的好处,而这对于旧式语法而言太容易了。

但是,现在我发现要坚持下去要困难得多。他们指出Oracle的实施不正确,并说:“谢谢,我们会尽力而为。”


1
进行交叉联接可能很容易,这也是不会自发发生的,并且肯定不是模棱两可的。任何体面的SQL开发人员都可以发现它。但是“使用”和“自然加入”是妖精,它们呼唤您,将您的小船砸在痛苦和痛苦的岩石上。

1
我的观点是,通过省略将两个表连接在一起的where子句,可以轻松地成功创建意外的交叉联接。交叉联接必须在ANSI-92中仔细考虑。但是我确实同意NATURAL JOIN是可憎的。:)
乔纳森

我明白你的意思。但是,这并非偶然。在调试查询时,您会注意到问题并予以解决。如果您使用自然联接,而对查询本身完全没有任何更改,则由于表更改,它可能会更改。

0

新的SQL标准继承了先前标准的所有内容,也就是“兼容性的束缚”。因此,“旧” /“逗号分隔” /“不合格”联接样式是完全有效的SQL-92语法。

现在,我认为SQL-92 NATURAL JOIN是您唯一需要的联接。例如,我认为它的优势inner join在于它不会生成重复的列- SELECT子句中不再有范围变量来消除列的歧义!但是我不能指望每一个想法都会改变,因此我需要与编码人员合作,他们将继续采用我个人认为的传统联接样式(它们甚至可能将范围变量称为“别名”!)。这是团队合作的本质,并非凭空运作。

对SQL语言的批评之一是,可以使用多种语义等效的语法(某些使用关系代数,某些使用关系演算)获得相同的结果,其中选择“最佳”仅是个人风格。因此,我对使用“老式”连接感到很满意INNER。我是否愿意花时间重写它们NATURAL取决于上下文。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.