首页  编辑  

实现游标


实现游标

Oracle在使用SELECT语句时总是需要游标,不管从数据库中请求多少行。在Microsoft SQL Server,SELECT语句并不把在返回客户的行上附加游标作为缺省的结果集合。这是一种返回数据给客户应用程序的有效的方法。

SQL Server为游标函数提供了两种接口。当在Transact-SQL批处理或者存储过程中使用游标的时候,SQL语句可用来声明、打开、和从游标中抽取,就像定位更新和删除一样。当使用来自DB-Library、ODBC、或者OLEDB程序的游标时,SQL Server显式的调用内建的服务器函数来更有效的处理游标。

当从Oracle输入一个PL/SQL过程时,首先判断是否需要在Transact-SQL中采用游标来实现同样的功能。如果游标仅仅返回一组行给客户程序,就使用非游标的SELECT语句来返回缺省的结果集合。如果游标用来从行中一次取得一个数据给本地过程变量,你就必须在Transact-SQL中使用游标。

语法

下表显示了使用游标的语法。

操作

Oracle

Microsoft SQL Server

声明一个游标

CURSOR cursor_name [(cursor_parameter(s))]
IS select_statement;

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]]]

打开一个游标

OPEN cursor_name [(cursor_parameter(s))];

OPEN cursor_name

从游标中提取(Fetching)

FETCH cursor_name INTO variable(s)

FETCH [[NEXT | PRIOR | FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]
FROM] cursor_name
[INTO @variable(s)]

更新提取行

UPDATE table_name
SET statement(s)…
WHERE CURRENT OF cursor_name;

UPDATE table_name
SET statement(s)…
WHERE CURRENT OF cursor_name

删除提取行

DELETE FROM table_name
WHERE CURRENT OF cursor_name;

DELETE FROM table_name
WHERE CURRENT OF cursor_name

关闭游标

CLOSE cursor_name;

CLOSE cursor_name

清除游标数据结构

N/A

DEALLOCATE cursor_name

声明一个游标

尽管Transact-SQL DECLARE CURSOR语句不支持游标参数的使用,但它确实支持本地变量。当游标打开的时候,它就使用这些本地变量的值。Microsoft SQL Server在其DECLARE CURSOR中提供了许多附加的功能。

INSENSITIVE选项用来定义一个创建数据的临时拷贝以被游标使用的游标。游标的所有请求都由这个临时表来应答。因此,对原表的修改不会反映到那些由fetch返回的用于该游标的数据上。这种类型的游标访问的数据是不能被修改的。

应用程序可以请求一个游标类型然后执行一个不被所请求的服务器游标类型支持的Transact-SQL语句。SQL Server返回一个错误,指出该游标类型被改变了,或者给出一组参数,隐式的转换游标。欲取得一个触发SQL Server 7.0隐式的把游标从一种类型转换为另一种类型的参数的完整列表,请参阅SQL Server联机手册。

SCROLL选项允许除了前向的抽取以外,向后的、绝对的和相对的数据抽取。一个滚动游标使用一种键集合的游标模型,在该模型中,任何用户提交的对表的删除和更新都将影响后来的数据抽取。只有在游标没有用INSENSITIVE选项声明时,上面的特性才起作用。

如果选择了READ ONLY选项,对游标中的行的更新就被禁止。该选项将覆盖游标的缺省选项——允许更新。

UPDATE [OF column_list]语句用来在游标中定义一个可更新的列。如果提供了[OF column_list],那么仅仅是那些列出的列可以被修改。如果没有指定任何列。则所有的列都是可以更新的,除非游标被定义为READ ONLY。

重要的是,注意到一个SQL Server游标的名字范围就是连接自己。这和本地变量的名字范围是不同的。不能声明一个与同一个用户连接上的已有的游标相同名字的游标,除非第一个游标被释放。

打开一个游标

Transact-SQL不支持向一个打开的游标传递参数,这一点和PL/SQL是不一样的。当一个Transact-SQL游标被打开以后,结果集的成员和顺序就固定下来了。其它用户提交的对原表的游标的更新和删除将反映到对所有未加INSENSITIVE选项定义的游标的数据抽取上。对一个INSENSITIVE游标,将生成一个临时表。

抽取数据

Oracle游标只能向前移动——没有向后或者相对滚动的能力。SQL Server游标可以向前或者向后滚动,具体怎么滚动,要由下表给出的数据抽取选项来决定。只有在游标是用SCROLL选项声明的前提下,这些选项才能使用。

卷动选项

描述

NEXT

如果这是对游标的第一次提取,则返回结果集合的第一行;否则,在结果结合内移动游标到下一行。NEXT是在结果集合中移动的基本方法。NEXT是缺省的游标提取(fetch)。

PRIOR

返回结果集合的前一行。

FIRST

把游标移动到结果集合的第一行,同时返回第一行。

LAST

把游标移动到结果集合的最后一行,同时返回最后一行。

ABSOLUTE n

返回结果集合的第n行。如果n为负数,则返回倒数第n行

RELATIVE n

返回当前提取行后的第n行,如果n是负数,则返回从游标相对位置起的倒数第n行。

Transact-SQL的FETCH语句不需要INTO子句。如果没有指定返回变量,行就自动作为一个单行结果集合返回给客户。但是,如果你的过程必须把行给客户,一个不带游标的SELECT语句更有效一些。

在每一个FETCH后面,@@FETCH_STATUS函数被更新。这和在PL/SQL中使用CURSOR_NAME%FOUNDCURSOR_NAME%NOTFOUND变量是相似的。@@FETCH_STATUS函数在每一次成功的数据抽取以后被设定为0。如果数据抽取试图读取一个超过游标末尾的数据,则返回一个为-1的值。如果请求的行在游标打开以后从表上被删除了,@@FETCH_STATUS函数就返回一个为-2的值。只有游标是用SCROLL选项定义的情况下,才会返回-2值。在每一次数据抽取之后都必须检查该变量,以确保数据的有效性。

SQL Server不支持Oracle的游标FOR循环语法。

CURRENT OF子句

更新和删除的CURRENT OF子句语法和函数在PL/SQL和Transact-SQL中是一样的。在给定游标中,在当前行上执行定位的UPDATE和DELETE。

关闭一个游标

Transact-SQL的CLOSE CURSOR语句关闭游标,但是保留数据结构以备重新打开。PL/SQL 的CLOSE CURSOR语句关闭并且释放所有的数据结构。

Transact-SQL需要用DEALLOCATE CURSOR语句来清除游标数据结构。DEALLOCATE CURSOR语句同CLOSE CURSOR是不一样的,后者保留数据结构以备重新打开。DEALLOCATE CURSOR释放所有与游标相关的数据结构并且清除游标的定义。

游标示例

下面的例子显示了在PL/SQL和Transact-SQL等价的游标语句。

Oracle

Microsoft SQL 

DECLARE
VSSN CHAR(9);
VFNAME VARCHAR(12);
VLNAME VARCHAR(20);

DECLARE
@VSSN CHAR(9),
@VFNAME VARCHAR(12),
@VLNAME VARCHAR(20)

CURSOR CUR1 IS
SELECT SSN, FNAME, LNAME
FROM STUDENT ORDER BY LNAME;
BEGIN
    OPEN CUR1;
    FETCH CUR1 INTO VSSN,     VFNAME, VLNAME;
    WHILE (CUR1%FOUND) LOOP
    FETCH CUR1 INTO VSSN,     VFNAME, VLNAME;
END LOOP;
CLOSE CUR1;
END;

DECLARE curl CURSOR FOR
   SELECT SSN, FNAME, LNAME
   FROM STUDENT ORDER BY SSN
OPEN CUR1
FETCH NEXT FROM CUR1
   INTO @VSSN, @VFNAME, @VLNAME
WHILE (@@FETCH_STATUS <> -1)
   BEGIN
  FETCH NEXT FROM CUR1 INTO       @VSSN, @VFNAME, @VLNAME
   END
CLOSE CUR1
DEALLOCATE CUR1