SQL Server中的临时表和表变量 Declare @Tablename Table

在SQL
Server的性能调优中,有一个不可比面的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择。记得在给一家国内首屈一指的海运公司作SQL
Server应用性能评估和调优的时候就看到过大量的临时数据集处理需求,而他们的开发人员就无法确定什么时候用临时表,什么时候用表变量,因此他们就简单的使用了临时表。实际上临时表和表变量都有特定的适用环境。

先卖弄一些基础的知识:

表变量
变量都以@或@@为前缀,表变量是变量的一种,另外一种变量被称为标量(可以理解为标准变量,就是标准数据类型的变量,例如整型int或者日期型DateTime)。以@前缀的表变量是本地的,因此只有在当前用户会话中才可以访问,而@@前缀的表变量是全局的,通常都是系统变量,比如说@@error代表最近的一个T-SQL语句的报错号。当然因为表变量首先是个变量,因此它只能在一个Batch中生存,也就是我们所说的边界,超出了这个边界,表变量也就消亡了。

表变量存放在内存中,正是因为这一点所有用户访问表变量的时候SQL
Server是不需要生成日志。同时变量是不需要考虑其他会话访问的问题,因此也不需要锁机制,对于非常繁忙的系统来说,避免锁的使用可以减少一部分系统负载。

表变量另外还有一个限制就是不能创建索引,当然也不存在统计数据的问题,因此在用户访问表变量的时候也就不存在执行计划选择的问题了(也就是以为着编译阶段后就没有优化阶段了),这一特性有的时候是件好事,而有些时候却会造成一些麻烦。

临时表
临时对象都以#或##为前缀,临时表是临时对象的一种,还有例如临时存储过程、临时函数之类的临时对象,临时对象都存储在tempdb中。以#前缀的临时表为本地的,因此只有在当前用户会话中才可以访问,而##前缀的临时表是全局的,因此所有用户会话都可以访问。临时表以会话为边界,只要创建临时表的会话没有结束,临时表就会持续存在,当然用户在会话中可以通过DROP
TABLE命令提前销毁临时表。

我们前面说过临时表存储在tempdb中,因此临时表的访问是有可能造成物理IO的,当然在修改时也需要生成日志来确保一致性,同时锁机制也是不可缺少的。

跟表变量另外一个显著去别就是临时表可以创建索引,也可以定义统计数据,因此SQL
Server在处理访问临时表的语句时需要考虑执行计划优化的问题。

表变量 vs.
临时表



结论
综上所述,大家会发现临时表和表变量在底层处理机制上是有很多差别的。

简单地总结,我们对于较小的临时计算用数据集推荐使用表变量。如果数据集比较大,如果在代码中用于临时计算,同时这种临时使用永远都是简单的全数据集扫描而不需要考虑什么优化,比如说没有分组或分组很少的聚合(比如说COUNT、SUM、AVERAGE、MAX等),也可以考虑使用表变量。使用表变量另外一个考虑因素是应用环境的内存压力,如果代码的运行实例很多,就要特别注意内存变量对内存的消耗。

一般对于大的数据集我们推荐使用临时表,同时创建索引,或者通过SQL
Server的统计数据(Statisitcs)自动创建和维护功能来提供访问SQL语句的优化。如果需要在多个用户会话间交换数据,当然临时表就是唯一的选择了。需要提及的是,由于临时表存放在tempdb中,因此要注意tempdb的调优。

SQL中的临时表和表变量

我们经常使用临时表和表变量,那现在我们就对临时表和表变量进行一下讨论.

临时表 

局部临时表 

全局临时表 

表变量 

                               

临时表 

临时表存储在TempDB数据库中,所有的使用此SQL
Server
实例的用户都共享这个TempDB,因为我们应该确保用来存储TempDB数据库的硬盘有足够的空间,以使之能够自己的增长.最好能够存储在一个拥有独立硬盘控制器上.因为这样不存在和其它的硬盘I/O进行争用. 

我们很多程序员认为临时表非常危险,因为临时表有可能被多个连接所共享.其实在SQL
Server中存在两种临时表:局部临时表和全局临时表,局部临时表(Local temp
table)以#前缀来标识,并且只能被创建它的连接所使用.全局临时表(Global temp
table)以##前缀来进行标识,并且可以和其它连接所共享. 

局部临时表 

局部临时表不能够被其它连接所共享的原因其实是在SQL
Server
2000中自动为局部临时表的表名后面加上了一个唯一字符来标识.如: 

     
CREATE TABLE [#DimCustomer_test] 

     


         [CustomerKey]
[int] 

        
,    [FirstName]
[nvarchar](50)   

     ,[MiddleName]
[nvarchar](50)   

     ,[LastName]
[nvarchar](50) 

        


现在我们来查看一下TempDB中
sysobjects表,我们会发现我们新创建的临时表#DimCustomer_test已经被加上了后缀: 

  USE
TempDB 

   GO 

   SELECT name FROM
sysobjects WHERE name LIKE ’%DimCustomer%’ 

the Result
is: 

name 

#DimCustomer_test___________________________________________________________________________________________________000000000005 

全局临时表 

下面我们来看一下全局临时表: 

     
CREATE TABLE [##DimCustomer_test] 

     


         [CustomerKey]
[int] 

        
,       [FirstName]
[nvarchar](50)   

     ,[MiddleName]
[nvarchar](50)   

     ,[LastName]
[nvarchar](50) 

        


现在我们来查看一下TempDB中
sysobjects表,我们会发现我们新创建的临时表##DimCustomer_test没有被加上了后缀: 

  USE
TempDB 

   GO 

   SELECT name FROM
sysobjects WHERE name LIKE ’%DimCustomer%’ 

The Result
are: 

#DimCustomer_test___________________________________________________________________________________________________000000000005 

##DimCustomer_test 

--Drop
test temp
tables 

                              
DROP TABLE
[##DimCustomer_test] 

                              
DROP TABLE
[#DimCustomer_test] 

可以看到我们刚才创建的全局临时表名字并没有被加上标识. 

表变量 

表变量和临时表针对我们使用人员来说并没有什么不同,但是在存储方面来说,他们是不同的,表变量存储在内存中.所以在性能上和临时表相比会更好些! 

另一个不同的地方是在表连接中使用表变量时,要为此表变量指定别名.如: 

  USE
AdventureWorksDW 

   GO 

  
DECLARE @DimCustomer_test TABLE 

  


      [CustomerKey]
[int] 

     
,       [FirstName]
[nvarchar](50)   

,[MiddleName]
[nvarchar](50)   

,[LastName]
[nvarchar](50) 

     


   ---insert data to
@DimCustomer_test 

   INSERT
@DimCustomer_test 

  


     
[CustomerKey]   

     
,      
[FirstName]   

,[MiddleName]   

,[LastName] 

     


  
SELECT   

     
[CustomerKey]   

     
,      
[FirstName]   

,[MiddleName]   

,[LastName] 

  
FROM DimCustomer 

   SELECT
[@DimCustomer_test].CustomerKey,SUM(FactInternetSales.OrderQuantity) 

FROM
@DimCustomer_test   INNER JOIN FactInternetSales   
ON 

@DimCustomer_test.CustomerKey =
FactInternetSales.CustomerKey 

Group BY
CustomerKey 

Result: 

Server: Msg 137,
Level 15, State 2, Line 32 

Must declare the variable
’@DimCustomer_test’. 

如果我们对上面的查询进行更改,对查询使用别名(并且找开IO): 

-----in
the follow script,we used the table alias. 

DECLARE
@DimCustomer_test TABLE 



    
[CustomerKey] [int] 

    
,       [FirstName]
[nvarchar](50)   

,[MiddleName]
[nvarchar](50)   

,[LastName]
[nvarchar](50) 

    


INSERT
@DimCustomer_test 



    
[CustomerKey]   

    
,      
[FirstName]   

,[MiddleName]   

,[LastName] 

    


SELECT   

    
[CustomerKey]   

    
,      
[FirstName]   

,[MiddleName]   

,[LastName] 

FROM
DimCustomer 

SELECT
t.CustomerKey,f.OrderQuantity 

FROM @DimCustomer_test t INNER JOIN
FactInternetSales   f ON 

t.CustomerKey =
f.CustomerKey 

where
t.CustomerKey=13513 

表变量在批处理结束时自动被系统删除,所以你不必要像使用临时表表一样显示的对它进行删除. 

----------------------------------------

另外在今天帮同事Tuning
SQL 脚本地时候,发现对于大数据量表的查询(10w-100W),用变量的方式比用select
的方式居然执行时间减少了100倍!!似懂非懂,但从来没有想到差别如此大,惊讶ing,记录一笔,研究一下

M1:
declare
@tempID int

set @tempID =(select lots_id from qs_notes where
id=‘CVT20080321‘) 

select * from ls_Qs_notes where id =
@tempID

---返回记录998,行执行时间6589

M2:

select * from
ls_Qs_notes where id =(select lots_id from qs_notes where
id=‘CVT20080321‘) 

---返回记录998 ,行执行时间60

时间: 2024-12-28 20:27:30

SQL Server中的临时表和表变量 Declare @Tablename Table的相关文章

SQL Server中的临时表和表变量

在SQL Server的性能调优中,有一个不可比拟的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择.记 得在给一家国内首屈一指的海运公司作SQL Server应用性能评估和调优的时候就看到过大量的临时数据集处理需求,而他们的开发人员就无法确定什么时候用临时表,什么时候用表变量,因此他们就简 单的使用了临时表.实际上临时表和表变量都有特定的适用环境. 先卖弄一些基础的知识: 表变量 变量都以@或@@为前缀,表变量是变量的一种,另外一种变量被称为标

SQL Server 中VARCHAR(MAX)变量赋值引起的性能问题。

案例环境: 操作系统版本 : Windows Server 2008 R2 Standard  SP1 数据库版本   :  Microsoft SQL Server 2012 (SP1) - 11.0.3000.0 (X64) 案例介绍: 由于不能将生产环境的代码和数据贴上来,所以我构造了下面一个小案例,当然没法和生产环境的案例一致.只能是接近而已.但是足以反映问题本质就足够了. DROP TABLE ProductPrice;   GO   CREATE TABLE ProductPrice

SQL server中使用临时表存储数据

将查询出来的数据直接用“INTO #临时表名称”的方式完成临时表的创建及数据的插入 SELECT * INTO #temp_NowStatusFROM Test SELECT * FROM #temp_NowStatus --查询临时表中的数据truncate table #temp_NowStatus --清除临时表中的数据--删除临时表if object_id('tempdb..#temp_NowStatus') is not null BEGIN drop table #temp_NowS

InMemory:在内存中创建临时表和表变量

在Disk-Base数据库中,如果系统频繁地创建和更新临时表,大量的IO操作集中在tempdb中,tempdb很可能成为系统性能的瓶颈.在SQL Server 2016的内存(Memory-Optimized)数据库中,如果考虑使用内存优化结构来存储临时表,表变量,表值参数的数据,那么将完全消除IO操作的负载消耗,发挥大内存的优势,大幅提高数据库的性能. 在SQL Server 2016中,能够直接创建内存优化的表类型,表变量和表值参数的数据只存储在内存中:不能直接在内存中创建临时表,但是,SQ

MSSQL之二 Sql Server中管理库与表

作为数据库开发人员,你负责创建和管理数据库和表.当创建表的时候,维护数据的完整性对你是很重要的.为确保表中的数据是准确的,一致的和可靠的,SQL Server提供了各种你可以应用到表上以增强数据完整性的检查. SQL Server包含各种系统数据库.本章介绍不同类型的系统数据库并且解释如何管理用户定义的数据库和管理用户自定义表. 重点 ?      管理数据库 ?      管理表 ?      SQL Server 2008中的数据类型 预习功课 ?        创建数据库 ?       

实现SQL Server中的切割字符串SplitString函数,返回Table

有时我们要用到批量操作时都会对字符串进行拆分,可是SQL Server中却没有自带Split函数,所以要自己来实现了. -- ============================================= -- Author: chenlong -- Create date: 2015-02-02 -- Description: 根据逗号分隔拆分字符串,返回table -- ============================================= ALTER FUN

Sql Server中清空所有数据表中的记录

清空所有数据表中的记录: 代码如下:exec sp_msforeachtable  @Command1 ='truncate table ?'删除所有数据表: 代码如下:exec sp_msforeachtable 'delete   N''?'''清空SQL Server数据库中所有表数据的方法(有约束的情况) 其实删除数据库中数据的方法并不复杂,为什么我还要多此一举呢,一是我这里介绍的是删除数据库的所有数据,因为数据之间可能形成相互约束关系,删除操作可能陷入死循环,二是这里使用了微软未正式公

SQL Server中遍历表中记录的方法

遍历表有下面几种方法 1.使用游标 2.使用表变量 3.使用临时表 下面通过一个实例分别介绍三中方法的实现: 1.需求 给HR.Employees表,fullname 列赋值,其值为 firstname+lastname 为了演示表的遍历,忽略 UPDATE HR.Employees SET fullname= firstname+' '+lastname; 的实现方式 2.使用游标 使用游标的代码主要有以下几个步骤,声明游标,打开游标,使用游标,关闭游标和释放游标. -- 方法1:游标 -- 

SQL Server中 SET 和 SELECT 赋值有什么区别?

SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT.对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们并没有注意,其实这两种方式还是有很多差别的. SQL Server推荐使用 SET 而不是 SELECT 对变量进行赋值.当表达式返回一个值并对一个变量进行赋值时,推荐使用 SET 方法. 下表列出 SET 与 SELECT 的区别.请特别注意红色部分.   set select 同时对多个变量同时赋值 不支持 支持 表