• Join的表顺序


     

    在 今天的文章里,我想谈下SQL Server里一个非常有趣的话题:在表联接里,把表指定顺序的话是否有意义?每次我进行查询和性能调优的展示时,大家都会问我他们是否应该把联接中的表 指定下顺序,是否会帮助查询优化器得出一个更好性能的执行计划。我们来看下这个重要又有趣的问题。

    合并联接(Inner Joins)

    假设在AdventureWorks数据库里,你要在Sales.SalesOrderHeader表和Sales.SalesOrderDetail表之间做一个内联接:

    复制代码
     1 USE AdventureWorks
     2 GO
     3 
     4 -- Returns for each SalesOrderHeader record all associated SalesOrderDetail records
     5 -- SQL Server performs a Merge Join, because both tables are phyiscally sorted
     6 -- by the column "SalesOrderID".
     7 SELECT
     8     h.SalesOrderID,
     9     h.CustomerID,
    10     d.SalesOrderDetailID,
    11     d.ProductID,
    12     d.LineTotal
    13 FROM Sales.SalesOrderHeader h
    14 JOIN Sales.SalesOrderDetail d
    15 ON h.SalesOrderID = d.SalesOrderID
    16 ORDER BY SalesOrderID
    17 GO
    复制代码

    当我们查看结果的执行计划时,我们可以看到查询优化器选择了合并联接(Inner Join)作为物理联接运算符,Sales.SalesOrderHeader表作为合并联接的外联接。在执行计划里表的顺序和我们在逻辑T-SQL查询里的顺序是一样的。

    现在的问题是,当我们在逻辑T-SQL查询里交换下2个表的顺序,执行计划会发生什么?我们来试下:

    复制代码
     1 -- The logical ordering of the tables during an Inner Join
     2 -- doesn't matter. It's up to the Query Optimnizer to arrange
     3 -- the tables in the best order.
     4 -- This query produces the same execution plan as the previous one.
     5 SELECT
     6     h.SalesOrderID,
     7     h.CustomerID,
     8     d.SalesOrderDetailID,
     9     d.ProductID,
    10     d.LineTotal
    11 FROM Sales.SalesOrderDetail d
    12 JOIN Sales.SalesOrderHeader h
    13 ON d.SalesOrderID = h.SalesOrderID
    14 ORDER BY SalesOrderID
    15 GO
    复制代码

    但我们现在看结果的执行计划,我们发现很有意思:

    在执行计划里没有任何改变!查询优化器选择了和刚才查 询一样的物理执行计划。但为什么?答案非常简单:查询优化器总引用最小的表(基于我们的统计信息!)作为每个物理连接运算符(嵌套循环联接,合并联接,哈 希匹配联接)的外联接表。因此在T-SQL查询里的表的逻辑顺序不会对查询优化器造成任何影响。按正确的顺序访问我们的表是查询优化器的职责。

    在表A和表B之间的合并联接与表B和表A之间的合并联接是一样的。

    外联接(Outer Join)

    在外联接(left join,right join)里,表顺序会有啥影响?我们来看下面的查询,在Sales.Customer表和 Sales.SalesOrderHeader表之间进行左联接。

    复制代码
     1 -- Execute the query with an Outer Join.
     2 -- Now we are also getting back customers that haven't placed orders.
     3 -- The left table is the preserving one, and missing rows from the right table are added with NULL values.
     4 -- SQL Server performs a "Merge Join (Left Outer Join)" in the execution plan.
     5 SELECT
     6     c.CustomerID,
     7     h.SalesOrderID
     8 FROM Sales.Customer c
     9 LEFT JOIN Sales.SalesOrderHeader h
    10 ON c.CustomerID = h.CustomerID
    11 GO
    复制代码

    当我们查看结果执行计划时,我们会看到查询优化器已经隐藏了我们的表顺序。

    当然这次我们不能修改T-SQL语句里的表顺序,不然查询会返回错误的结果。但当我们在查询里切换下表会发生什么,不是左联接,我们用右联接。我们来试下:

    复制代码
    1 -- You can rewrite the query from above with a Right Outer Join when you swap the order
    2 -- of the tables. This time you get back the same result (32166 rows).
    3 SELECT
    4     c.CustomerID,
    5     h.SalesOrderID
    6 FROM Sales.SalesOrderHeader h
    7 RIGHT JOIN Sales.Customer c
    8 ON c.CustomerID = h.CustomerID
    9 GO
    复制代码

    当我们看执行计划时,我们再次看到没有任何改变:查询 优化器转化右联接为左联接,重排了下表还是返回正确的结果。查询优化器的目标是使用最小表作为物理联接运算符的外表。因此在外联接里表的顺序也不会影响查 询优化器。只要我们的统计信息是正确的,查询优化器总会选择正确的顺序。

    在表A和表B之间的左联接与表B和表A之间的右联接是一样的。

    小结:

    在这篇文章里我们讨论对于联接,表的顺序是否会影响执行计划。如我们所见,这完全由查询优化器来决定选择优化的表顺序——基于统计信息。在合并联接里表顺序完全不影响,使用外联接的话,SQL Server可以通过切换左联接/右联接来重排表,还是获得正确的结果。

  • 相关阅读:
    JS 关于 URL 的编码或解码方法
    PHP 获取上一个页面的url
    踩坑 Uncaught RangeError: Maximum call stack size exceeded
    Wordpress 数据库查询错误 Call to a member function get_results() on null
    Chrome autocomplete="off"无效
    js 遍历对象属性(for in、Object.keys、Object.getOwnProperty) 以及高效地输出 js 数组
    PHP PDO fetch() 详解
    Wordpress 自定义文章类型添加 Categoried、Tags
    Mac: mac git 的安装 及实现自动补全
    Uncaught TypeError: Cannot read property of undefined In JavaScript
  • 原文地址:https://www.cnblogs.com/lykbk/p/dfdfdfdfddfdf345455656565656.html
Copyright © 2020-2023  润新知