8

我们正在开发 ETL 作业,我们的顾问在加入表时一直在使用“旧式”SQL

select a.attr1, b.attr1
from table1 a, table2 b
where a.attr2 = b.attr2

而不是使用内部连接子句

select a.attr1, b.attr1
from table1 as a inner join table2 as b
   on a.attr2 = b.attr2

我的问题是,从长远来看,使用旧的“where join”是否存在风险?这种连接作为 ANSI 标准受支持和保持多长时间?我们的平台是 SQL Server,我的主要原因是将来不再支持这些“连接”。发生这种情况时,我们必须使用“内连接”样式的连接来修改所有 ETL 作业。

4

9 回答 9

8

与其担心未来可能出现的一些风险,何不担心自己现在面临的风险呢?

除了马克的观点:

  • 当 ON 子句与连接表断开连接(有时是多行)时,代码更难阅读(因此难以理解其目的)。这增加了修改代码时出错的可能性。
  • 确定正在执行哪种 JOIN 更难- 您必须浏览 WHERE 子句并希望您看到的是正确的。
  • 查找丢失的 JOIN 子句要困难得多,增加了无意的笛卡尔连接的风险 - 当您使用 ANSI 语法时,ON 子句很好地排列,这使得这变得微不足道。
于 2010-09-10T11:40:11.160 回答
7

我怀疑“在哪里加入”将永远不受支持。不支持它们是不可能的,因为它们基于笛卡尔积和简单的过滤。它们实际上不是连接。

但是有很多理由使用较新的连接语法。其中:

  • 可读性
  • 可维护性
  • 更轻松地更改外部连接
于 2010-09-10T11:40:40.150 回答
4

避免隐式连接的原因有很多。大的是:

  • 它不能轻易地更改为外部连接。
  • 使用隐式连接更容易忘记连接条件。
  • 如果同时混合隐式和显式连接,则会遇到混淆优先级的问题。这是几个小时前的一个例子:MySQL 语法错误

我认为它们不会很快被删除,但还有很多其他原因可以停止使用它们。

于 2010-09-10T11:36:23.320 回答
3

最新版本的 ISO SQL (2003,2008) 支持这两种语法。在 FROM 子句中使用逗号指定交叉连接是完全标准的 SQL,我遇到的所有 SQL 产品都支持它。它似乎极不可能在 SQL 中被弃用或取消支持。

于 2010-09-10T11:43:25.613 回答
2

只要他们不使用 ***= 和 =* 作为连接语法(自SQL Server 2008 R2 起已弃用),从长远来看,我看不到它会消失,但正如 Mark Byers 所说,有很多理由不这样做。

我最大的担心是,如果他们正在编写这样的连接,他们还在做什么非传统的?

于 2010-09-10T11:39:29.087 回答
1

很难争论某种语法结构的优雅或丑陋。你只是看到或不看到。逗号分隔的连接语法反映了关系代数的基本特征,它断言选择-项目-连接查询的标准形式。唯一能逃脱它的连接(因此需要专用的语法)是外连接。缺少相等谓词导致连接图不相交的意外错误只是前端 SQL 编程工具的复杂程度(它是否显示连接图?)。

这不仅仅是美学。生产数据库通常具有跨多个表的 CREATED_ON 或 COMMENTS 等列。在这种情况下,NATURAL JOIN 语法是很危险的。

正如 Anthony Molinaro(流行的“SQL Cookbook”的作者)雄辩地指出的那样:“<em>旧式风格短小精悍,完美无缺。ANSI 把它简化了,对于已经开发了一段时间的人来说,这是完全没有必要的”。

于 2010-09-10T22:42:01.220 回答
1

人们有一些好的观点,但到目前为止还有两个没有被提及的重要观点:

  1. 无论旧式 *= 和 =* 外连接是否给出了正确的结果,它们也不能正确地表示某些连接。考虑以下查询。我们想向所有未下单超过 100 美元的客户展示:

    SELECT
    FROM
       Customer C
       LEFT JOIN Order O ON C.OrderID = O.OrderID AND O.OrderTotal >= 100.0;
    WHERE
       O.OrderID IS NULL;
    

    试着用旧的方式表达……如果可以的话。不使用派生表。

  2. 对我来说,使用正确的连接子句的巨大价值是将标准连接条件(几乎适用于涉及这两个表的每个查询中)与查询的特殊过滤器分开,该过滤器将返回所需的行:

    SELECT
       C.FullName,
       C.CustomerCode,
       O.OrderDate,
       O.OrderTotal,
       OD.ExtendedShippingNotes
    FROM
       Customer C
       INNER JOIN Order O ON C.CustomerID = O.CustomerID
       INNER JOIN OrderDetail OD ON O.OrderID = OD.OrderID
    WHERE
       C.CustomerStatus = 'Preferred'
       AND O.OrderTotal > 1000.0;
    

    这种分离意味着查看查询的开发人员在扫描该查询的独特之处时不必处理一堆混乱。如果他熟悉这些表,他可以完全跳过 FROM 子句,只需阅读 WHERE 子句即可获得他需要的所有信息。它更快。如果你不关心更快,即使只是用你的眼球扫描查询,我也不想和你一起工作。

    现在,对于那些认为使用 JOIN 语法时所有内容的位置有什么特别之处的人来说,你错了。以下查询与上面的查询一样好:

    SELECT
       C.FullName,
       C.CustomerCode,
       O.OrderDate,
       O.OrderTotal,
       OD.ExtendedShippingNotes
    FROM
       Customer C
       CROSS JOIN Order O
       INNER JOIN OrderDetail OD
          ON C.CustomerID = O.CustomerID
          AND C.CustomerStatus = 'Preferred'
          AND O.OrderTotal > 1000.0
    WHERE
       O.OrderID = OD.OrderID;
    

    该查询甚至可能具有完全相同的执行计划。惊讶吗?不要这样。就像旧式语法一样,优化器负责根据您提供的条件确定如何连接表。只要条件不引用尚未提及的表,条件在哪里并不重要。

那么这两种风格最大的区别是什么?如果您认为上面的第二个混合查询很难理解并且会是一种疯狂的编写方式,那么您自然会认为旧式查询是蹩脚的。因为,坦率地说,将所有条件随意放在任何旧地方都是杂乱无章的。JOIN 的组织系统很有意义。如果你习惯了旧风格而不喜欢新风格,那可能是因为改变是不愉快的(对我们所有人来说)。但是一旦你使用它一段时间,我相信它会在你身上生长。至少,如果不是,我可能无法理解为什么。

于 2010-09-11T05:06:10.593 回答
0

右连接和左连接隐含的语法 *= 和 =* 已被弃用,并且有充分的理由,它目前不会始终返回正确的结果。如果他们使用了此功能,则必须立即修复这些问题,因为它们目前会使您面临不正确结果的风险。这不是一个选择。其他语法将继续有效,但出于多种原因也应替换。首先,很容易得到意外的交叉连接,这可能会返回错误的结果,或者通过使用 distinct 来修复,这会产生性能问题。

另一个问题是维护。如果人们稍后将其他联接添加到查询中并再次开始混合隐式和显式联接,您可能会得到错误的结果,甚至不知道它。在你的代码库中留下这种糟糕的代码是非常非常糟糕的。隐式连接也更难理解,因为它们通常是由不理解连接的开发人员编写的,所以它们可能不是你需要的。如果查询中有交叉连接,维护者如何知道它是错误(和意外交叉连接)还是故意交叉连接(我们偶尔确实需要它们)。我不会接受这个代码。我会坚持让编写它的无能者免费修复它。

于 2010-09-10T13:17:36.667 回答
-2

如果您担心它们会从标准或 SQL 产品中删除,请不要担心。这不太可能发生。

这种“旧式”的连接就是这样:只是一个风格问题。支持它的人发誓,在某些情况下“旧式”连接更容易理解。虽然我自己并不完全相信,但有一件事是肯定的:旧式连接会不时遇到,重新设计代码以适应您自己的个人风格并不总是合适的。因此,要习惯这种风格并学会使用它。

于 2010-09-10T13:11:12.720 回答