T-SQL 临时表、表变量、UNION
T-SQL 临时表、表变量、UNION
这次看一下临时表,表变量和Union命令方面是否可以被优化呢?
阅读导航
一、临时表和表变量
很多数据库开发者使用临时表和表变量将代码分解成小块代码来简化复杂的逻辑。但是使用这个的后果就是可能带来性能的损害
1. 对I/O子系统的影响 (存储区域网络SAN 或逻辑存储),这是由于增加了页和页I/O闩锁等待,这样等待被认为是最差的等待,这也可能会增加临时数据库的密集竞争进而导致高分配请求,最后可能出现全局分配映射页(GAM)、共享全局映射页(SGAM)或可用空间(PFS)瘫痪。
- 全局分配映射页(Global Allocation Map, GAM)用于跟踪区的使用情况,每个GAM页可以跟踪64000个区或者说4GB的数据。在GAM页中,如果某个位值为0,则表示它所对应的区已经分配给了某个对象使用,值为1时表示这个区是空闲的。
- 共享全局分配映射页(Shared Global Allocation Map, SGAM)功能和GAM是一样的,所不同的就是SGAM是用来管理混合区的。不过它的位图映射关系正好是相反的:在GAM中设置为1的,在SGAM中设置为0——用于代表一个空闲的区。
- 页可用空间(Page Free Space, PFS),这种页记录了某个页是否分配给了某个对象,并且记录这个页上有多少可用的空间,位图映射值可显示一个页的使用率是50%、85%、95%或是95%以上。SQL Server根据这个信息来决定是否要给一行数据分配新的空间
2. 影响CPU利用率,这是由于Cxpacket在索引不足的临时数据库上等待结果,如果临时表有聚集索引和非聚集索引,这样的现象可以被减缓。
因此,最好有限的使用临时表。
在必须使用临时表的情况下,可以参照一下预防措施:
- 使用临时表(create table #Temp)而不是使用表变量(Declare @table table),这样做的原因是可以在临时表上使用索引。
- 使用临时表时,用小型数据量的小表来限制性能影响。
- 如果临时表中使用inner join , group by , order by 或 where,要确保临时表有聚集索引或非聚集索引。
那么,采用什么办法避免使用临时表和表变量呢?
- CTE表达式(Common Table Expression, CTE)
- 子查询
- 在数据库架构中创建物理表,而不是在历史数据库中创建临时表。
- SQL Server 2008以后,表参数是可以用的。
例子 :
首先,在新数据库MyDemo中创建新表
1: --创建新表
2: use MyDemo
3: CREATE TABLE [dbo].[Employees](
4: [empid] [int] IDENTITY(1,1) NOT NULL,
5: [empname] [nvarchar](100) NULL,
6: [deptid] [int] NULL,
7: [Salary] [float] NULL,
8: CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED
9: ( [empid] ASC )
10: WITH
11: (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
12: ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
13: ) ON [PRIMARY]
14: GO
15: CREATE TABLE [dbo].[Departments](
16: [deptid] [int] IDENTITY(1,1) NOT NULL,
17: [deptname] [nchar](10) NULL,
18: CONSTRAINT [PK_Departments] PRIMARY KEY CLUSTERED
19: ( [deptid] ASC )
20: WITH
21: (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
22: IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] )
23: ON [PRIMARY]
24: GO
使用表变量:
1: alter procedure Performance_Issue_Table_Variables
2: as
3: begin
4: SET NOCOUNT ON;
5: declare @table table(empid int, empname varchar (25),Department varchar (25) ,Salary int)
6: insert into @table select S.empid,S.empname,T.deptname,S.salary from Employees s inner join Departments T ON S.deptid =T.deptid
7: SELECT COUNT (empid) ,Department,Salary FROM @table GROUP BY Department,Salary HAVING Salary>2000
8: end
使用临时表:
1: Create procedure Performance_Issue_Table_Variables
2: as
3: begin
4: SET NOCOUNT ON;
5: create table #table (empid int, empname varchar (25),Department varchar (25) ,Salary int)
6: create clustered index #table_index1 on #table (empid asc )
7: create nonclustered index #table_index2 on #table (Salary) include (Department,empid )
8: insert into #table select S.empid,S.empname,T.deptname,S.salary from Employees s
9: inner join Departments T ON S.deptid =T.deptid
10: SELECT COUNT (empid) ,Department,Salary FROM #table GROUP BY Department,Salary HAVING Salary>2000
11: DROP TABLE #table
12: end
使用CTE表达式:
1: Create procedure Performance_Solution_CTEexpression
2: as
3: begin
4: SET NOCOUNT ON;
5: With temp as
6: (
7: select S.empid,S.empname,T.deptname as Department,S.salary from Employees s inner
8: join Departments T ON S.deptid =T.deptid
9: )
10: SELECT COUNT (empid) ,Department,Salary FROM temp GROUP BY Department,Salary HAVING Salary>2000
11: end
使用表参数
表参数可通过三个步骤实现
第一,创建一个新的数据表:
1: create type Specialtable as table
2: (EmployeeID int NULL,
3: EmployeeName Nvarchar (50) Null )
接下来,创建存储过程,并接受这个表所谓参数输入:
1: create procedure Performance_Solution_Table_Paramters @Temptable Specialtable Readonly
2: as
3: begin
4: select * from @Temptable
5: end
6: Finally, execute the stored procedure :
7: declare @temptable_value specialtable
8: insert into @temptable_value select '1','Jone' union select '2', 'Bill'
9: exec dbo.SP_Results @temptable=@temptable_value
使用子查询
1: Create procedure Performance_Solution_SubQuery
2: as
3: begin
4: SET NOCOUNT ON;
5: SELECT COUNT (empid) ,S.Department,Salary FROM
6: (select S.empid,S.empname,T.deptname as Department,S.salary from Employees s inner join Departments T ON S.deptid =T.deptid) S
7: GROUP BY Department,Salary HAVING Salary>2000
8: end
使用物理表
1: create table schema_table (empid int, empname varchar (25),Department varchar (25) ,Salary int)
2: create clustered index schema_table_index1 on schema_table (empid asc )
3: create nonclustered index schema_table_index2 on schema_table (Salary) include (Department,empid )
4: insert into schema_table select S.empid,S.empname,T.deptname,S.salary from Employees s inner join Departments T ON S.deptid =T.deptid
5: go
6: Create procedure Performance_Solution_PhysicalTables
7: as
8: begin
9: SET NOCOUNT ON;
10: SELECT COUNT (empid) ,Department,Salary FROM schema_table GROUP BY Department,Salary HAVING Salary>2000
11: end
二、本次的另一个重头戏UNION 命令
使用Union命令,和使用临时表一样,会影响I/O子系统(如,页和页I/O闩锁等待)。但是很多数据库开发者仍然使用Union命令处理复杂的业务逻辑。
选择/改善Union :
· 使用Case When 子句代替,它们可以做聚合和详细的查询
· 使用动态查询:用强大的sp_executesq来节省每次运行查询执行计划,节省时间消耗。存储过程中使用If Else 语句决定查询语句适合的一组参数,这样可以根据传入存储过程的参数控制Union的数量。
· 选择排序语句内使用Union,使用轻量级的选择查询减少重量级的选择查询消耗的页闩锁等待。
例子:
使用性能较差的Union命令:
1: create procedure Poor_Performing_UnionSP
2: as
3: begin
4: SET NOCOUNT ON;
5: select S.empid,S.empname,T.deptname,S.salary from Employees s inner join Departments T ON S.deptid =T.deptid WHERE T.deptid>1 and S.salary>5000
6: UNION
7: select S.empid,S.empname,'Management deparments' as deptname,S.salary from Employees s inner join Departments T ON S.deptid =T.deptid WHERE T.deptid=1 and S.salary >10000
8: end
使用Case When语句:
1: create procedure PerformantSP_Grid_Results_Using_CaseWhen
2: AS
3: BEGIN
4: select S.empid,S.empname,
5: case when T.deptid>1 and S.salary>5000 then T.deptname
6: when T.deptid=1 and S.salary>10000 then 'Management deparments' end as deptname
7: ,S.salary
8: from Employees s inner join Departments T ON S.deptid =T.deptid
9: END
10: GO
使用Union获得聚合结果:
1: create procedure Poor_Performing_Union_Aggregate_Results
2: as
3: begin
4: SET NOCOUNT ON;
5: select count (S.empid)as Employee_count,T.deptname,S.salary from Employees s
6: inner join Departments T
7: ON S.deptid =T.deptid WHERE T.deptid>1 and S.salary>10000 group by T.deptname,S.salary
8: end
使用Case When获得集合结果:
1: create procedure PerformantSP_Aggregate_Results_Using_CaseWhen
2: as
3: begin
4: SET NOCOUNT ON;
5: select sum (case when T.deptid>1 and S.salary>10000 then 1 else 0 end)
6: as Employee_count2
7: ,T.deptname,S.salary
8: from Employees s inner join Departments T ON S.deptid =T.deptid
9: group by T.deptname,S.salary
10: end
期待下一篇吧!
在此谢谢读完这篇博客,有什么写的不对的地方请指正
有帮助就推荐下,有感想就写下留言,不满意也留言,有问题就更正。
T-SQL 临时表、表变量、UNION的更多相关文章
- SQL Server 表变量和临时表的区别
SQL Server 表变量和临时表的区别 一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯 ...
- sql server 表变量、表类型、临时表
sql server 中临时表分为会话临时表和永久临时表.会话临时表在会话结束后自动被删除,永久临时表与基本表的使用上基本无差异,需要显示调用drop将其删除. 创建临时表 创建会话临时表 creat ...
- [转]SQL Server 表变量和临时表的区别
一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯一约束,NULL约束和CHECK约束(外键约 ...
- SQL SERVER表变量和临时表
一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯一约束,NULL约束和CHECK约束(外键约 ...
- sql临时表与变量表
1)临时表存储在 tempdb 中,当不再使用时会自动删除 一般使用如下: --创建临时表 select * into #temp from TABLE --使用临时表 select * from # ...
- sql server 表变量存储临时查询数据
对于使用sql server 编写存储过程或者类似的sql 查询的时候我们使用表变量进行临时数据的存储,可以方便我们进行下来的数据处理 表变量的使用类似如下: declare @userinfo ta ...
- sql创建表变量,转百分数
declare @tab table( ID nt identity(1,1) primary key, --从1开始,每次自增1 ,Name nvarchar(200) ) declare a fl ...
- SQL 表变量和临时表
SQL 表变量和临时表 表变量:存储在内存中,作用域是脚本的执行过程中,脚本执行完毕之后就会释放内存,适合短时间内存储数据量小的数据集. 优点:使用灵活,使用完之后立即释放,不占用物理存储空间 缺点: ...
- SQL Server中的临时表和表变量 Declare @Tablename Table
在SQL Server的性能调优中,有一个不可比面的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择.记得在给一家国内首屈一指的海运公司作SQL Se ...
- SQL Server中的临时表和表变量
SQL Server中的临时表和表变量 作者:DrillChina出处:blog2008-07-08 10:05 在SQL Server的性能调优中,有一个不可比拟的问题:那就是如何在一段需要长时间的 ...
随机推荐
- dozer-初识
1.简介 dozer是一种JavaBean的映射工具,类似于apache的BeanUtils.但是dozer更强大,它可以灵活的处理复杂类型之间的映射.不 但可以进行简单的属性映射.复杂的类型 ...
- Hibernat之关系的处理一对一处理
第一步:编写两个pojo,比如一个学生表一个班级表 这里使用注解. 需要 公司表: package com.qcf.pox; import javax.persistence.CascadeType ...
- unity3d插件Daikon Forge GUI 中文教程-1-Daikon Forge介绍
(游戏蛮牛首发)大家好我是孙广东官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上,要观看是须要FQ的. 只是教程 ...
- 安卓Monkey源码分析之运行流程
在<MonkeyRunner源码分析之与Android设备通讯方式>中,我们谈及到MonkeyRunner控制目标android设备有多种方法,其中之一就是在目标机器启动一个monkey服 ...
- Floodlight 在 ChannelPipeline 图
我们知道,在Netty架构,一个ServerBootstrap用于生成server端的Channel的时候都须要提供一个ChannelPipelineFactory类型的參数,用于服务于建立连接的Ch ...
- C#JSON序列化与反序列化
原文:C#JSON序列化与反序列化 windows phone学习也有一段时间了,想要做一个新闻客户端练练手,于是就在网上找看有没有接口之类.在天狗播客找到了热点热词新闻资讯API开放接口,接口提供的 ...
- 使用QT来制作串口终端
为什么要使用QT,因为它是跨平台的. 我现在使用的环境是Win7 64bit,使用VS的编译器来编译QT工程. 安装这套环境简单说一下:先到QT官网下载qt-windows-opensource-5. ...
- JS中通过call方法实现继承
原文:JS中通过call方法实现继承 讲解都写在注释里面了,有不对的地方请拍砖,谢谢! <html xmlns="http://www.w3.org/1999/xhtml"& ...
- [译]Java 设计模式之装饰器
(文章翻译自Java Design Pattern: Decorator – Decorate your girlfriend) 1.装饰模式的来历 让我们假设你在寻找一个女朋友.有来自像没美国中国日 ...
- QTP脚本不能录制怎么办?
QTP是基于VBS脚本语言的,大部分VBS脚本都能在QTP上运行,只是在一些细节上略有不同,比如说VBS上停止用sleep,QTP上用wait.QTP的强大之处在于对程序窗口的操作,有很多针对窗体的属 ...