【问题标题】:Does the order of tables referenced in the ON clause of the JOIN matter?JOIN 的 ON 子句中引用的表的顺序是否重要?
【发布时间】:2010-10-21 14:21:21
【问题描述】:

我在 ON 子句中为 JOIN 排序条件的方式是否重要?

select a.Name, b.Status from a
inner join b
on a.StatusID = b.ID

select a.Name, b.Status from a
inner join b
on b.ID = a.StatusID

对性能有影响吗?如果我有多个条件怎么办?

一个订单比另一个更易于维护吗?

【问题讨论】:

    标签: sql mysql sql-server oracle


    【解决方案1】:

    JOIN 可以通过在FROM 子句中将表按正确顺序来强制执行:

    1. MySQL 有一个名为 STRAIGHT_JOIN 的特殊子句,它使顺序很重要。

      这将使用b.id 上的索引:

      SELECT  a.Name, b.Status
      FROM    a
      STRAIGHT_JOIN
              b
      ON      b.ID = a.StatusID
      

      这将使用a.StatusID 上的索引:

      SELECT  a.Name, b.Status
      FROM    b
      STRAIGHT_JOIN
              a
      ON      b.ID = a.StatusID
      
    2. Oracle 有一个特殊提示 ORDERED 来强制执行 JOIN 顺序:

      这将使用b.id 上的索引或在b 上构建哈希表:

      SELECT  /*+ ORDERED */
              *
      FROM    a
      JOIN    b
      ON      b.ID = a.StatusID
      

      这将使用a.StatusID 上的索引或在a 上构建哈希表:

      SELECT  /*+ ORDERED */
              *
      FROM    b
      JOIN    a
      ON      b.ID = a.StatusID
      
    3. SQL Server 有一个名为 FORCE ORDER 的提示来执行相同的操作:

      这将使用b.id 上的索引或在b 上构建哈希表:

      SELECT  *
      FROM    a
      JOIN    b
      ON      b.ID = a.StatusID
      OPTION (FORCE ORDER)
      

      这将使用a.StatusID 上的索引或在a 上构建哈希表:

      SELECT  *
      FROM    b
      JOIN    a
      ON      b.ID = a.StatusID
      OPTION (FORCE ORDER)
      
    4. PostgreSQL 伙计们,对不起。你的TODO list 说:

      优化器提示(不需要)

      优化器提示用于解决优化器中的问题。我们宁愿报告和修复问题。

    至于比较中的顺序,在任何RDBMS,AFAIK中都没有关系。

    虽然我个人总是尝试估计将搜索哪一列并将该列放在左侧(使其看起来像 lvalue)。

    更多详情请参阅this answer

    【讨论】:

    • 关于“PostgreSQL 伙计们,对不起”-官方 TODO wiki.postgresql.org/wiki/Todo#Features_We_Do_Not_Want 中的“优化器提示”。
    • 我有点惊讶。 OP 询问了 ON 子句的顺序,还展示了一个示例,其中他没有反转表,而只反转了 ON-子句,但这个答案只处理表顺序反转的情况。还是最后一条评论“至于对比中的顺序……”指的是它吗?
    • @TimSchmelter:至于比较中的顺序,在任何 RDBMS 中都没有关系,AFAIK。
    【解决方案2】:

    不,它没有。

    我所做的(为了可读性)是你的第二个例子。

    【讨论】:

      【解决方案3】:

      没有。数据库应该根据整个标准确定最佳执行计划,而不是通过按顺序查看每个项目来创建它。您可以通过请求两个查询的执行计划来确认这一点,您会看到它们是相同的(您会发现即使是截然不同的查询,只要它们最终指定相同的逻辑,通常都会编译成相同的执行计划)。

      【讨论】:

        【解决方案4】:

        不,没有。归根结底,您实际上只是在评估是否 a=b。

        正如平等的对称性所说:

        • 对于任意数量 a 和 b,如果 a = b,则 b = a。

        所以无论您检查(12)*=12 还是12=(12)* 在逻辑上都没有区别。

        如果值相等,则加入,如果不相等,则不加入。无论您是在第一个示例还是第二个示例中指定它,都没有区别。

        【讨论】:

        • 这是已发布问题的正确答案。
        【解决方案5】:

        正如许多人所说:订单对结果或性能没有影响。

        我想指出的是,LINQ to SQL 只允许第一种情况

        例如,下面的例子效果很好,...

        var result = from a in db.a
                     join b in db.b on a.StatusID equals b.ID
                     select new { Name = a.Name, Status = b.Status }
        

        ...虽然这会在 Visual Studio 中引发错误:

        var result = from a in db.a
                     join b in db.b on b.ID equals a.StatusID
                     select new { Name = a.Name, Status = b.Status }
        

        会引发这些编译器错误:

        • CS1937:名称“name”不在“equals”左侧的范围内。考虑交换 'equals' 两边的表达式。
        • CS1938:名称“name”不在“equals”右侧的范围内。考虑交换 'equals' 两边的表达式。

        虽然与标准 SQL 编码无关,但在习惯使用其中任何一种时,这可能是需要考虑的一点。

        【讨论】:

          【解决方案6】:

          Read this

          SqlServer 包含针对远比这复杂的情况的优化。

          如果你有多个标准,通常会懒惰地评估东西(但如果有的话,我需要围绕边缘情况做一些研究。)

          为了可读性,我通常更喜欢

          SELECT Name, Status FROM a 
          JOIN b 
          ON a.StatusID = b.ID
          

          我认为按照声明变量的顺序引用变量更有意义,但这确实是个人品味。

          【讨论】:

            【解决方案7】:

            我不使用您的第二个示例的唯一原因:

            select a.Name, b.Status 
            from a
            inner join b
              on b.ID = a.StatusID
            

            您的用户更有可能回来说“即使他们没有状态记录,我也可以查看所有 a.name 吗?”而不是“即使 b.status 没有名称记录,我也可以看到所有的信息吗?”,所以为了提前计划这个例子,我会使用 On a.StatusID = b.ID 来期待 LEFT Outer Join。这假设您可以有没有“b”的表“a”记录。

            更正:它不会改变结果。

            这可能是一个有争议的问题,因为用户永远不想改变他们的要求。

            【讨论】:

            • 在 LEFT 或 RIGHT 连接中的顺序无关紧要,所以我可以看到在这种情况下并没有真正的区别。即使 ON 子句是“b.ID = a.StatusID”,您仍然可以将子句更改为 LEFT OUTER JOIN
            • @Tom - 很高兴你指出了这一点。我差点惊恐发作。
            【解决方案8】:

            不,没关系。但这里有一个示例可以帮助您提高查询的可读性(至少对我而言)

            select a.*, b.*
            from tableA a
                 inner join tableB b
                      on a.name=b.name
                           and a.type=b.type
            

            每个表引用都在单独的行上,每个连接条件都在单独的行上。制表符有助于保持属于什么的直。

            我喜欢做的另一件事是让我在 on 语句中的条件与表格的顺序相同。所以如果先a后b,a在左边,b在右边。

            【讨论】:

              【解决方案9】:

              错误:ON 子句在其右侧引用表(php sqlite 3.2)

              替换这个

              LEFT JOIN itm08 i8 ON  i8.id= **cdd01.idcmdds** and i8.itm like '%ormit%'  
              
              LEFT JOIN **comodidades cdd01** ON cdd01.id_registro = u.id_registro 
              

              为此

              LEFT JOIN **comodidades cdd01** ON cdd01.id_registro = u.id_registro
              
              LEFT JOIN itm08 i8 ON  i8.id= **cdd01.idcmdds** and i8.itm like '%ormit%'
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-07-11
                • 1970-01-01
                • 2017-12-19
                • 2011-09-22
                • 1970-01-01
                • 2011-03-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多