Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1268546
  • 博文数量: 510
  • 博客积分: 20296
  • 博客等级: 上将
  • 技术积分: 4680
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-30 03:58
文章存档

2011年(13)

2010年(92)

2009年(242)

2008年(163)

我的朋友

分类: 数据库开发技术

2009-04-01 20:44:11

上篇文章,我们对tempdb系统数据库有了一个新的认识,现在我们来慢慢对其进行深入的了解和掌握。

 

    首先,先介绍“tempdb的空间使用”,有下面几种类型的对象会占用tempdb的空间:

 

    .   内部对象

    .   版本存储

    .   用户对象

 

    内部对象

 

       内部对象是指由SQL Server系统内部自动创建的一些系统级别的对象,应用程序不能直接的从这些对象删除或者插入数据,系统对象的元数据存储在内存里面,并且不能通过象“sys.all_objects”这样的系统目录来进行查询,因此,我们可认为内部对象是一些隐藏对象。通常系统对象被用于:

 

    .   存储排序的中间临时结果集。

    .   存储HASH连接以及HASH聚合的中间结果集。

    .   存储XML变量或者象LOB这样的大对象类型变量。LOB数据类型包括:text, image, ntext, varchar(max), varbinary(max)以及其他。

         .        存储会利用到Spool运算符(CTE查询)保存的中间结果集。

         .        存储键集驱动游标生成的工作表。

         .        通过静态游标存储查询结果。

         .        通过ServiceBroker存储传输中的消息。

         .        通过INSTEAD OF触发器存储数据以及内部的处理。

 

                   内部对象还可以用于下面这些的任何一个功能。比如,DBCC CHECK内部使用的查询需要利用到Spool运算符生成中间结果集,查询通知,以及事件通知,ServiceBroker,因此它们同样需要利用到tempdb的空间。

 

                   更新内部对象不会产生日志纪录。除非涉及排序单位,否则内部对象上页分配是不会生成日志纪录的。如果语句失败,这些对象将会被释放。每个tempdb上的内部对象至少占用9个页(包括1IAM页和8个数据页的区)。

 

 

版本存储

 

       版本存储用于存储新特性中通过事务产生的行版本。例如快照隔离级别,触发器,MARS(多个活动的结果集)以及联机索引创建。在SQL Server 2005中,有两个版本的存储区。关于更多的相关信息,请参见联机帮助:

 

 

        版本存储由append-only存储单位构成。对于连续的插入和随机的查找,append-only存储单位是非常高效的。它们不会显示在系统目录,比如sys.all_objects。插入版本存储不会产生日志纪录。每个单位可以存储很多行的版本。如果有新的版本需要被存储,大约每1分钟就有1个新的存储单位被创建。

        由于版本存储涉及的内容比较多且复杂,在这里就不再一一论述,更多内容,请网友们自行参考联机丛书关于“版本存储”的章节。

 

 

用户对象

 

     可以在系统目录sys.all_objects找到用户对象。Sp_spaceused可以显示这些对象占用的空间。用户对象包括用户定义的表和索引以及系统表和索引。这两种类型的表在磁盘上的数据结构都完全一样。可以通过T-SQL语句对用户表进行操作,不能通过T-SQL语句直接的对系统表进行修改,它们对于使用系统目录是可见的。通常用户对象上的操作是可以被纪录日志的,包括BCPBULK INSERTSELECT INTO以及索引重建操作,这些行为和其他的简单恢复模型的数据库的行为是完全一样的。

     

      用户定义的表包括全局临时表例如##t,以及局部临时表例如#t.全局临时表会在其依赖的整个会话过期或者中止以后释放,而局部临时表只存在于其依赖的某种作用域。在另外一方面,也会伴随一个作用域的结束而被释放。(例如存储过程)局部临时表包括表变量@t,表值函数的返回值,以及那些使用了带有sort_in_tempdb选项的联机聚集索引创建所需要的Mapping索引。

 

  对于临时表的作用域,网友们经常遇到下面的这些问题,顺便在这里提出来:

 

  1

 

     我们先看下面的代码:

 

        use tempdb

go

 

create proc p1

as

set nocount on

select top 1 * into #t from sys.objects

select * from #t

go

 

我们创建了个存储过程,先通过系统表插入1行纪录生成一个局部临时表#t,然后再通过SELECT语句查询这个临时表。

然后,我们创建完以后,通过执行:

 

exec p1

go

 

我们可以看到结果集,但如果,我们再次通过SELECT  查询语句对#t进行查询:

 

select * from #t

go

 

我们将会看到下面的错误信息,这是因为在存储过程的作用域完之后,这个局部临时表对象也随之被销毁了。

Msg 208, Level 16, State 0, Line 1

Invalid object name '#t'.

 

 

如果想在执行完存储过程以后,还可以进行SELECT查询,把局部临时表替换为全局临时表就可以了。

 

 

2

 

    还是利用上面提到的存储过程p1

 

    create proc p1

as

set nocount on

 

select top 1 * into #t from sys.objects

select * from #t

go

 

突然某天,需求改变,创建临时表的源表需要改变或者需要适应可变的需求,那么我们肯定会考虑传入一个表名参数,使用动态语句来实现,代码如下:

 

alter proc p1

@table_name varchar(100)

as

set nocount on

 

declare @sql varchar(200)

 

set @sql = 'select top 1 * into #t from '+@table_name

exec(@sql)

select * from #t

go

 

 

然后我们执行存储过程:

 

exec p1 @table_name='sys.objects'

 

我们并未得到我们想要的结果,而是一个错误信息:

 

Msg 208, Level 16, State 0, Procedure p1, Line 9

Invalid object name '#t'.

 

这也是因为,这里的局部临时表只存在于动态语句内的作用域,对于这样的需求我们可以通过下面两个方式来解决:

 

第一种方式是通过将查询语句一并包含到动态语句里,代码如下:

 

alter proc p1

@table_name varchar(100)

as

set nocount on

 

declare @sql varchar(200)

 

set @sql = 'select top 1 * into #t from '+@table_name + '

select * from #t'

exec(@sql)

go

 

第二种方式则是将局部临时表修改为全局临时表##t

 

3

 

    我们来考虑全局临时表的作用域,我们做个测试,这个测试还是利用到最开始的那个存储过程P1,只不过代码我们把局部临时表调整为了全局临时表:

 

        alter proc p1

as

set nocount on

 

select top 1 * into ##t from sys.objects

select * from ##t

go

 

 

我们创建完这个存储过程以后,先在查询窗口1执行1次这个存储过程,那么按照我们之前说的,则会创建一个全局的的临时表,我们可以在执行完存储过程以后,依然可以使用SELECT语句对其进行查询。

然后我们试着重新开启第2,第3……查询窗口,同样执行语句查询:

 

select * from ##t

 

我们依然可以看到我们想要的结果集。我们再次把查询窗口1关闭,然后再次在窗口23……执行上面这个查询代码,结果出错了。

 

通过这个测试,说明了什么,大家都已经很清楚了。

 

 

最后,谢谢各位看完这篇文章,下一次,我将介绍SQL Server 2005tempdb的改进以及tempdb的磁盘空间需求,欢迎各位捧场,谢谢大家。

阅读(735) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~