SQL Cursor(游标)--转

之前做东西的时候遇到需要用有标的, 当时就仿照着别人的例子写的, 最近突然想起来, 就想系统的看一下游标。

原文链接

定义概念

游标是处理数据的一种方法,为了查看或者处理结果集中的数据, 游标提供了在结果集中一次一行或者多行前进或者向后浏览数据的能力。可以吧游标当作一个指针,他可以指定结果中的任何位置,然后允许用户对指定位置的数据进行处理。

使用规范及示例

1.声明游标

游标主要包含游标结果集合游标位置两个部分,游标结果集是定义游标的select语句返回的行集合,游标的位置则是指向这个结果集中的某一行指针。

使用游标之前, 必须要先声明游标, SQL Server中声明使用declare cursor 语句,声明游标包含定义游标的滚动行为和用户生成游标所操作的结果集的查询:

DECLARE cursor_name CURSOR [ LOCAL | GLOBAL]

[ FORWARD_ONLY | SCROLL ]

[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]

[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]

[ TYPE_WARNING ] 

FOR select_statement

[ FOR UPDATE [ OF column_name [,...n] ] ]

说明:

cursor_name:是所定义的T_SQL 服务器游标的名称。

LOCAL:对于在其中创建批处理、存储过程或触发器来说,该游标的作用域是局部的。

扫描二维码关注公众号,回复: 31804 查看本文章

GLOBAL:指定该游标的作用域是全局的

FORWARD_ONLY:指定游标只能从第一行滚动到最后一行。FETCH NEXT是唯一支持的提取选项,如果在指定FORWARD_ONLY时不指定STATIC,KEYSET和DYNAMIC关键字,则游标作为DYNAMIC游标进行操作,如果FORWARD_ONLY和SCROLL均为指定,则除非指定STATIC,KEYSET和DYNAMIC关键字,否则默认为FORWARD_ONLY。STATIC,KEYSET和DYNAMIC游标默认为SCROLL。与ODBC和ADO这类数据库API不同,STATIC,KEYSET和DYNAMIC T_SQL游标支持FORWARD_ONLY。

STATIC:定义一个游标,以创建将又该游标使用的数据临时复本,对游标的所有请求都从tempdb中的这以临时表中不得到应答;因此,在对该游标进行提取操作时返回的数据中不反映对基表所做的修改,并且该游标不允许修改。

KEYSET:指定当游标打开时,游标重的行的成员身份和顺序已经固定。对行进行唯一标识的键值内置在tempdb内一个称为keyset的表中。

DYNAMIC:定义一个游标,以反映在滚动游标时对结果集内的各行所做的所有数据更改。行的数据值、顺序和成员身份在每次提取时都会更改,动态游标不支持ABSOLUTE提取选项。

FAST_FORWARD:指定启动了性能优化的FORWARD_ONLY、READ_ONLY游标。如果指定了SCROLL或FOR_UPDATE,则不能指定FAST_FORWARD。

SCROLL_LOCKS:指定通过游标进行的定位更新或删除一定会成功。将行读入游标时SQL Server将锁定这些行,以确保随后可对它们进行修改,如果还指定了FAST_FORWARD或STATIC,则不能指定SCROLL_LOCKS。

OPTIMISTIC:指定如果行自读入游标以来已得到更新,则通过游标进行的定位更新或定位删除不成功。当将行读入游标时,SQL Server不锁定行,它改用timestamp列值比较结果来确定行读入游标后是否发生了修改,如果表不包含timestamp列,它改用校验和值进行确定,如果以修改该行,则尝试进行的定位更新或删除将失败,如果还指定了FAST_FORWARD,则不能指定OPTIMISTIC。

TYPE_WARNING:指定游标从所请求的类型隐式转换为另一种类型时,向客户端发送警告消息。

select_statement:是定义游标结果集中的标准SELECT语句。

 2.打开游标

使用游标之前, 需要打开游标:

OPEN [ GLOBAL ] cursor_name | cursor_variable_name;

说明:

GLOBAL:指定cursor_name是全局游标。

cursor_name:已声明的游标的名称。如果全局游标和局部游标都使用cursor_name作为其名称,那么如果指定了GLOBAL,则cursor_name指的是全局游标,否则cursor_name指的是局部游标。

cursor_variable_name:游标变量的名称。

3.读取游标中的数据

打开游标之后就可以读取游标中的数据了, fetch 命令可以读取游标中的某一行数据,fetch的语法:

ETCH 
[ [ NEXT | PRIOR | FIRST | LAST 

         | ABSOLUTE { n | @nvar }

         | RELATIVE { n | @nvar }
    ]
FROM
]
{ { [GLOBAL ] cursor_name } | @cursor_variable_name}
[ INTO @variable_name [ ,...n ] ]

说明:

NEXT: 紧跟当前行返回结果行,并且当前行递增为返回行,如果FETCH NEXT为对游标的第一次提取操作,则返回结果集中的第一行。NEXT为默认的游标提取选项。

PRIOR:返回紧邻当前行前面的结果行,并且当前行递减为返回行,如果FETCH PRIOR为对游标的第一次提取操作,则没有行返回并且游标置于第一行之前。

FIRST:返回游标中的第一行并将其作为当前行。

LAST:返回游标中的最后一行并将其作为当前行。

ABSOLUTE { n | @nvar }:如果n或@nvar为正,则返回从游标头开始向后n行的第n行,并将返回行变成新的当前行。如果n或@nvar为负,则返回从游标末尾开始向前的n行的第n行,并将返回行变成新的当前行。如果n或@nvar为0,则不返回行。n必须是整数常量,并且@nvar的数据类型必须为int、tinyint或smallint.

RELATIVE { n | @nvar }:如果n或@nvar为正,则返回从当前行开始向后的第n行。如果n或@nvar为负,则返回从当前行开始向前的第n行。如果n或@nvar为0,则返回当前行,对游标第一次提取时,如果在将n或@nvar设置为负数或0的情况下指定FETCH RELATIVE,则不返回行,n必须是整数常量,@nvar的数据类型必须是int、tinyint或smallint.

GLOBAL:指定cursor_name是全局游标。

cursor_name:已声明的游标的名称。如果全局游标和局部游标都使用cursor_name作为其名称,那么如果指定了GLOBAL,则cursor_name指的是全局游标,否则cursor_name指的是局部游标。

@cursor_variable_name:游标变量名,引用要从中进行提取操作的打开的游标。

INTO @variable_name [ ,...n ]:允许将提取操作的列数据放到局部变量中。列表中的各个变量从左到右与游标结果集中的相应列相关联。各变量的数据类型必须与相应的结果集列的数据类型相匹配,或是结果集列数据类型所支持的隐士转换。变量的数目必须与游标选择列表中的列数一致

4.关闭游标

SQL Server 在打开游标之后, 服务器会专门为游标开辟一定的内存空间存放游标操作的数据结果集,同时游标的使用也会根据具体情况对某些数据进行封锁。所以在不使用游标的时候,可以将其关闭 ,已释放游标所占用的服务器资源,关闭游标使用close 语句。

CLOSE [ GLOBAL ] cursor_name | cursor_variable_name

5.释放游标

游标操作的结果集空间虽然释放了,但是游标本身也会占用一定的计算集资源,所以在使用完游标之后, 为了收回被游标占用的资源, 应该将游标释放。释放游标使用deallocate语句。

DEALLOCATE [GLOBAL] cursor_name | @ccursor_variable_name

@ccursor_variable_name:游标变量的名称,@ccursor_variable_name必须为cursor类型。

DEALLOCATE @ccursor_variable_name 语句只删除对游标变量名称的引用,直到批处理、存储过程或触发器结束时变量离开作用域,才释放变量。

示例:

        DECLARE BookOID_Cursor CURSOR
        FOR
            SELECT  OID
            FROM    T_Book_TX
            WHERE   ReceivedDate < DATEADD(DAY, -@Xdays, GETDATE());
        OPEN BookOID_Cursor;
        FETCH NEXT FROM BookOID_Cursor INTO @BookOID;
        WHILE @@FETCH_STATUS = 0
            BEGIN
                EXEC P_UpdateBookByOID @BookOID;
                FETCH NEXT FROM BookOID_Cursor INTO @BookOID;
            END;
        CLOSE BookOID_Cursor;
        DEALLOCATE BookOID_Cursor;

网上示例:

1.使用游标变量

声明变量用DECLARE,为变量赋值可以用set或SELECT语句,对于游标变量的声明和赋值,其操作基本相同。在具体使用时,首先要创建一个游标,将其打开后,将游标的值赋给游标变量,并通过FETCH语句从游标变量中读取值,最后关闭释放游标。

【例】声明名称为@varCursor的游标变量,输入如下:

DECLARE @varCursor Cursor --声明游标变量

DECLARE cursor_fruit CURSOR FOR --创建游标

SELECT f_name,f_price FROM fruits;

OPEN cursor_fruit --打开游标

SET @varCursor=cursor_fruit --为游标变量赋值

FETCH NEXT FROM @varCursor --从游标变量中读取值

WHILE @@FETCH_STATUS=0 --判断FETCH语句是否执行成功

BEGIN

FETCH NEXT FROM @varCursor --读取游标变量中的数据

END

CLOSE @varCursor --关闭游标

DEALLOCATE @varCursor; --释放游标

2.用游标为变量赋值

在游标的操作过程中,可以使用FETCH语句将数据值存入变量,这些保持表中列值的变量可以在后面的程序中使用。

【例】创建游标cursor_variable,将fruits表中的记录f_name,f_price值赋给变量@fruitName和@fruitPrice,并打印输出。

--创建游标cursor_variable,将fruits表中的记录f_name,f_price值赋给变量@fruitName和@fruitPrice
DECLARE @fruitName varchar(50),@fruitPrice Decimal(8,2)
DECLARE cursor_variable CURSOR FOR
SELECT f_name,f_price FROM fruits WHERE f_id<100;
OPEN cursor_variable
FETCH NEXT FROM cursor_variable
INTO @fruitName,@fruitPrice
PRINT 'Fruit Name and Fruit Price is:'
WHILE @@FETCH_STATUS=0
BEGIN
   PRING  @fruitName+'  '+STR(@fruitPrice,8,2)
FETCH NEXT FROM cursor_variable
INTO @fruitName,@fruitPrice
END
CLOSE cursor_variable;
DECALLOCATE cursor_variable;

3.用ORDER BY 子句改变游标中的执行顺序

游标是一个查询结果集,那么能不能对结果进行排序呢?答案是否定的。与基本的SELECT语句中的排序方法相同,ORDER BY子句添加到查询中可以对游标查询的结果排序。

注意:只有出现在游标中的SELECT语句中的列才能作为ORDER BY 子句的排序列,而对与非游标的SELECT语句中,表中任何列都可以作为ORDER BY 的排序列,即使该列没有出现在SELECT语句的查询结果列中。

【例】声明名称为cursor_order的游标,对fruits表中的记录按照价格字段降序排列,输入语句如下: 

--创建游标cursor_order的游标,将fruits表中的记录按照价格排序

DECLARE cursor_order CURSOR FOR
SELECT f_name,f_price FROM fruits WHERE f_id<100 ORDER BY f_price DESC;
OPEN cursor_order
FETCH NEXT FROM cursor_order
WHILE @@FETCH_STATUS=0
FETCH NEXT FROM cursor_order
CLOSE cursor_variable
DECALLOCATE cursor_variable

4.用游标修改数据

【例】声明整型变量@fid=99,然后声明一个对fruits表进行操作的游标,打开该游标,使用FETCH NEXT方法来获取游标中的每一行的数据,如果获取到的记录的f_id的字段值与@sid值相同,将f_id=@fid的记录中的f_price修改为500,最后关闭释放游标,输入如下:

--使用FETCH NEXT 方法来获取游标中的每一行数据
--如果获取到的记录的f_id 与@id相同,则将价格改为500
DECLARE @id INT = 99,@fid INT
DECLARE cursor_fruit CURSOR FOR
SELECT f_id FROM fruits;
OPEN cursor_fruit
FETCH NEXT FROM cursor_fruit
INTO @fid
WHILE @@FETCH_STATUS=0
BEGIN
  IF @fid=@id
    update fruits set f_price=500 where f_id=@id
FETCH NEXT FROM cursor_variable
INTO @fid
END
CLOSE cursor_fruit;
DECALLOCATE cursor_fruit;

5.用游标删除数据

使用游标删除数据时,既可以删除游标结果集中的数据,也可以删除基本表中的数据

【例】使用游标删除fruits表中s_id=102的记录,如下:

--【例】使用游标删除fruits表中s_id=102的记录,如下
DECLARE @sid1 INT,@id1 int=102
DECLARE cursor_delete CURSOR FOR
SELECT s_id FROM fruits;
OPEN cursor_delete
FETCH NEXT FROM cursor_delete INTO @sid1
WHILE @@FETCH_STATUS=0
BEGIN
    IF @sid1=@id1
BEGIN 
    DELETE FROM fruits where s_id=@id1
END
FETCH NEXT FROM cursor_delete INTO @sid1
END
CLOSE cursor_delete
DEALLOCATE cursor_delete;
SELECT * FROM fruits where s_id=102;

猜你喜欢

转载自www.cnblogs.com/wanff/p/8875907.html