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

合并联接(Inner Joins)

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

 USE AdventureWorks
GO -- Returns for each SalesOrderHeader record all associated SalesOrderDetail records
-- SQL Server performs a Merge Join, because both tables are phyiscally sorted
-- by the column "SalesOrderID".
SELECT
h.SalesOrderID,
h.CustomerID,
d.SalesOrderDetailID,
d.ProductID,
d.LineTotal
FROM Sales.SalesOrderHeader h
JOIN Sales.SalesOrderDetail d
ON h.SalesOrderID = d.SalesOrderID
ORDER BY SalesOrderID
GO

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

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

 -- The logical ordering of the tables during an Inner Join
-- doesn't matter. It's up to the Query Optimnizer to arrange
-- the tables in the best order.
-- This query produces the same execution plan as the previous one.
SELECT
h.SalesOrderID,
h.CustomerID,
d.SalesOrderDetailID,
d.ProductID,
d.LineTotal
FROM Sales.SalesOrderDetail d
JOIN Sales.SalesOrderHeader h
ON d.SalesOrderID = h.SalesOrderID
ORDER BY SalesOrderID
GO

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

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

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

外联接(Outer Join)

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

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

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

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

 -- You can rewrite the query from above with a Right Outer Join when you swap the order
-- of the tables. This time you get back the same result (32166 rows).
SELECT
c.CustomerID,
h.SalesOrderID
FROM Sales.SalesOrderHeader h
RIGHT JOIN Sales.Customer c
ON c.CustomerID = h.CustomerID
GO

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

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

小结:

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

参考文章:

http://www.sqlpassion.at/archive/2015/12/15/table-ordering-for-joins/

Join的表顺序的更多相关文章

  1. JOIN关联表中ON,WHERE后面跟条件的区别

    select * from td  left join (select case_id as sup_case_id , count(*) supervise_number from  td_kcdc ...

  2. join多表连接和group by分组

    join多表连接和group by分组 上一篇里面我们实现了单表查询和top N查询,这一篇我们来讲述如何实现多表连接和group by分组. 一.多表连接 多表连接的时间是数据库一个非常耗时的操作, ...

  3. 【Spark调优】大表join大表,少数key导致数据倾斜解决方案

    [使用场景] 两个RDD进行join的时候,如果数据量都比较大,那么此时可以sample看下两个RDD中的key分布情况.如果出现数据倾斜,是因为其中某一个RDD中的少数几个key的数据量过大,而另一 ...

  4. MySQL JOIN 多表连接

    除了常用的两个表连接之外,SQL(MySQL) JOIN 语法还支持多表连接.多表连接基本语法如下: 1 ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON ...

  5. 对于大量left join 的表查询,可以在关键的 连接节点字段上创建索引。

    对于大量left join 的表查询,可以在关键的 连接节点字段上创建索引. 问题: 大量的left join 怎么优化 select a.id,a.num,b.num,b.pcs,c.num, c. ...

  6. left join 连表时,on后多条件无效问题

    http://www.cnblogs.com/guixiaoming/p/6516261.html left join 连表时,on后多条件无效问题 最近开发素材库项目,各种关系复杂的表,一度6张表的 ...

  7. 【Spark调优】小表join大表数据倾斜解决方案

    [使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...

  8. mybatis逆向工程,实现join多表查询,避免多表相同字段名的陷阱

    ​ mybatis逆向工程,实现join多表查询,避免多表相同字段名的陷阱 ​ 前言:使用 mybatis generator 生成表格对应的pojo.dao.mapper,以及对应的example的 ...

  9. 大数据开发实战:Hive优化实战3-大表join大表优化

    5.大表join大表优化 如果Hive优化实战2中mapjoin中小表dim_seller很大呢?比如超过了1GB大小?这种就是大表join大表的问题.首先引入一个具体的问题场景,然后基于此介绍各自优 ...

随机推荐

  1. Delphi Berlin 10.1 for 小米平板2 (Win 10) 电子罗盘测试

    Windows 10 下没有 Sensor.HeadingXSensor.HeadingYSensor.HeadingZ 需改用 Sensor.CompMagHeading

  2. mssql游标实现递归查询

    表值函数 存储过程

  3. 2016暑假多校联合---Substring(后缀数组)

    2016暑假多校联合---Substring Problem Description ?? is practicing his program skill, and now he is given a ...

  4. Scalaz(4)- typeclass:标准类型-Equal,Order,Show,Enum

    Scalaz是由一堆的typeclass组成.每一个typeclass具备自己特殊的功能.用户可以通过随意多态(ad-hoc polymorphism)把这些功能施用在自己定义的类型上.scala这个 ...

  5. 实用的Scala泛函编程

    既然谈到实用编程,就应该不单止了解试试一个新的编程语言那么简单了,最好通过实际的开发项目实例来演示如何编程.心目中已经有了一些设想:想用Scala泛函编程搞一个开源的数据平台应用系统,也就是在云平台P ...

  6. 让.NET 4.0支持TLS1.2协议

    The default System.Net.ServicePointManager.SecurityProtocol in both .NET 4.0/4.5 is SecurityProtocol ...

  7. Python实现装饰模式的一段代码

    # 实现装饰模式的一段代码 import functools def log(func): @functools.wraps(func) def wrapper(*args,**kw): print( ...

  8. 初识Aop和扩展Aop

    一.什么叫做AOp 解析:Aop(Aspect Oriented Programming)是面向切面编程,软件编程的一种思想. OOp(Object Oriented Programming)是面向对 ...

  9. 玩转Docker之安装篇(二)

    按理说,安装软件最好是按照官方文档进行,但由于国内网络的局限性,使我们不得不搭梯子,但终究太麻烦.鉴于此情况,我推荐方案一,利用某公司的加速站点来安装,而且是傻瓜式的,非常方便. 方案一: 在linu ...

  10. Nodejs与ES6系列2:Promise对象

    2.promise对象 js单线程异步执行的特性,因此在代码中充斥着回调函数.随着回调函数的增加,代码的可读性会愈来愈差,因此引入promise对象是不错的一种选择,可以避免层层回调函数.在ECMA6 ...