Duangw

游标

索引:

  1. 游标种类
  2. 游标操作语句
  3. 滚动游标
  4. 更新游标
  5. 插入游标

1.游标种类

有如下几种类型:

 

2.游标操作语句

DECLARE;

OPEN;

FETCH;

CLOSE;

FREE。

 

3.滚动游标

(1).DECLARE语句

语法:

DECLARE cursor_name [SCROLL] CURSOR [WITH HOLD] FOR selectstmt

说明:

cursor_name:游标名称,是一个标示符或存放标示符的宿主变量。标示符在整个程序中是全局的、唯一的;除非使用-local编译,此时它在源文件中唯一。

selectstmt:SELECT语句,可在此使用INTO子句(或者在FETCH中使用),两者必居其一。不能使用INTO TEMP。

WITH HOLD:当事务结束时游标仍保持打开状态。

该语句只是一个声明,并不执行语句。游标结构会一直保存在内存中,直到使用FREE释放它为止。

(2).OPEN语句

语法:

OPEN cursor_name;

说明:

cursor_name:使用DECLARE创建的游标,是一个标示符或存放标示符的宿主变量。

该语句激活游标并开始执行相关语句,有如下步骤:

  1. 如果WHERE子句有宿主变量,此时才使用它们的值。
  2. 把SELECT语句发送到数据库服务器,由服务器进行语法检查和优化查询路径。
  3. 开始执行SELECT语句,直到标示了活动集,定位和构建该集合的头一个记录,包括打开所有需要的文件和创建临时表。产生的活动集一直存在,直到关闭游标。
  4. 重新打开先前关闭的游标时,将再次使用宿主变量,但服务器不会再对SELECT语句进行语法检查和优化,除非使用了FREE语句释放了同游标相关联的内存。
(3).FETCH语句

语法:

FETCH [ position ] cursor_name [ INTO host_varlist ];

说明:

position:操作活动集的位置。可选择:

数据库服务器一次从基表中读取满一个缓冲区的数据(可能包括多条记录),放到临时表里,再把它们拷贝到应用程序建立的管道中,由FETCH把它们读到数据缓冲区里。每次FETCH的NEXT操作首先查找数据缓冲区,当缓冲区没有数据时,才由服务器再次读基表。

FETCH中除NEXT外的其他操作只从数据库和临时表中获取数据,即每次操作都进行一次管道I/O。

当游标成功移动到指定的位置且在该位置有记录时,SQLCODE值为0,如果没有记录,返回SQLNOTFOUND(100)。

(4)CLOSE语句

语法:

CLOSE cursor_name;

关闭游标,并释放活动集。关联的一些资源被释放:

没有释放的资源是原先OPEN时创建的SELECT语句。

关闭游标之后,除OPEN和FREE外其他语句都不能再使用。

重新打开一个关闭的游标,会得到一个新的活动集。

(5).FREE语句

语法:

FREE cursor_name;

释放游标。

在释放之前,最好先显示的关闭它。此外,还释放在服务器存放的进行了语法分析的SELECT语句版本,由DECLARE设置的游标结构。

滚动游标的问题

使用滚动游标存在两个问题:临时表可能增长得非常大;临时表中的数据可能过期或已陈旧。

解决方法是:

为减少临时表的大小,可以SELECT较少的记录或字段;为避免陈旧数据,应确保在把记录显示给用户之前从实际的表中重新获取记录的内容。

实现方法:使用两个游标。第一个游标是滚动游标,它只获取必要的字段,如主键字段,得到的值在第二个游标里使用。第二个游标用来选择整个记录,每次针对第一个游标执行FETCH操作时,针对第二个游标使用OPEN/FETCH操作。该OPEN/FETCH序列迫使第二个游标每次从实际表中获取记录。

 

4.更新游标

定义

更新游标对当前所指向的记录加锁,其他用户可以查看记录的内容,但在记录加锁期间其他用户不能更改记录。当记录被修改和删除时,该锁被提升为一个互斥锁,一般情况下,该锁一直到事务结束时才释放,如果没有事务,该锁在DELETE或UPDATE操作完成后释放。

语法
DECLARE cursor_name CURSOR FOR selectstmnt \
FOR UPDATE [ OF column_list ];

更新游标只能对单表操作的SELECT语句进行定义。

更改/删除

当要对当前记录进行更改/删除时,UPDATE/DELETE的WHERE子句使用如下语法:

WHERE CURRENT OF cursor_name;
加锁时机

方法一、等到UPDATE时加锁。缺点是FETCH时没有加锁,用户查看到记录可能已经被其他用户更改。

方法二、获取记录时加锁,使用滚动游标实现。缺点是用户即使选择不修改,也要加锁。

方法三、由用户决定要修改时加锁。做法是:定义一个滚动游标来完成查询,用户浏览记录,当选择修改时,为想要修改的记录定义一个更新游标,使用更新游标获取记录,并重新显示锁定的记录,一旦做出更改,使用WHERE CUREENT OF修改记录。该法的副作用是当用户选择了修改而没有进行修改时,记录会保持锁定直到事务回滚或完成。

方法四、选择记录以获取最新消息,把记录存放到old_record中,把old_record拷贝到new_record,允许用户修改new_record,当用户选择修改时,把old_record同选择用于修改的记录相比较,如果两者有区别,就告知用户记录已经被别人修改,并停止修改过程;否则,使用new_record的值来修改记录。

 

5.插入游标

插入游标用于一次向数据库中插入多条记录。

(1).DECLARE语句
DECLARE cursor_name CURSOR FOR insert_statement;
(2).OPEN语句
OPEN cursor_name;

完成如下工作:

(3).PUT语句
PUT cursor_name;

使用PUT把一条记录存放到INSERT缓冲区,以后再插入到数据库中。Sqlca.sqlcode和sqlca.sqlerrd[2]指明语句执行的结果:

(4).FLUSH语句
FLUSH cursor_name;

把缓冲区中的记录插入到数据库中,而不关闭游标。

如果插入成功,则sqlca.sqlcode设置为0,sqlca.sqlerrd[2]设置为所插入的记录的数目。

如果插入不成功,则sqlca.sqlcode设置为负值(错误码),而把sqlca.sqlerrd[2]设置为成功插入到数据库中的记录的数目。

退出程序前如果没有关闭游标,则缓冲区的内容不会写到数据库中,在缓冲区中的记录会丢失。

(5).CLOSE语句
CLOSE cursor_name;

把缓冲区中的记录插入到数据库中,腾空缓冲区,并使INSERT游标和INSERT缓冲区失效,缓冲区内存被释放。

sqlca.sqlerrd[2]设置为成功插入到数据库中的记录的数目。

(6).FREE语句
FREE cursor_name;

释放游标。