游标是映射结果集并在结果集内的单行上建立一个位置的实体。有了游标,用户就可以访问结果集中任意一行数据了,将游标放置到某行之后,可以在该行或从该位置开始的行块上执行操作。最常见的操作是提取当前行或行块。

游标通常在存储过程中使用。
执行SELECT语句所得到的结果集叫做游标结果集,而指向游标结果集中的某一条记录的指针叫做游标的位置。

游标的功能

  • 允许定位在结果集的特定行
  • 从结果集的当前位置检索一行或者多行
  • 支持对结果集中当前位置的行进行数据你修改
  • 如果其他用户需要对显示在结果集中的数据库数据进行修改,游标可以提供不同级别的可见性支持
  • 提供在脚本,存储过程和触发器中使用,访问结果集中的数据的T-SQL语句

游标被定义后存在两种状态,即打开和关闭。当游标被关闭时,游标的结果集不存在;当游标被打开时,用户可以按行读取或修改游标结果集中的数据。

游标的分类

三种游标

T-SQL游标

使用T-SQL语句创建的游标,主要用在T-SQL脚本,存储过程和触发器中。T-SQL游标在服务器上实现并从客户端发送到服务器的T-SQL语句管理。它们还包含在批处理,存储过程,触发器中

应用编程接口(API)服务器游标

支持OLEDB,ODBC和DB-Library中的API游标函数。API服务器游标在服务器上的实现。每次客户应用程序调用API游标函数时,SQL Server提供程序,ODBC驱动程序,DB-Library中动态链接库(DLL),就会把请求传到服务器,以便于对API服务器游标进行操作。

客户端游标

由SQL Server ODBC驱动程序,DB-Library DLL和实现ADO API的DLL在内部实现。客户端游标通过客户端高速缓存所有结果集来实现。每次客户端应用程序调用API游标函数时,SQL Server ODBC驱动程序,DB-Library DLL或ADO DLL就对高速缓存在客户端的结果集进行执行游标操作。

API服务器游标

由于T-SQL游标和API服务器游标都在服务器端实现,他们一起被称为服务器游标。
SQL Server支持4种API服务器游标类型

静态游标

静态游标的完整结果集在打开时建立在tempdb中,它总是按照游标打开时的原样显示结果集。
静态游标被打开以后,数据库中任何影响结果集的变化都不会体现在游标中,例如:

  • 静态游标不会显示其被打开以后在数据库中插入的行。
  • 静态游标仍会显示在游标被打开以后删除的行
  • 静他游标仍会显示被修改行的原始数据

也就是说,静他游标打开后,一切在其打开后的DML操作改变都不会更新游标。除非关闭游标重新打开。

  • 静态游标始终是只读的。

    动态游标

    动态游标是与静态游标相对的概念,当滚动游标时,动态游标反映结果集中所做的所有DML更改。结果集中的行数据,顺序和成员在每次提取时都可能会改变。

    只进游标

    只进游标不支持滚动,它只支持游标从头到尾顺序提取。行只在从数据库中提取出来后才能检索。对所有影响结果集行的DML操作语句其效果在这些行从游标中提取时是可见的。但是,因为游标不能像后滚动,所以在行被提取后对行所做的更改对游标是不可见的。

    键集驱动游标

    键集驱动游标由一套被称为键集的唯一标识符(键)控制。键由以唯一方式在结果集中标识的行的列构成。键集是游标打开时来自所有合适SELECT语句的行中的一系列键值。键集驱动游标的键集在游标被打开时建立在tempdb中。
    对非键集列中的数据值所做的更改,在用户滚动游标时是可见的。在游标外对数据库所做的插入在游标内是不可见的,除非关闭并重新打开游标。使用API函数通过游标所做的插入在游标的末尾可见,如果试图提取一个在打开游标后被删除的行,则@@FETCH_STATUS将返回一个“行缺少”的状态。

游标的使用

使用游标的流程是声明游标,打开游标,读取游标中的数据,获取游标的属性和状态,最后一定记得关闭游标,释放游标占用资源。如果不再使用游标,那么还应该及时将其删除。

声明游标

在声明游标时需要指定游标类型,在使用游标时也应该考虑游标的类型。

可以用DECLARE CURSOR语句来声明T-SQL服务器游标和定义游标的特性,如游标的滚动行为和结果集的查询方式。

  1. DECLARE <游标名> CURSOR
  2. [LOCAL|GLOBAL]
  3. [FORWARD_ONLY|SCROLL]
  4. [STATIC|KEYSET|DYNAMICK|FAST_FORWARD]
  5. [READ_ONLY|SCROLL_LOCKS|OPTIMISTIC]
  6. [TYPE_WARNING]
  7. FOR <T-SQL查询语句>
  8. [FOR UPDATE [OF <列名>[,...n]]]
  • LOCAL:指定该游标的作用域对创建它的批处理,存储过程或触发器是局部的。该游标名称仅在这个作用于内有效。
  • GLOBAL:指定该游标的作用域对数据库连接是全局的。在由数据库连接执行的任何存储过程或批处理中,都可以引用该游标名称。该游标仅在断开连接时被隐性释放。
  • FORWARD_ONLY:指定声明游标为只进游标。如果在指定FORWARD_ONLY时不指定STATIC,KEYSET,DYNAMIC关键字,则游标作为动态游标进行操作。如果FORWARD_ONLY和SCROLL均未指定,除非指定STATIC,KEYSET或DYNAMIC关键字,否则默认为FORWARD_ONLY。STATIC,KEYSET,DYNAMIC游标则默认为SCROLL。
  • STATIC:指定当前声明的游标为静态游标
  • KEYSET:指定当前声明的游标为键集驱动游标
  • DYNAMIC:指定当前声明的游标为动态游标
  • FAST_FORWARD:指定启用了性能优化的FORWARD_ONLY,READ_ONLY游标,如果指定FAST_FORWARD,则不指定SCROLL或FOR_UPDATE。FAST_FORWARD和FORWARD_ONLY是互斥的,如果指定一个,则不能指定另一个。
  • READ_ONLY:禁止通过对游标数据进行更新。在UPDATE或DELETE语句的WHERE CURRENT OF子句中不能引用游标。
  • SCROLL_LOCKS:指定确保通过游标完成的定位更新或者定位删除可以成功。当将行读入的游标用于修改时,SQL Server会锁定这些行。如果还指定了FAST_FORWARD,则不能指定SCROLL_LOCKS。
  • OPTIMISTIC:指定如果行自从被读入游标以来已得到更新,则通过游标进行定位更新或定位删除不成功。当将行读入游标时SQL Server不锁定行。
  • TYPE_WARNING:指定如果游标从所请求的类型隐性的转换成另一种类型,则给客户端发送警告消息。
  • :如果SELECT内的子句与所请求的游标类型冲突,SQL Server将游标隐性转换成另一种类型。
  • FOR UPDATE [OF <列名>[,…n]]:用于定义游标内可更新的列。如果提供了OF<列名>[,…n],则只允许修改列出的列。如果在UPDATE子句中未指定列的列表,除非制定了READ_ONLY并发选项,否则所有列均可更新。

打开游标

  1. OPEN {{[GLOBAL] <游标名>}|<游标变量>}

读取游标数据

定义游标的最终目的就是读取游标数据

FETCH语句

  1. FETCH
  2. [[NEXT|PRIOR|FIRST|LAST|ABSOLUTE {n|@nvar}|RELATIVE {n|@nvar}] FROM]
  3. {{[CLOBAL] <游标名>}|<游标变量>}
  4. [INTO <变量>[,...n]]
  • NEXT:返回仅跟当前行之后的结果行,并且当前行递增为结果行。如果FETCH NEXT对游标的第一次提取操作,则返回结果集中的第一行。NEXT为默认游标提取选项。
  • PRIOR:返回紧跟当前行前面的结果行,并且当前行递减为结果行。如果FETCH NEXT对游标的第一次提取操作,则没有返回行,并且游标位于第一行之前。
  • FIRST:返回游标中的第一行并将其作为当前行。
  • LAST:返回游标中的最后一行并将其作为当前行。
  • ABSOLUTE {n|@nvar}:如果n或者@nvar为整数,返回从游标头开始的第n行并将返回的行变成新的当前行。如果n或者@nvar为0,则没有行返回。n必须为整型常量,且@nvar类型必须为SMALLINT,TINYINT,INT。
  • RELATIVE {n|@nvar}:如果n或@nvar为正整数,返回当前行之后的第n行并将返回的行变成新的当前行。如果n或@nvar为负数,返回当前行之前的n行,并将返回行变成新的当前行。如果n或@nvar为0,返回当前行。如果对游标的第一次提取操作时,将n或@nvar指定为负数或0,则没有行返回。
  • GLOBAL:指定为全局游标
  • <游标名>|<游标变量名>:如果有两个同样名字游标,一个是全局游标,一个是局部游标。若是指定了GLOBAL,则<游标名>对应全局游标,未指定GLOBAL则对应于局部游标。
  • INTO <变量>[,…n]:允许将提取的列数据放到局部变量中。列表中的各个变量从左到右与游标结果集中的相应列相关联。各变量的数据类型必须与相应的结果列的数据类型匹配或是结果列数据类型所支持隐性转换。变量的数量必须与游标选择列表中的列的数目一致。

例子

  1. GO
  2. DECLARE reader_cursor CURSOR
  3. FOR
  4. SELECT *FROM Reader
  5. GO
  6. OPEN reader_cursor
  7. GO
  8. FETCH reader_cursor
  9. GO

默认是NEXT

SELECT * FROM Reader这张表查询出来的结果是这样
image.png
每次执行FETCH reader_cursor的结果是这样的,现在这次是谭小勇。
image.png
当我在执行一次FETCH reader_cursor的结果就变成了
image.png
看是不是和上面的SELECT * FROM Reader查询出来的顺序一样,因为默认是NEXT查询,每次游标取一条下一行的数据回来。和程序语言的指针遍历有点像。执行一次代码,指针就指向下一条数据。

@@FETCH_STATUS函数

可以使用@@FETCH_STATUS函数获取FETCH语句的状态。返回值等于0表示FETCH语句执行成功;返回值等于-1表示FETCH语句执行失败;返回值等于-2表示提取的行不存在。

  1. PRINT @@FETCH_STATUS

@@CURSOR_ROWS函数

这个函数返回最后打开的游标中当前存在的行的数量,返回值说明

返回值 说明
-m 游标被异步填充。返回值是键集中当前的行数
-1 游标为动态。因为动态游标可以反应所有更改,所以符合游标的行数不断的变化。因而永远不能确定地说所有符合条件的行均已被检测到。
0 没有被打开的游标,没有符合最后打开游标的行,或最后打开的游标已被关闭或释放
n 游标已完全填充。返回值是在游标中的总行数
  1. PRINT @@CURSOR_ROWS

关闭游标

关闭游标后会完成以下工作

  • 释放当前结果集
  • 解除定位于游标行上的游标锁定

不允许在关闭的游标上提取,定位和更新数据,知道游标从新打开为止。关闭游标并不意味着释放它所有资源,所以在关闭游标后,不能创建同名的游标。

  1. CLOSE {[CLOBAL] <游标名>}|<游标变量>

获取游标的状态和属性

获取游标状态

在使用游标时经常需要根据游标的状态来决定所要进行的操作。

  1. CURSOR_STATUS(<游标类型>,<游标名称或游标变量>)

游标的类型LOCAL或者GLOBAL

函数返回值说明

返回值 说明
-1 游标的结果集中至少存在一行数据
0 游标的结果集为空
1 游标被关闭
2 游标不适用
3 指定名称的游标不存在

使用sp_cursor_list获取游标属性

  1. sp_cursor_list @cursor_return=<游标名称> OUTPUT,@cursor_scope=<游标级别>

游标级别=1表示所有本地游标,游标级别等于2表示所有全局游标,游标级别等于3表示所有本地和全局游标。

sp_cursor_list返回结果集常用字段说明

列名 说明
reference_name 用于引用的游标名称,可以是游标名称,也可以是心仪的游标的变量
cursor_name 在DECLARE CURSOR中声明的游标名称
cursor_scope 游标的范围,1表示LOCAL,2表示GLOBAL
status 游标的状态
model 游标的类型,1表示静态游标,2表示键集游标,3表示动态游标,4表示快进游标
concurrency 1表示只读游标,2表示滚动锁定,3表示乐观锁定
scrollable 0表示只进,1表示可滚动
open_status 0表示关闭,1表示打开
cursor_row 游标结果集中的行数
fetch_status 游标上次提取数据的状态。0表示提取成功,-1表示提取失败或超出游标的界限,-2表示缺少所请求的行
column_count 游标结果集中的列数
row_count 上次游标操作所影响的行数
last_operation 上次对游标执行的操作

使用sp_describe_cursor读取游标属性

  1. sp_describe_cursor @cursor_return=<输入游标的名称> OUTPUT ,@cursor_source=<游标类型> ,@cursor_identity=<游标名称>

游标类型:'local'局部游标,'global'全局游标,'variable'表示游标变量

sp_describe_cursor返回的结果集和sp_cursor_list相同

使用sp_describe_cursor_columns获取游标属性

  1. sp_describe_cursor_columns @cursor_return=<输出游标的名称> OUTPUT, @cursor_source=<游标类型>, @cursor_identity=<游标名称>

sp_describe_cursor_columns返回结果集常用字段说明

列名 说明
column_name 结果集中的列名
ordinal_position 从结果集最左侧算起的相对位置
column_size 此列值得最大可能尺寸
data_type_sql 表示列的数据类型的数字
column_precision 列的最大精度
columns_cale 指定NUMERIC或者DECIMAL数据类型小数点右边的位数
order_position 如果此列参与结果集的排序,则它表示当前列在排序列中的位置
order_direction 等于A表示升序排列,等于D表示降序排列,等于NULL表示当前列没有参与排序
columnid 基列的列ID
objectid 列所属的对象或基表的ID
dbid 基表所属的数据库ID
dbname 基表所属的数据库的名称

使用sp_describe_cursor_tables获取游标的基表

  1. sp_describe_cursor_tables @cursor_return=<输出游标的名称> OUTPUT, @cursor_source=<游标类型>, @cursor_identity=<游标名称>

sp_describe_cursor_tables返回结果集常用字段说明

列名 说明
table_owner 表所有者的用户ID
table_name 表名
server_name 数据库服务器的名称
objectid 表的对象ID
dbid 表所属的数据库ID
dbname 表所属的数据库名称

删除游标结果集中的行

  1. DELETE FROM <表名> WHERE CURRENT OF <游标名>

删除游标

删除了就释放了资源,删除后可以再次创建同名的游标

  1. DEALLOCATE {{[GLOBAL] <游标名>}|<游标变量>}