15.2 游标的生命期
游标有很多小的组成部分,但是,最好先讨论游标最基本的形式,然后从这里开始逐步建立游标。
在进入到实际的语法之前必须明白,游标的使用需要不止一个语句——实际上,要用到几个语句。主要的部分有:
l 声明;
l 打开;
l 使用或导航;
l 关闭;
l 释放。
话虽如此,用来声明游标的基本语法如下所示:
DECLARE <游标名> CURSOR
FOR <SELECT 语句>
要记住,这是很简单的表达——尽可能使用默认值创建游标。在本章后面将看到更高级的游标。
游标名除了不需要“@”前缀外,与任何其他变量名一样,游标名必须服从SQL Server命名规范。SELECT语句可以是任何有效的返回结果集的SELECT语句。然而要注意,一些结果集是不可更新的。(例如,如果使用了GROUP BY,那么,要更新组中的哪一部分呢?由于几乎相同的原因,对于计算字段而言,同样是不可更新的。)
接下来,着手创建一个相当简单的例子。其实,现在我们还不会大量地使用它,但是,以后将会看到,它是管理索引的一种相当好用的工具:


注意,这只是将要创建的东西的一个开始。关于游标,第一件要注意的事情是,比起常见的SELECT语句来说,它们需要多得多的代码。
我们将基于一个选取数据库中所有表的SELECT语句来声明游标,该游标的名字是TableCursor。在使用游标时,还要声明一个包含当前行的值的变量。
只声明游标是不够的,我们还必须打开它:
![]()
实际上,这会执行作为FOR子句对象的查询,但是在我们能够使用的地方,依然没有任何东西。鉴于此,必须进行如下的两件事情:
l 获取(或FETCH)第一条记录;
l 有必要的话,循环,获取其余的记录。
我们发出第一个FETCH——这是一个表明要检索特定记录的命令。另外,还必须说明想要把值放置在哪一个变量中。
![]()
现在,有了第一条记录,接下来,我们可以执行游标集上的操作:

从本书前面对全局变量的简短讨论中,你或许还记得@@FETCH_STATUS。每当提取一行时,就会更新@@FETCH_STATUS以告知我们提取进行得如何。其可能的值是:
l 0 Fetch语句成功——一切正常;
l -1 Fetch语句失败——找不到记录(还没有到达游标的末尾,但自打开游标以后,记录已经被删除),很快将在本章后面做进一步的讨论;
l -2 Fetch语句失败——这一次是由于已经超出了游标中的最后一条(或者第一条)记录,同样,在本章的后面将更详细地讨论。
根据这里的目的可知,一旦退出循环,说明我们已经完成了对游标的使用,因此,关闭游标:
![]()
然而,关闭游标并没有释放与游标相关联的内存。关闭游标只是释放了与游标相关联的锁。为了确保完全释放游标使用的所有资源,必须释放游标:
![]()
那么,为了弄清楚前面讲述的内容,把它们集合在一起:


现在已经可以运行了,但是此刻,当完成对它的创建时,其实只不过是运行了SELECT语句本身而已(技术上讲,由于不能“PRINT”出SELECT语句,因此事实并非如此,不过,效果总的来说是一样的)。不同之处是,如果选择了这样做,那么能够针对每一行做几乎所有的事情。接下来完成我们的小工具,以此为例进行说明。
在过去,没有任何单独的语句能够重建整个数据库中所有的索引(幸运的是,现在,DBCC INDEXDEFRAG中有一个选项能够针对整个数据库来实现)。然而,对索引进行碎片整理是系统管理的核心部分。这里要使用的关于游标的例子,是完成这种索引碎片整理的常见方式的某种延续。尽管如此,在这一较新的版本中,使用了具体的碎片信息,并且,让ALTER INDEX的使用成为可能(在如何确切地完成碎片整理上有更多的选项),而非DBCC INDEXDEFRAG。
因此,在重建或重新组织索引上,我们有了一些不同的方法,而不必完全删除并重新创建索引。在整理碎片时,ALTER INDEX是最灵活的,它使你能够选择不同的整理碎片的底层方法(联机或脱机,完全重建或仅对这里的部分进行重新组织,等等),因此,接下来将运用这种完成任务的方法。关于ALTER INDEX的语法,最简单的版本如下:
ALTER INDEX <索引名> | ALL
ON <对象>
{[REBUILD] | [REORGANIZE]}
这是ALTER INDEX最简单的语法形式。在第8章中讲述过ALTER INDEX,它有大量琐碎的开关和选项。
试图使用这里的语句来重建所有表上的所有索引的问题在于,该语句是设计为一次针对一个表来进行的。如果想要创建一个表上所有的索引,可以使用ALL选项来代替索引的名字,但是,却不能不使用表的名字来为所有的表创建索引。实际上,即使已经使用了像DBCC INDEXDEFRAG这样的工具——该工具能针对整个数据库来完成其工作,但是,却无法拥有很多的选项,它仍将是某种极端的事情。即是说,不能告知它只针对上述的表进行某种级别的碎片整理,或者排除特定的表(我们或许想要在这些表中有碎片)。
要知道,有时碎片也是件好事情。特别是,当要在一个表上进行大量的随机插入时,碎片有助于减少页拆分的次数,这会很有帮助。
我们只需使用游标来动态构建DBCC命令,就能解决上述问题:

眼下,我们实现了仅仅使用基于行集的命令无法完成的事情。ALTER INDEX命令需要一个参数——假设记录集无效。我们通过把行集操作(形成游标的基础的SELECT)的概念与单一数据点操作(游标中的数据)的概念结合在一起,来解决这一问题。
为了混合基于行集的操作与单一数据点的操作,我们必须经历一系列的步骤。首先,需要声明游标和所有必需的容纳数据的变量。然后,“打开”游标——直到这时,才真正从数据库中检索出数据。接下来,通过在游标中导航来使用游标。在这里,我们只向前检索,但是将会看到,也可以创建能够向前滚动和向后滚动的游标。再往后,我们将关闭游标(如果游标仍然有一些打开的锁,则此刻将释放这些锁),但是,为游标分配的内存依然被占用。最后,释放游标。这时,释放了游标使用的所有资源,这些资源可以由系统中其他对象使用。
就是这么快,我们有了自己的第一个游标。不过,这只是开始。对于游标,除了本例中呈现的这些内容外,还有更多值得关注的地方。接下来,将继续深入了解赋予游标额外的灵活的强大功能。






