LeftOuterJoin和OuterApply性能比较(转)

建立测试环境:

  建立一个表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中生成,并在不再需要时自动删除。

(完)

时间: 2024-12-02 17:56:09

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

10.6 监控io性能 - 10.7 free命令 - 10.8 ps命令 - 10.9 查看网络状态 - 10.10 linux下抓包

- 10.6 监控io性能 - 10.7 free命令 - 10.8 ps命令 - 10.9 查看网络状态 - 10.10 linux下抓包 - 扩展tcp三次握手四次挥手 http://www.doc88.com/p-9913773324388.html  - tshark几个用法:http://www.aminglinux.com/bbs/thread-995-1-1.html  # 10.6 监控io性能 ![mark](http://oqxf7c508.bkt.clouddn.com/b

iOS开发——项目实战总结&amp;UITableView性能优化与卡顿问题

UITableView性能优化与卡顿问题 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候,就会堆积很多cell.如果重用cell,为cell创建一个ID 每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell 2.避免cell的重新布局 cell的布局填充等操作 比较耗时,一般创建时就布局好 如可以将cell单独放到一个自定义类,初始化时就布局好

Java性能优化之JVM GC(垃圾回收机制)

Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-the-world 的发生. JVM GC回收哪个区域内的垃圾? 需要注意的是,JV

Performance Monitor4:监控SQL Server的IO性能

SQL Server的IO性能受到物理Disk的IO延迟和SQL Server内部执行的IO操作的影响.在监控Disk性能时,最主要的度量值(metric)是IO延迟,IO延迟是指从Application创建IO请求,到Disk完成IO请求的时间延迟.如果物理Disk不能及时完成IO请求,跟不上请求负载的速度,那么SQL Server就容易出现性能问题.SQL Server内部在执行一些特定的操作时,会和Disk做读写交互,这也会影响物理硬盘响应SQL Server的IO请求的性能,使查询进程处

LR如何利用siteScope监控MySQL性能

本次实验,是在自己的电脑上使用APMServ5.2.6部署Discuz2.X论坛下,对该论坛的数据库MySQL5.1进行性能测试的,下面讲述LoadRunner在设计场景时,如何利用siteScope工具监控MySQL数据库性能: 一.在网上下载siteScope 我在网上搜了很久,发现siteScope软件的下载资源很少,毕竟它是收费的,想找个破解版,不过还是有的,哈哈...但是版本旧了点siteScope7.9.5 下载地址:http://download.csdn.net/detail/y

Linux性能优化之磁盘优化(三)

前言 关于本章内容,设计的东西比较多.这里会有关于文件系统.磁盘.CPU等方面的知识,以及涉及到关于这方面的性能排查等. 术语 文件系统通过缓存和缓冲以及异步I/O等手段来缓和磁盘的延时对应用程序的影响.为了更详细的了解文件系统,以下就简单介绍一些相关术语: 文件系统:一种把数据组织成文件和目录的存储方式,提供了基于文件的存取接口,并通过文件权限控制访问.另外,一些表示设备.套接字和管道的特殊文件类型,以及包含文件访问时间戳的元数据. 文件系统缓存:主存(通常是DRAM) 的一块区域,用来缓存文

spark性能调优之资源调优

转https://tech.meituan.com/spark-tuning-basic.html spark作业原理 使用spark-submit提交一个Spark作业之后,这个作业就会启动一个对应的Driver进程.根据你使用的部署模式(deploy-mode)不同,Driver进程可能在本地启动,也可能在集群中某个工作节点上启动.Driver进程本身会根据我们设置的参数,占有一定数量的内存和CPU core.而Driver进程要做的第一件事情,就是向集群管理器(可以是Spark Stand

CPU性能压测

有时候为了项目需求需要对CPU性能做一个压力测试,这里提供一种方法.通过对圆周率位数进行计算进而确定CPU性能,根据定义预计执行时间,具体操作如下: time echo "scale=1000; 4*a(1)" | bc -l -q 通过该命令运行,如果3.4分钟没有出现结果,基本问题就可以定位在CPU上,这里我通过自己的测试机,得出如下数字: "scale=1000; 4*a(1)"这个表达式具体什么意思我没看明白,但是大概意思应该是将该表达式的交给计算器bc来处

Linux 性能监控 : CPU 、Memory 、 IO 、Network

一.CPU 1.良好状态指标 CPU利用率:User Time <= 70%,System Time <= 35%,User Time + System Time <= 70% 上下文切换:与CPU利用率相关联,如果CPU利用率状态良好,大量的上下文切换也是可以接受的 可运行队列:每个处理器的可运行队列<=3个线程 2.监控工具 vmstat $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- --s