在我工作过的每一家公司,我发现人们仍在使用 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 - 不希望人们尝试优化他们的代码,对吗?
根据 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 问题。
ANSI092 标准包含一些非常令人发指的语法。 Natural Joins 是一个,而 USING 子句是另一个。恕我直言,向表中添加列不应破坏代码,但 NATURAL JOIN 会以最令人震惊的方式破坏。破解的“最佳”方法是编译错误。例如,如果您在某处选择 *,则添加列 可能 无法编译。失败的下一个最佳方法是运行时错误。更糟糕的是,您的用户可能会看到它,但它仍然会给您一个很好的警告,表明您已经破坏了某些东西。如果您使用 ANSI92 并使用 NATURAL 连接编写查询,它不会在编译时中断,也不会在运行时中断,查询将突然开始产生错误的结果。这些类型的错误是阴险的。报告出错,可能财务披露不正确。
对于那些不熟悉自然连接的人。他们在两个表中存在的每个列名上连接两个表。当您有一个 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
这是完全模棱两可的。我在 Companies 和 user 表中都放置了一个 UserID 列,没有任何投诉。如果公司中的 UserID 列是最后一个修改该行的人的 ID 怎么办?
我是认真的,谁能解释为什么这种模棱两可是必要的?为什么它直接内置到标准中?
我认为比尔是正确的,有大量的开发人员通过编码复制/粘贴到那里。事实上,我可以承认在 ANSI-92 方面我是其中的一员。我见过的每个示例都显示多个连接嵌套在括号中。老实说,这使得在 sql 中挑选表充其量是困难的。但随后一位 SQL92 布道者解释说,这实际上会强制执行连接顺序。耶稣……我见过的所有复制粘贴器现在实际上都在强制加入订单 - 95% 的工作最好留给优化人员,尤其是复制/粘贴。
托马拉克说得对,他说,
人们不会仅仅因为它在那里就切换到新语法
它必须给我一些东西,我看不出有什么好处。如果有上行空间,那么负面因素就是一只大到不容忽视的信天翁。
SELECT *
属于同一个包(然后发现客户端依赖于字段顺序)- 只是感觉有点草率
想到几个原因:
人们这样做是出于习惯
人们很懒,更喜欢“旧式”连接,因为它们涉及的打字更少
初学者经常会遇到关于 SQL-92 连接语法的问题
人们不会仅仅因为它在那里就切换到新语法
人们没有意识到新的(如果你想这样称呼它)语法的好处,主要是它使您能够在执行外部联接之前过滤表,而不是在您只有 WHERE 子句时过滤表之后。
就我而言,我使用 SQL-92 语法进行所有连接,并尽可能转换代码。这是一种更清洁、更易读、更强大的方法。但是很难说服某人使用新样式,因为他们认为在不改变查询结果的情况下增加打字工作量会伤害他们。
回应上面的 NATURAL JOIN 和 USING 帖子。
为什么你会看到需要使用这些 - 它们在 ANSI-89 中不可用,并且是为 ANSI-92 添加的,因为我只能将其视为快捷方式。
我永远不会把连接留给机会,并且总是会指定表/别名和 ID。
对我来说,唯一可行的方法是 ANSI-92。它更冗长,并且语法不被 ANSI-89 追随者喜欢,但它巧妙地将您的 JOINS 与您的 FILTERING 分开。
首先让我说,在 SQL Server 中,外连接语法 (*=) 并不总是给出正确的结果。有时它会将其解释为交叉连接而不是外部连接。因此,有充分的理由停止使用它。并且该外连接语法是一个已弃用的功能,并且不会出现在 SQL Server 2008 之后的下一版本 SQL Server 中。您仍然可以进行内连接,但究竟为什么会有人想要这样做呢?它们不清楚并且更难维护。您不容易知道什么是联接的一部分,什么是真正的 where 子句。
我认为您不应该使用旧语法的一个原因是,了解连接以及它们做什么和不做什么对于任何将编写 SQL 代码的人来说都是一个关键步骤。如果没有彻底了解连接,则不应编写任何 SQL 代码。如果您很好地理解它们,您可能会得出这样的结论:ANSI-92 语法更清晰且更易于维护。我从来没有遇到过不使用 ANSI-92 语法而不是旧语法的 SQL 专家。
我遇到或打过交道的大多数人都使用旧代码,真的不了解连接,因此在查询数据库时会遇到麻烦。这是我的个人经历,所以我并不是说它总是正确的。但作为一名数据专家,多年来我不得不修复太多这种垃圾,以至于我不相信它。
我在学校学习过 ANSI-89,并在工业界工作了几年。然后我离开了 DBMS 的神话般的世界 8 年。但后来我回来了,这个新的 ANSI 92 东西正在被教授。我已经学习了 Join On 语法,现在我实际教 SQL,我推荐新的 JOIN ON 语法。
但我看到的缺点是相关子查询在 ANSI 92 连接的情况下似乎没有意义。当连接信息包含在 WHERE 中并且相关的子查询在 WHERE 中“连接”时,一切似乎都是正确且一致的。在 ANSI 92 中,表的连接条件不在 WHERE 中,而子查询中的“连接”是,语法似乎不一致。另一方面,试图“修复”这种不一致可能只会让情况变得更糟。
我不确定答案.. 这是一场宗教战争(虽然程度低于 Mac-Pc 或其他人)
一个猜测是,直到最近,Oracle(可能还有其他供应商)还没有采用 ANSI-92 标准(我认为它是在 Oracle v9 或更高版本中),因此,对于在公司工作的 DBA/Db 开发人员来说仍在使用这些版本,(或者希望代码可以跨可能使用这些版本的服务器移植,他们必须坚持旧标准......
真的很遗憾,因为新的连接语法更具可读性,而旧语法会在几个有据可查的场景中产生错误(不正确)的结果。
具体来说,当在联接的“外部”一侧的表中的非联接相关列上存在条件过滤谓词时,外部联接。
INNER JOIN
,但直到 1996 年才添加 LEFT JOIN
。MySQL 至少从 1999 年发布的 3.23 开始支持它; PostgreSQL 至少从 2002 年发布的 7.2 开始。一些随意的谷歌搜索没有给我关于 Teradata 的答案。
我有一个最初为 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 秒。
有充分的理由转换。
转自 here。
惯性和实用性。
ANSI-92 SQL 类似于触摸打字。从某种理论上讲,它总有一天可能会让一切变得更好,但我现在可以用四根手指更快地打字。我需要倒退才能前进,但不能保证永远会有回报。
编写 SQL 大约占我工作的 10%。如果我需要 ANSI-92 SQL 来解决 ANSI-89 SQL 无法解决的问题,那么我会使用它。 (事实上,我在 Access 中使用它。)如果一直使用它可以帮助我更快地解决现有问题,我会花时间吸收它。但是我可以在不考虑语法的情况下抽出 ANSI-89 SQL。我得到报酬是为了解决问题——思考 SQL 语法是浪费我的时间和雇主的钱。
总有一天,年轻的蚱蜢,你会为你使用 ANSI-92 SQL 语法辩护,反对年轻人抱怨你应该使用 SQL3(或其他)。然后你就会明白了。 :-)
以下是比较 SQL-89 和 SQL-92 的几点,并澄清了其他答案中的一些误解。
自然连接是一个可怕的想法。它们是隐含的,它们需要有关表的元信息。 SQL-92 不需要使用它们,所以只需忽略它们。它们与本次讨论无关。 USING 是一个好主意,它有两个效果: 它只在 equijoin 的结果集上生成一列。它强制执行一个健全和理智的约定。在 SQL-89 中,有人在两个表上都写了列 id。加入表后,这变得模棱两可,并且需要显式别名。此外,连接上的 id 几乎可以肯定有不同的数据。如果您将人员连接到公司,您现在必须将一个 id 别名为 person_id,并将一个 id 别名为 company_id,否则连接将产生两个不明确的列。使用表的代理键的全局唯一标识符是使用 USING 的标准奖励的约定。 SQL-89 语法是一种隐式的 CROSS JOIN。 CROSS JOIN 不会减少集合,它会隐式增长它。 FROM T1,T2 与 FROM T1 CROSS JOIN T2 相同,它产生笛卡尔连接,这通常不是您想要的。有选择性地将删除的内容减少到遥远的 WHERE 条件意味着您更有可能在设计过程中犯错误。 SQL-89 和 SQL-92 显式 JOIN 具有不同的优先级。 JOIN 具有更高的优先级。更糟糕的是,像 MySQL 这样的一些数据库在很长一段时间内都犯了这个错误。所以混合这两种风格是一个坏主意,而今天更流行的风格是 SQL-92 风格。
UNION JOIN
一样。我敢打赌,再也没有人记得那些了(当然,除非你使用 HyperSQL)。
我可以从普通开发人员的角度来回答,知道足够的 SQL 来理解这两种语法,但每次我需要它时仍然使用谷歌搜索插入的确切语法...... :-P(我不整天做 SQL ,只是不时修复一些问题。)
嗯,实际上,我发现第一种形式更直观,两个表之间没有明显的层次结构。我用可能的旧书学习 SQL 的事实,显示第一种形式,可能无济于事...... ;-) 我在谷歌的 sql 选择搜索中找到的第一个参考资料(它为我返回主要是法语答案...... ) 首先显示旧形式(然后解释第二个形式)。
只是对“为什么”问题给出一些提示...... ^_^ 我应该阅读一本关于该主题的优秀现代书籍(与数据库无关)。如果有人有建议...
我不能代表所有学校,但在我的大学里,当我们在做我们课程的 SQL 模块时,他们没有教 ANSI-92,他们教的是 ANSI-89 - 那时在一个旧的 VAX 系统上!直到我开始在 Access 中挖掘,使用查询设计器构建了一些查询,然后深入研究 SQL 代码,我才接触过 ANSI-92。意识到我不知道它是如何完成连接的,也不知道我开始深入挖掘语法的含义,以便理解它。
鉴于在很多情况下可用的文档并不完全直观,并且人们倾向于坚持他们所知道的,并且在许多情况下并不会努力学习超过他们完成工作所需的任何东西,它是很容易看出为什么采用需要这么长时间。
当然,有些技术布道者喜欢修补和理解,而且往往是那些采用“更新”原则并试图转换其余部分的类型。
奇怪的是,在我看来,很多程序员从学校毕业就停止了进步;认为因为这是他们被教导的,所以这就是它的完成方式。直到你摘下眼罩,你才意识到学校只是为了教你基础知识,让你有足够的理解来自己学习其余的东西,而实际上你几乎没有触及到要知道的东西的表面;现在你的工作就是继续这条路。
当然,这只是我根据我的经验得出的看法。
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)更长的查询不那么混乱。
从我与老、年轻程序员、实习生和应届毕业生的实践经验来看,人们使用 ANSI-89 的原因:
他们从他们看到的现有代码(而不是书本)中学习 SQL,并从代码中学习 ANSI-89
ANSI-89,因为打字少
他们不考虑它并使用一种或另一种风格,甚至不知道两者中的哪一个被认为是新的或旧的,也不关心
代码也是与维护代码的下一个程序员的通信的想法不存在。他们认为他们与计算机交谈,而计算机不在乎。
“干净编码”的艺术是未知的
特别是编程语言和 SQL 知识太差,以至于他们将在其他地方找到的内容复制并粘贴在一起
个人喜好
我个人更喜欢 ANSI-92 并更改我在 ANSI-89 语法中看到的每个查询,有时只是为了更好地理解手头的 SQL 语句。但我意识到,与我一起工作的大多数人都没有足够的技能来为许多表编写连接。他们尽可能地编写代码,并在第一次遇到 SQL 语句时使用他们记住的内容。
Oracle 根本没有很好地实现 ANSI-92。我遇到了几个问题,尤其是因为 Oracle Apps 中的数据表具有非常好的列。如果您的联接中的列数超过大约 1050 列(这在应用程序中很容易做到),那么您将得到这个完全没有逻辑意义的虚假错误:
ORA-01445: cannot select ROWID from a join view without a key-preserved table.
重新编写查询以使用旧式连接语法使问题消失,这似乎将责任直接归咎于 ANSI-92 连接的实现。
在我遇到这个问题之前,我一直是 ASNI-92 的坚定推动者,因为它可以减少意外交叉连接的机会,这对于旧式语法来说太容易了。
然而,现在我发现坚持下去要困难得多。他们指出甲骨文的糟糕实施并说“我们会按照自己的方式做,谢谢。”
新的 SQL 标准继承了以前标准的所有内容,也就是“兼容性的枷锁”。所以“旧”/“逗号分隔”/“不合格”连接样式是完全有效的 SQL-92 语法。
现在,我认为 SQL-92 的 NATURAL JOIN
是您需要的唯一连接。例如,我认为它优于 inner join
,因为它不会生成重复的列 - SELECT
子句中没有更多的范围变量来消除列的歧义!但我不能指望改变每一个想法,所以我需要与将继续采用我个人认为是遗留连接样式的编码人员合作(他们甚至可能将范围变量称为“别名”!)。这是团队合作的本质,而不是在真空中运作。
对 SQL 语言的批评之一是可以使用许多语义等效的语法(一些使用关系代数,一些使用关系演算)获得相同的结果,其中选择“最佳”的只是个人风格.因此,我对“旧式”联接和对 INNER
一样满意。我是否会花时间将它们重写为 NATURAL
取决于上下文。