SQL Server 性能优化之——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
期待下一篇吧!
SQL Server 性能优化之——T-SQL 临时表、表变量、UNION的更多相关文章
- 【SQL Server性能优化】运用SQL Server的全文检索来提高模糊匹配的效率
原文:[SQL Server性能优化]运用SQL Server的全文检索来提高模糊匹配的效率 今天去面试,这个公司的业务需要模糊查询数据,之前他们通过mongodb来存储数据,但他们说会有丢数据的问题 ...
- SQL Server性能优化(8)堆表结构介绍
一.表结构综述 下图是SQL Server中表的组织形式(其中分区1.分区2是为了便于管理,把表进行分区,放到不同的硬盘数据文件里.默认情况下,表只有一个分区.).表在硬盘上的存放形式,有堆和B树两种 ...
- SQL SERVER性能优化综述
SQL SERVER性能优化综述 一个系统的性能的提高,不单单是试运行或者维护阶段的性能调优的任务,也不单单是开发阶段的事情,而是在整个软件生命周期都需要注意,进行有效工作才能达到的.所以我希望按照软 ...
- SQL Server 性能优化(一)——简介
原文:SQL Server 性能优化(一)--简介 一.性能优化的理由: 听起来有点多余,但是还是详细说一下: 1.节省成本:这里的成本不一定是钱,但是基本上可以变相认为是节省钱.性能上去了,本来要投 ...
- SQL Server 性能优化之——系统化方法提高性能
SQL Server 性能优化之——系统化方法提高性能 阅读导航 1. 概述 2. 规范逻辑数据库设计 3. 使用高效索引设计 4. 使用高效的查询设计 5. 使用技术分析低性能 6. 总结 1. 概 ...
- SQL Server性能优化与管理的艺术 附件下载地址
首先感谢读者们对鄙人的支持,购买了<SQL Server性能优化与管理的艺术>,由于之前出版社的一些疏忽,附件没有上传成功,再次本人深表歉意. 请需要下载附件的读者从下面链接下载,谢谢: ...
- SQL Server性能优化(6)查询语句建议
1. 如果对数据不是工业级的访问(允许脏读),在select里添加 with(nolock) ID FROM Measure_heat WITH (nolock) 2. 限制结果集的数据量,如使用TO ...
- SQL Server 性能优化之RML Utilities:快速入门(Quick Start)(1)
SQL Server 性能优化之RML Utilities:快速入门(Quick Start)(1) 安装Quick Start工具 RML(Replay Markup Language)是MS ...
- 【SQL Server性能优化】删除大量数据的方法比较
原文:[SQL Server性能优化]删除大量数据的方法比较 如果你要删除表中的大量数据,这个大量一般是指删除大于10%的记录,那么如何删除,效率才会比较高呢? 而如何删除才会对系统的影响相对较小呢? ...
- SQL Server 性能优化详解
故事开篇:你和你的团队经过不懈努力,终于使网站成功上线,刚开始时,注册用户较少,网站性能表现不错,但随着注册用户的增多,访问速度开始变慢,一些用户开始发来邮件表示抗议,事情变得越来越糟,为了留住用户, ...
随机推荐
- Mac下U盘安装系统“未验证的错误”
bash下 输入下面命令: date 1220141012015.30
- 从多列的DataTable里取需要的几列(转)
方法一: 也是广为人知的一种: YourDataTable.Columns.Remove("列名"); 但是这种情况只适合于去掉很少列的情况. 如果有很多列我却只要一两列呢,那就得 ...
- 工具fiddler学习
1:Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1, 端口:8888. 当Fiddler会自动设置代理.能支持HTTP代理的任意程序的数据包都能被Fiddler嗅 ...
- 无法打开注册表项 unknown 没有足够的权限访问
secedit /configure /cfg %windir%\inf\defltbase.inf /db defltbase.sdb /verbose 执行完,重新安装即可.
- 总结一下使用react-native的一些入门级重要点
经过多方的协调沟通,我们决定至4.1日起,停止向新用户透出该服务:今年10.15以后,老用户也停止该服务(具体表现是网站上传APK-Android或者URL-iOS入口会下掉,已经发版的App,SDK ...
- loadrunner监控linux服务器
参考http://www.cnblogs.com/yangxia-test/archive/2012/11/27/2790771.html http://www.cnblogs.com/candle8 ...
- 将一个数组分成奇数部分和偶数部分,并分别排好序 CVTE
给定一个数组,将奇数放到前面,偶数放到后面,各自排好序 (2016年3月12日晚上,CVTE笔试编程第一道题): 思路很简单: (1)先将数组中的奇数和偶数分开(利用两个指针遍历一遍即可,同时统计好数 ...
- HTML与CSS基础知识补遗(一)
开始从零基础系统地学习前端知识了,虽说html和css多少了解一些,但是学着还是能发现很多新大陆.... 一. HTML中head标签 1. <meta>标签: meta标签里是一些基础的 ...
- [UCSD白板题] Points and Segments
Problem Introduction The goal in this problem is given a set of segments on a line and a set of poin ...
- 在Ubuntu下配置舒服的Python开发环境
Ubuntu 提供了一个良好的 Python 开发环境,但如果想使我们的开发效率最大化,还需要进行很多定制化的安装和配置.下面的是我们团队开发人员推荐的一个安装和配置步骤,基于 Ubuntu 12.0 ...