ANSI JOIN与非ANSI JOIN查询的执行方式是否不同?

时间:2009-10-21 06:28:47

标签: sql-server tsql join sql

我在~7000行T-SQL存储过程中有我的业务逻辑,其中大多数都有下一个JOIN语法:

SELECT A.A, B.B, C.C
FROM aaa AS A, bbb AS B, ccc AS C
WHERE
    A.B = B.ID
AND B.C = C.ID
AND C.ID = @param

如果我用这个替换这样的查询,我会获得性能增长:

SELECT A.A, B.B, C.C
FROM aaa AS A
JOIN bbb AS B
   ON A.B = B.ID
JOIN ccc AS C
   ON B.C = C.ID
   AND C.ID = @param

或者他们是一样的?

7 个答案:

答案 0 :(得分:74)

两个查询是相同的,除了第二个是ANSI-92 SQL语法,第一个是没有包含join子句的旧SQL语法。它们应该生成完全相同的内部查询计划,尽管您可能需要检查。

出于几个原因,您应该使用ANSI-92语法

  • 使用JOIN子句分隔 来自的关系逻辑 过滤逻辑(WHERE)因此 更清洁,更容易理解。
  • 与此特定查询无关,但在某些情况下,较旧的外部联接语法(使用+)不明确且查询结果因此依赖于实现 - 或者查询根本无法解析。 ANSI-92
  • 不会出现这些情况
  • 这是一个很好的做法,因为大多数开发人员和dba现在都会使用ANSI-92,你应该遵循标准。当然,所有现代查询工具都将生成ANSI-92。
  • 正如@gbn所指出的,它确实倾向于避免偶然的交叉连接。

我自己在一段时间内抵制ANSI-92,因为旧语法略有概念上的优势,因为它更容易设想SQL作为所有表的大规模笛卡尔连接,然后是过滤操作 - 一种心理技巧可以用于掌握SQL查询正在执行的操作。然而,几年前我决定我需要与时俱进,经过相对较短的调整期后,我现在非常喜欢它 - 主要是因为上面提到的第一个原因。唯一一个应该偏离ANSI-92语法的地方,或者更确切地说不使用该选项的地方是自然连接,这是隐含的危险。

答案 1 :(得分:4)

第二个结构在SQL社区中称为“无限连接语法”。第一个构造AFAIK没有广泛接受的名称,所以我们称之为'旧式'内连接语法。

通常的论点是这样的:

'传统'语法的优点: 谓词在WHERE子句中被物理地组合在一起 无论什么顺序使得查询一般,特别是n-ary关系,更容易阅读和理解(不完整语法的ON子句可以展开谓词,所以你必须寻找一个表或列的外观在视距上。)

'传统'语法的缺点:省略其中一个'join'谓词时没有解析错误,结果是笛卡尔积(在混合语法中称为CROSS JOIN)并且出现这样的错误检测和调试可能很棘手。此外,'join'谓词和'过滤'谓词在WHERE子句中被物理地组合在一起,这可能导致它们彼此混淆。

答案 2 :(得分:3)

执行两者并检查他们的查询计划。他们相等。

答案 3 :(得分:3)

两个查询 相等 - 第一个使用非ANSI JOIN语法,第二个是ANSI JOIN语法。我建议坚持使用ANSI JOIN语法。

是的,当您加入的表可能不包含任何匹配记录时,LEFT OUTER JOIN(其中,btw也是ANSI JOIN语法)是您想要使用的。

参考:Conditional Joins in SQL Server

答案 4 :(得分:3)

好的,他们执行相同的操作。这是同意的。 与许多人不同,我使用较旧的惯例。 SQL-92“更容易理解”是值得商榷的。编写了40年的编程语言(gulp)我知道“易于阅读”首先在任何其他约定之前开始,具有“视敏度”(这里误用术语,但这是我可以使用的最好的词组)。 在阅读SQL时,您首先关心的是涉及哪些表,然后是哪个表(大多数)定义了粒度。然后,您关心数据的相关约束,然后选择所选的属性。虽然SQL-92主要将这些想法分开,但是有很多噪音词,思维的眼睛必须解释和处理它们,这使得读取SQL变慢。

SELECT Mgt.attrib_a   AS attrib_a
      ,Sta.attrib_b   AS attrib_b
      ,Stb.attrib_c   AS attrib_c
FROM   Main_Grain_Table  Mgt
      ,Surrounding_TabA  Sta
      ,Surrounding_tabB  Stb
WHERE  Mgt.sta_join_col  = Sta.sta_join_col
AND    Mgt.stb_join_col  = Stb.stb_join_col
AND    Mgt.bus_logic_col = 'TIGHT'

视觉敏锐度! 将逗号添加到前面的新属性它也使得注释代码更容易 对函数和关键字使用特定的大小写 对表使用特定的大小写 使用特定的属性案例 垂直排列运算符和运算 使FROM中的第一个表代表数据的粒度 使WHERE的第一个表成为连接约束,并让特定的紧密约束浮动到底部。 为数据库中的所有表选择3个字符别名,并使用别名EVEREWHERE引用该表。您应该使用该别名作为该表上(许多)索引的前缀。 6 1/2的打打,对吗?也许。但即使您使用的是ANSI-92惯例(正如我所拥有并在案例中将继续这样做),使用视敏度原则,垂直对齐让您的大脑避开您想要看到的地方,并轻松避开事物(特别是噪音词)你不需要。

答案 5 :(得分:1)

在我看来,FROM子句是我决定行中需要哪些列来处理SELECT子句的地方。这是表达业务规则的地方,它将带来计算所需的同一行。业务规则可以是具有发票的客户,从而产生包括客户负责的发票行。它也可以是与客户相同的邮政编码中的场所,从而产生一个靠近的场地和客户列表。

这是我计算结果集中行的中心的地方。毕竟,我们只是简单地展示了RDBMS中列表的隐喻,每个列表都有一个主题(实体),每一行都是实体的一个实例。如果理解行中心性,则理解结果集的实体。

WHERE子句在概念上在from子句中定义行之后执行,它会剔除SELECT子句不需要的行(或包含所需的行)。

因为连接逻辑可以在FROM子句和WHERE子句中表示,并且因为存在用于划分和征服复杂逻辑的子句,所以我选择将包含值的连接逻辑放在FROM子句的列中,因为这基本上是表达匹配列中值的业务规则。

即。我不会写这样的WHERE子句:

 WHERE Column1 = Column2

我会把它放在FROM子句中,如下所示:

 ON Column1 = Column2

同样,如果要将列与外部值(列中可能存在或不存在的值)进行比较,例如将邮政编码与特定邮政编码进行比较,我会将其放在WHERE子句中,因为我实质上是在说我只想要这样的行。

即。我不会写这样的FROM子句:

 ON PostCode = '1234'

我会把它放在WHERE子句中,如下所示:

 WHERE PostCode = '1234'

答案 6 :(得分:0)

ANSI语法不会在正确的子句(即ON或WHERE)中强制执行谓词放置,也不强制ON子句与相邻表引用的关联。开发人员可以自由地编写像这样的混乱

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;

说到“将生成ANSI-92”的查询工具,我在这里评论是因为它生成了

SELECT 1
   FROM DEPARTMENTS C
        JOIN EMPLOYEES A
             JOIN JOBS B
     ON C.DEPARTMENT_ID = A.DEPARTMENT_ID
     ON A.JOB_ID = B.JOB_ID

唯一逃避传统“restrict-project-cartesian product”的语法是外连接。此操作更复杂,因为它不是关联的(本身和正常连接)。至少,必须明智地用外连接括起查询。然而,这是一项奇特的行动;如果你经常使用它我建议采用关系数据库类。