【问题标题】:Explicit vs implicit SQL joins显式与隐式 SQL 连接
【发布时间】:2010-09-07 21:04:45
【问题描述】:

在显式和隐式内部连接中是否存在效率差异? 例如:

SELECT * FROM
table a INNER JOIN table b
ON a.id = b.id;

对比

SELECT a.*, b.*
FROM table a, table b
WHERE a.id = b.id;

【问题讨论】:

  • 好问题。我很好奇为什么要使用显式连接。没有它就不能进行所有查询吗?
  • 使用 EXPLAIN 关键字来了解这两个查询的区别。使用 JOIN 并查看区别。如果您在表中尝试超过 100k 条记录,您可以看到区别...
  • @andrew 我的问题实际上是隐式联接是否是一种“黑客”形式(如“涉及多个表的查询,不使用联接?那是黑客不是吗?” )
  • 它们是不同的,隐式连接在处理空值时会时不时地让你吃惊;使用显式连接并避免在“没有任何改变!”时出现的错误!
  • 没有区别。 ,CROSS JOIN,绑定较松,INNER JOINCROSS JOINON 类似于 WHERE,但绑定更紧密。对执行而言重要的是 DBMS 如何优化查询。

标签: sql join


【解决方案1】:

在性能方面,它们完全相同(至少在 SQL Server 中)。

PS:请注意,自 SQL Server 2005 起,IMPLICIT OUTER JOIN 语法已被弃用。(问题中使用的 IMPLICIT INNER JOIN 语法仍受支持)

Deprecation of "Old Style" JOIN Syntax: Only A Partial Thing

【讨论】:

  • @lomaxx,为了清楚起见,您能否指定问题中 2 的 which 语法已弃用?
  • 您能提供支持文件吗?这在多个层面上听起来都是错误的。
  • 如何弃用 SQL 标准?
  • @david Crenshaw,隐式连接不再是标准,并且已经有 18 年了。
  • “内部”或“交叉”类型的所谓“隐式连接”仍保留在标准中。 SQL Server 正在弃用从未成为标准的“旧式”外连接语法(即*==*)。
【解决方案2】:

我个人更喜欢连接语法,因为它可以更清楚地说明表的连接方式以及连接方式。尝试比较较大的 SQL 查询,您从 8 个不同的表中进行选择,并且您在 where 中有很多过滤。通过使用连接语法,您可以将连接表的部分与过滤行的部分分开。

【讨论】:

  • 我完全同意,但这有点离题。 OP 询问效率。
【解决方案3】:

在性能方面,它们完全相同(至少在 SQL Server 中),但请注意,它们正在弃用这种连接语法,并且开箱即用的 sql server2005 不支持它。

我认为您正在考虑已弃用的 *= 和 =* 运算符与“外部联接”。

我刚刚测试了给定的两种格式,它们在 SQL Server 2008 数据库上正常工作。就我而言,它们产生了相同的执行计划,但我不能自信地说这将永远是正确的。

【讨论】:

    【解决方案4】:

    在某些数据库(尤其是 Oracle)上,连接顺序会对查询性能产生巨大影响(如果有两个以上的表)。在一个应用程序中,在某些情况下,我们实际上有两个数量级的差异。如果您使用正确的提示语法,则使用内连接语法可以控制这一点。

    您没有指定您使用的是哪个数据库,但概率建议 SQL Server 或 MySQL 在那里没有真正的区别。

    【讨论】:

    • Leigh,你也可以在隐式连接中使用提示。
    • 在 Oracle 中,连接顺序以有意义的方式影响执行计划的情况极为罕见。请参阅 Jonathan Lewis 的 this article 以获得解释。
    【解决方案5】:

    @lomaxx:澄清一下,我很确定 SQL Serv 2005 支持上述两种语法。但是不支持以下语法

    select a.*, b.*  
    from table a, table b  
    where a.id *= b.id;
    

    具体来说,不支持外连接 (*=)。

    【讨论】:

    • 坦率地说,即使在 SQL Server 2000 中我也不会使用它,*= 语法经常给出错误的答案。有时它将这些解释为交叉连接。
    【解决方案6】:

    您给出的第一个答案使用了所谓的 ANSI 连接语法,另一个是有效的并且适用于任何关系数据库。

    我同意 grom 的观点,即您应该使用 ANSI 连接语法。正如他们所说,主要原因是为了清晰。而不是一个带有大量谓词的 where 子句,其中一些连接表,而另一些则限制使用 ANSI 连接语法返回的行,而是让您清楚地清楚哪些条件用于连接您的表,哪些条件用于限制结果。

    【讨论】:

      【解决方案7】:

      正如 Leigh Caldwell 所说,查询优化器可以根据功能上看起来与同一 SQL 语句相似的内容生成不同的查询计划。如需进一步阅读,请查看以下两篇博文:-

      One posting from the Oracle Optimizer Team

      Another posting from the "Structured Data" blog

      我希望你觉得这很有趣。

      【讨论】:

      • Mike,他们所说的不同之处在于,您需要确保如果指定显式连接,则指定要连接的 连接条件,而不是过滤器.您会注意到,对于语义正确的查询,执行计划是相同的。
      【解决方案8】:

      第二种语法有交叉连接的不希望的可能性:您可以将表添加到 FROM 部分而无需相应的 WHERE 子句。这被认为是有害的。

      【讨论】:

      • 如果from子句中的表名是从where子句中使用的表生成的呢?
      • 您也可以使用显式 JOIN 语法进行交叉连接。(stackoverflow.com/a/44438026/929164) 您可能意味着它不那么严格,因此更容易出现用户错误。
      【解决方案9】:

      在性能方面,它应该没有任何区别。显式连接语法对我来说似乎更清晰,因为它清楚地定义了 from 子句中表之间的关系,并且不会弄乱 where 子句。

      【讨论】:

        【解决方案10】:

        在 MySQL 5.1.51 上,两个查询具有相同的执行计划:

        mysql> explain select * from table1 a inner join table2 b on a.pid = b.pid;
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        | id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        |  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
        |  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        2 rows in set (0.02 sec)
        
        mysql> explain select * from table1 a, table2 b where a.pid = b.pid;
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        | id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        |  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
        |  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
        +----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
        2 rows in set (0.00 sec)
        

        table1 有 166208 行; table2 大约有 1000 行。

        这是一个非常简单的案例;它无论如何都不能证明查询优化器不会混淆并在更复杂的情况下生成不同的计划。

        【讨论】:

        • 这应该是公认的答案。这是正确的,计划是相同的(或接近更大的语句),但记录的数量会很大,从而导致性能差异。
        【解决方案11】:

        根据我的经验,使用 cross-join-with-a-where-clause 语法通常会导致执行计划受损,尤其是在您使用 Microsoft SQL 产品时。例如,SQL Server 尝试估计表行数的方式非常可怕。使用内连接语法可以让您控制查询的执行方式。所以从实际的角度来看,鉴于当前数据库技术的返祖性质,您必须使用内部连接。

        【讨论】:

        【解决方案12】:

        基本上,两者的区别在于一个是用旧方式写的,而另一个是用现代方式写的。就个人而言,我更喜欢使用内部、左侧、外部、右侧定义的现代脚本,因为它们更具解释性并且使代码更具可读性。

        在处理内连接时,可读性也没有真正的区别,但是,在处理左右连接时可能会变得复杂,因为在旧方法中你会得到这样的结果:

        SELECT * 
        FROM table a, table b
        WHERE a.id = b.id (+);
        

        上面是左连接的旧写法,而不是下面的:

        SELECT * 
        FROM table a 
        LEFT JOIN table b ON a.id = b.id;
        

        如您所见,现代的脚本编写方式使查询更具可读性。 (顺便说一下,右连接也是如此,外连接稍微复杂一些)。

        回到样板,它不会对 SQL 编译器如何编写查询产生影响,因为它以相同的方式处理它们。我在 Oracle 数据库中看到了这两种情况的混合,很多人都写过它,无论是年长的还是年轻的。同样,它归结为脚本的可读性和您正在开发的团队。

        【讨论】:

          猜你喜欢
          • 2019-04-03
          • 1970-01-01
          • 2011-06-15
          • 2010-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-10
          相关资源
          最近更新 更多