建立测试环境:

  建立一个表Department和Employee,并向Department插入50W条记录,向Employee插入200W条记录,

我们就拿【统计DepartmentID 从150000 至380000的每一个Department有多少个Employee】这样一问题

来比较性能,再说他们如何使用更有效。


Use Test
Go
If object_id('Employee') Is Not Null
    Drop Table Employee
If object_id('Department') Is Not Null
    Drop Table Department
Go
Create Table Department
(
    ID int Identity(1,1) Not Null,
    Name nvarchar(50) Not Null,
    Constraint PK_Department Primary Key(ID Asc)
)
Go
Create Table Employee
(
    ID int Identity(1,1) Not Null,
    DepartmentID int Not Null,
    Name nvarchar(50) Not Null,
    Constraint PK_Employee Primary Key(ID Asc),
    Constraint FK_Employee_DepartmentID Foreign Key(DepartmentID) References Department(ID)
)
Create Nonclustered Index IX_Employee_DepartmentID On Employee(DepartmentID Asc)
GO
;With
t0 As(Select ID=1 Union All Select ID=1),
t1 As(Select a.ID From t0 As a,t0 As b),
t2 As(Select a.ID From t1 As a,t1 As b),
t3 As(Select a.ID From t2 As a,t2 As b),
t4 As(Select a.ID From t3 As a,t3 As b),
t5 As(Select a.ID From t4 As a,t4 As b),
t6 As(Select ID=Row_number() Over( Order By a.ID) From t5 As a,t5 As b)
Insert Into Department (Name)
    Select N'Dept'+Rtrim(ID) 
        From t6 
        Where ID<=500000 ;With
t0 As(Select ID=1 Union All Select ID=1),
t1 As(Select a.ID From t0 As a,t0 As b),
t2 As(Select a.ID From t1 As a,t1 As b),
t3 As(Select a.ID From t2 As a,t2 As b),
t4 As(Select a.ID From t3 As a,t3 As b),
t5 As(Select a.ID From t4 As a,t4 As b),
t6 As(Select ID=Row_number() Over( Order By a.ID) From t5 As a,t5 As b)
Insert Into Employee (DepartmentID,Name)
    Select ID%500000+1,N'Emp'+Rtrim(ID)
        From t6 
        Where ID<=2000000 Go

开始测试:


Use Test
Go
Dbcc Freeproccache With No_Infomsgs
Dbcc DropCleanBuffers With No_Infomsgs
Go
Set Statistics Io On
Set Statistics Time On
Go
Select a.ID,a.Name,b.Qty 
    From Department As a
        Outer Apply(Select Qty=Count(*) 
                        From Employee 
                        Where DepartmentID=a.ID
                    ) As b
    Where a.id Between 150000 And 380000 Select a.ID,a.Name,b.Qty  
    From Department As a
        Left Outer Join(Select Qty=Count(*),DepartmentID 
                            From Employee 
                            Group By DepartmentID
                        ) As b On b.DepartmentID=a.ID
    Where a.id Between 150000 And 380000 Go
Set Statistics Time Off
Set Statistics Io Off /* SQL Server 分析和编译时间: 
   CPU 时间 = 31 毫秒,占用时间 = 65 毫秒。 (230001 行受影响)
表 'Employee'。扫描计数 230001,逻辑读取 735944 次,物理读取 159 次,预读 16 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Department'。扫描计数 1,逻辑读取 1063 次,物理读取 3 次,预读 1060 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 3094 毫秒,占用时间 = 6440 毫秒。 (230001 行受影响)
表 'Department'。扫描计数 1,逻辑读取 1063 次,物理读取 22 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Employee'。扫描计数 1,逻辑读取 1258 次,物理读取 6 次,预读 8 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 750 毫秒,占用时间 = 7149 毫秒。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。 */

从上面的例子可以看出来,使用Outer Apply   的时候,对表Employee进行230001次扫描,从缓冲区去取页的数次为

735944,远远超过使用Left Outer Join 的次数。

再来测试一种情况,就是在Outer Apply()里不加Where条件 ,而Left Outer Join 的 On 为1=1:


Use Test
Go
Dbcc Freeproccache With No_Infomsgs
Dbcc DropCleanBuffers With No_Infomsgs
Go
Set Statistics IO On
Set Statistics Time On
Go
Select a.ID,a.Name,b.Qty 
    From Department As a
        Outer Apply(Select Qty=Count(*) 
                        From Employee 
                    ) As b
     Select a.ID,a.Name,b.Qty  
    From Department As a
        Left Outer Join(Select Qty=Count(*)
                            From Employee 
                        ) As b On 1=1 Go
Set Statistics Time Off
Set Statistics Io Off /* SQL Server 分析和编译时间: 
   CPU 时间 = 16 毫秒,占用时间 = 28 毫秒。 (500000 行受影响)
表 'Worktable'。扫描计数 1,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Employee'。扫描计数 1,逻辑读取 2731 次,物理读取 3 次,预读 2716 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Department'。扫描计数 1,逻辑读取 2276 次,物理读取 3 次,预读 2253 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 812 毫秒,占用时间 = 13823 毫秒。 (500000 行受影响)
表 'Worktable'。扫描计数 1,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Employee'。扫描计数 1,逻辑读取 2731 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Department'。扫描计数 1,逻辑读取 2276 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 969 毫秒,占用时间 = 12327 毫秒。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。 */

从这里可以看出,两种方法,I/O显示信息是一致的,而且执行的时间比较接近。性能相当。

这样性能相当的情况 ,也发生在这样的环境中:


Use Test
Go
Dbcc Freeproccache With No_Infomsgs
Dbcc DropCleanBuffers With No_Infomsgs
Go
Set Statistics IO On
Set Statistics Time On
Go
Select a.ID,a.Name,b.Qty 
    From Department As a
        Outer Apply(Select Qty=Count(*) 
                        From Employee 
                        Where DepartmentID=a.ID
                    ) As b
    Where a.ID=454514 Select a.ID,a.Name,b.Qty  
    From Department As a
        Left Outer Join(Select Qty=Count(*),DepartmentID 
                            From Employee 
                            Group By DepartmentID
                        ) As b On b.DepartmentID=a.ID
    Where a.ID=454514
Go
Set Statistics Time Off
Set Statistics Io Off /*
SQL Server 分析和编译时间: 
   CPU 时间 = 15 毫秒,占用时间 = 37 毫秒。 (1 行受影响)
表 'Employee'。扫描计数 1,逻辑读取 3 次,物理读取 3 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Department'。扫描计数 0,逻辑读取 3 次,物理读取 3 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 35 毫秒。 (1 行受影响)
表 'Employee'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Department'。扫描计数 0,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
*/

从多次的测试中可以发现,不能滥用Outer Apply 来代替Left Outer Join。

其实在联机帮助中Apply 定义是这样:

  ”使用 APPLY 运算符可以为实现查询操作的外部表表达式返回的每个行调用表值函数。表值函数作为右输入,外部表表达式作为左输入。通过对右输入求值来获得左输入每一行的计算结果,生成的行被组合起来作为最终输出。APPLY 运算符生成的列的列表是左输入中的列集,后跟右输入返回的列的列表。  “

  当在右边输入的是表值函数情况,我们可以考虑使用Apply,这样能为我们带来很多方便。或者条件类似为最后面测试那样,使用Outer Apply 和Left Outer Jion性能是一样的。在更复杂的环境更是要小心,需要多做测试,使用多种方法来调试才知道哪一方法好,性能更佳。

知识点:

在第二次测试中看到有,

表 'Worktable'。扫描计数 1,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

这里说明"WorkTable"含义:

  The relational engine may need to build a worktable to perform a logical operation specified in an SQL statement. Worktables are internal tables that are used to hold intermediate results. Worktables are generated for certain GROUP BY, ORDER BY, or UNION queries. For example, if an ORDER BY clause references columns that are not covered by any indexes, the relational engine may need to generate a worktable to sort the result set into the order requested. Worktables are also sometimes used as spools that temporarily hold the result of executing a part of a query plan. Worktables are built in tempdb and are dropped automatically when they are no longer needed.

  关系引擎可能需要生成一个工作表以执行 SQL 语句中指定的逻辑操作。工作表是用于保存中间结果的内部表。某些 GROUP BY、ORDER BY 或 UNION 查询中会生成工作表。例如,如果 ORDER BY 子句引用了不为任何索引涵盖的列,则关系引擎可能需要生成一个工作表以按所请求的顺序对结果集进行排序。工作表有时也用作临时保存执行部分查询计划所得结 果的假脱机。工作表在tempdb中生成,并在不再需要时自动删除。

(完)

LeftOuterJoin和OuterApply性能比较(转)的更多相关文章

  1. [看图说话] 基于Spark UI性能优化与调试——初级篇

    Spark有几种部署的模式,单机版.集群版等等,平时单机版在数据量不大的时候可以跟传统的java程序一样进行断电调试.但是在集群上调试就比较麻烦了...远程断点不太方便,只能通过Log的形式,进行分析 ...

  2. Spark 3.x Spark Core详解 & 性能优化

    Spark Core 1. 概述 Spark 是一种基于内存的快速.通用.可扩展的大数据分析计算引擎 1.1 Hadoop vs Spark 上面流程对应Hadoop的处理流程,下面对应着Spark的 ...

  3. 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题

    背景起因: 记起以前的另一次也是关于内存的调优分享下   有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...

  4. 01.SQLServer性能优化之----强大的文件组----分盘存储

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 文章内容皆自己的理解,如有不足之处欢迎指正~谢谢 前天有学弟问逆天:“逆天,有没有一种方 ...

  5. 03.SQLServer性能优化之---存储优化系列

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 概  述:http://www.cnblogs.com/dunitian/p/60413 ...

  6. SignalR系列续集[系列8:SignalR的性能监测与服务器的负载测试]

    目录 SignalR系列目录 前言 也是好久没写博客了,近期确实很忙,嗯..几个项目..头要炸..今天忙里偷闲.继续我们的小系列.. 先谢谢大家的支持.. 我们来聊聊SignalR的性能监测与服务器的 ...

  7. 【前端性能】高性能滚动 scroll 及页面渲染优化

    最近在研究页面渲染及web动画的性能问题,以及拜读<CSS SECRET>(CSS揭秘)这本大作. 本文主要想谈谈页面优化之滚动优化. 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的 ...

  8. Web性能优化:What? Why? How?

    为什么要提升web性能? Web性能黄金准则:只有10%~20%的最终用户响应时间花在了下载html文档上,其余的80%~90%时间花在了下载页面组件上. web性能对于用户体验有及其重要的影响,根据 ...

  9. Web性能优化:图片优化

    程序员都是懒孩子,想直接看自动优化的点:传送门 我自己的Blog:http://cabbit.me/web-image-optimization/ HTTP Archieve有个统计,图片内容已经占到 ...

随机推荐

  1. linux常用命令-搜索

    1.find $ find . -name nginx* $ find . -name nginx* -ls $ find . -type f -mmin -10 搜索当前目录中,所有过去10分钟中更 ...

  2. apacheserver下载、安装、配置

     1.下载 下载地址:apache下载地址 点击左側"Download"下的链接,打开例如以下页面 这里能够选择版本号.我选择2.4.16.单击"2.4.16" ...

  3. Codeforces gym 100685 F. Flood bfs

    F. FloodTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100685/problem/F Desc ...

  4. 异步消息总线hornetq学习-03客户端连接hornet进行jms消息的收发-非jndi方式连接

    在上节中介绍了通过jndi方式连接到hornetq服务器上,有时候由于某些原因,我们不希望通过jndi方式连接,hornetq也支持这种方式进行 以第2章节的例子为模板,我们编写了另一个获取Conne ...

  5. U盘安装Win7 64位系统(笔记本+台式机亲测)

    准备工具: 1. Win7 64位系统的镜像文件(网上随便一搜即可,最好是纯净版,没有一堆乱七八糟的内置软件) 2. 4G以上的U盘一个 所用软件: 老毛桃(官网下载) 具体步骤: 1.数据备份(将原 ...

  6. Blueprint编译过程

    Blueprint 编译概述 一.术语 Blueprint,像C++语言一下的,在游戏中使用前须要编译.当你在BP编辑器中,点击编译button时候.BP资源開始把属性和图例过程转换为一个类对象处理. ...

  7. 一个坐标点围绕任意中心点旋转--C#实现

    假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x', y'),有公式: x'= (x - rx0)*cos(RotaryAngle) ...

  8. Android之自定义AlertDialog无法监听控件

    参考:http://www.cnblogs.com/511mr/archive/2011/10/21/2220253.html 要做一个自定义的弹出框,以前都是用一个Activity来实现,总觉得不是 ...

  9. 【完结】利用 Composer 完善自己的 PHP 框架(三)——Redis 缓存

    本教程示例代码见 https://github.com/johnlui/My-First-Framework-based-on-Composer 回顾 上两篇文章中我们完成了 View 视图加载类和 ...

  10. Eclipse开发JQuery环境设置(Spket)

     http://www.cnblogs.com/sayo/archive/2008/10/20/1315528.html   首先需要安装Spket.可以参阅我的这篇文章进行Spket的安装. 之后进 ...