SQL Server中的临时表和表变量

在SQL Server的性能调优中,有一个不可比拟的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择。

  在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 Server临时表和表变量

 今天在我和一家软件公司的开发人员讨论数据库设计调优的时候又讨论到了表变量和临时表的问题,觉得这个问题确实是一个争议比较大的问题。

  其实从上次发表了表变量和临时表的一个帖子http://database.ctocio.com.cn/tips/442/8206442.shtml以来,也有些人留言,也有些人发过邮件讨论这个问题。其实表变量和临时表的区别虽然有一些,但是两者最根本的区别还是在于

  对存储的需求:表变量和临时表都消耗Tempdb中的存储空间,但是进行数据更新的时候,表变量不会写日志,而临时表则会写日志。(这一点是经过脚本测试的,表变量并不像我们想象的那样,只写在内存而不出现在Tempdb中。)

  对优化的支持:表变量不支持索引和统计数据,临时表则可以支持索引和统计数据。

  通常需要表变量或者临时表的情况都是一些需要支持临时计算结果集的地方,那么就有一些常见的情况了:

  如果临时结果集仅仅需要往里面写数据,比如通过一个循环多次查找相关数据并合成一个临时结果集,那么就可以使用表变量。(结果有人提到了返回结果集的时候需要有排序,但是表变量不支持索引阿。其实这个不要紧,因为表变量虽然不支持索引,但是表变量支持主键阿,所以可以利用主键来替代索引。)

  如果临时结果集不太多需要更改,而是更多地充当一个临时的关联数据集去参加各种数据集的连接(JOIN),那么索引和统计数据可能会更加适合一些(当然这个临时结果集要足够大,这样索引和统计数据带来的代价才可以被弥补掉)。

  由于表变量不支持统计数据,因此在一个存储过程中使用表变量可以减少由于数据变化而导致的重新编译问题。

  当然,除了索引和统计数据这个明显的限制外,表变量同时也不支持并行执行计划,因此对于大型的临时结果集,表变量也不是一个好的选择。

  前面一个关于表变量和临时表的贴子,有一位robi_xu的朋友提到的问题也确实是在选择表变量和临时表时候的一些问题。

  对于函数中不能支持临时表是由于函数不能对函数作用域外部的资源状态造成永久性的更改,在SQLServer中也称为副作用(sideeffect)。不过如果在函数中使用大型的临时结果集是不推荐的,因为如果将这样的函数放置到一个查询中会造成很明显的性能问题,因此这种情况一般都采用存储过程之类的批处理脚本。

  对于动态脚本不支持表变量的原因是因为存储过程不接受表类型的参数。不过如果表变量的声明和赋值都在sp_executesql的参数中的话,sp_executesql就可以执行了,因为这个时候表变量就存在sp_executesql的stmt参数里面,不需要传入,例如下面的代码:(当然这样的实用性也就没有多少了)

  DECLARE @m nvarchar(max)

  SET @m = N"DECLARE @t TABLE (ID int);INSERT INTO @tVALUES(1);SELECT * FROM @t T"

  EXEC sp_executesql @m

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值