SQL Server 性能优化之——T-SQL 临时表、表变量、UNION

 

此次看一下临时表,表变量和Union命令方面是否能够被优化呢?html

阅读导航数据库

1、临时表和表变量express

2、本次的另外一个重头戏UNION 命令网络

1、临时表和表变量架构

不少数据库开发者使用临时表和表变量将代码分解成小块代码来简化复杂的逻辑。可是使用这个的后果就是可能带来性能的损害性能

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在索引不足的临时数据库上等待结果,若是临时表有汇集索引和非汇集索引,这样的现象能够被减缓。spa

所以,最好有限的使用临时表。code

在必须使用临时表的状况下,能够参照一下预防措施:orm

  • 使用临时表(create table #Temp)而不是使用表变量(Declare @table table),这样作的缘由是能够在临时表上使用索引。
  • 使用临时表时,用小型数据量的小表来限制性能影响。
  • 若是临时表中使用inner join , group by , order by 或 where,要确保临时表有汇集索引或非汇集索引。

那么,采用什么办法避免使用临时表和表变量呢?

  1. CTE表达式(Common Table Expression, CTE
  2. 子查询
  3. 在数据库架构中建立物理表,而不是在历史数据库中建立临时表。
  4. 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

 

2、本次的另外一个重头戏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

期待下一篇吧!

相关文章
相关标签/搜索