与顺序游标相关联的 INSERT 语句的子集
如 DECLARE 语句的程序中所述,要创建一个 Insert 游标,应将顺序游标与 INSERT 语句的限制格式相关联。INSERT 语句必须包括 VALUES 子句;它不可以包含嵌入的 SELECT 语句。
下面的示例包含声明 Insert 游标的 GBase 8s ESQL/C 代码:
EXEC SQL declare ins_cur cursor for
insert into stock values
(:stock_no,:manu_code,:descr,:u_price,:unit,:u_desc);
Insert 游标简单地插入数据行;它不可以用于取得数据。当打开 Insert 游标时,会在内存中创建一个缓冲区。该缓冲区在程序执行 PUT 语句时接收数据行。只有在缓冲区满的时候才会将这些行写入磁盘。您可以在缓冲区还未满时使用 CLOSE 、FLUSH 或 COMMIT WORK 语句刷新缓冲区(即将它的内容写入数据库)。该主题会在 CLOSE 、FLUSH 和 PUT 语句下做进一步讨论。
您必须在程序结束之前关闭 Insert 游标,从而将任何已缓冲的行插入数据库。如果没有正确关闭游标,则会丢失数据。有关对 INSERT 语法和使用的完整讨论,请参阅 INSERT 语句。
Insert 游标
当您将 INSERT 语句与一个游标相关联的时候,该游标称为 Insert 游标。Insert 游标是一个数据结构,它代表 INSERT 语句将要添加到数据库的行。INSERT 游标简单地插入数据行;它不可以用于取得数据。要创建 Insert 游标,应将一个游标与 INSERT 语句的限制格式相关联。INSERT 语句必须包括 VALUES 子句;它不可以包含嵌入的 SELECT 语句。
如果想要在 INSERT 操作中将多个行添加到数据库,应创建一个 Insert 游标。Insert 游标允许将大量的插入数据缓存到内存中,并在缓冲区满的时候写入磁盘,具体如以下这些步骤所述:
- 使用 DECLARE 可为 INSERT 语句定义一个 Insert 游标。
- 使用 OPEN 语句打开游标。数据库服务器会在内存中创建缓冲区。并将游标的位置确定在插入缓冲区的第一行。
- 使用 PUT 语句将连续的数据行复制到插入缓冲区。
- 数据库服务器只有在缓冲区满的时候才会将这些行写到磁盘。您可以在缓冲区还未满的时候使用 CLOSE 、FLUSH 或 COMMIT WORK 语句刷新缓冲区。该主题会在 PUT 和 CLOSE 语句下做进一步讨论。
- 当不再需要 Insert 游标时使用 CLOSE 语句关闭游标。您必须在查询结束之前关闭Insert 游标,从而将任何已缓冲的行插入数据库。如果没有正确关闭游标,则会丢失数据。
- 使用 FREE 语句释放游标。FREE 语句会释放为 Insert 游标分配的资源。
使用 Insert 游标比直接嵌入 INSERT 语句更有效。此进程减少了程序和数据库服务器之间的通信,也加快了插入的速度。
Insert 游标还具有顺序游标特征。要创建 Insert 游标,可将一个顺序游标与 INSERT 语句额限制格式相关联。(有关更多信息,请参阅 Insert 语句。)以下示例包含声明顺序 Insert 游标的 GBase 8s ESQL/C 代码:
EXEC SQL declare ins_cur cursor for
insert into stock values (:stock_no,:manu_code,:descr,:u_price,:unit,:u_desc);
游标特性
您可以将一个游标声明为顺序游标(缺省值)、滚动游标(通过使用 SCROLL 关键字)或保持游标(通过使用 WITH HOLD 关键字)。SCROLL 和 WITH HOLD 关键字并不互相排斥。后面的章节解释了这些结构特征。
Select 或 Function 游标可以是一个顺序游标或滚动游标。Insert 游标只可以是一个顺序游标。在 ESQL/C 例程中,选择游标、函数游标和 Insert 游标可以有选择地成为保持游标。(在 SPL 例程中,所有的游标都是顺序游标,但是只有 Select 游标可以是保持游标。)
缺省创建顺序游标
如果只使用 CURSOR 关键字,则会创建一个顺序游标,它只可以从活动集合按顺序取得下一行。每次打开顺序游标时,顺序游标只可以从活动集合读一次。
如果正在为一个 Select 游标使用顺序游标,则在每次执行 FETCH 语句时,数据库服务器会返回当前行的内容,并且找到活动集合中的下一行。
以下示例在一个不兼容 ANSI 的数据库中创建了一个只读顺序游标,在一个兼容 ANSI 的数据库汇总创建了一个更新顺序游标:
EXEC SQL declare s_cur cursor for
select fname, lname into :st_fname, :st_lname
from orders where customer_num = 114;
Insert 游标也具有顺序游标的特征。要创建一个 Insert 游标,请将顺序游标与 INSERT 语句的限制格式相关联。(有关更多信息,请参阅 Insert 游标。)以下示例声明了一个 Insert 游标:
EXEC SQL declare ins_cur cursor for
insert into stock values
(:stock_no,:manu_code,:descr,:u_price,:unit,:u_desc);
使用 SCROLL 关键字创建滚动游标
使用 SCROLL 关键字创建滚动游标,它可以任何顺序取活动集合的行。
数据库服务器将游标的活动集合保留为一个临时表,直到关闭游标。您可以取得活动集合的第一行、最后一行或任何一个中间行,以及在不必关闭再重新打开游标的情况下重复取得一些行。(请参阅 FETCH。)
多用户系统中,在派生出活动集合的行的表中的行可能在打开游标之后有所更改,并且会在临时表中生成一个副本。如果在一个事务内使用滚动游标,那么通过将隔离级别设置为“可重复读取”或通过在事务期间以共享方式锁定整个表可以防止更改复制的行。(请参阅 SET ISOLATION 和 LOCK TABLE。)
以下示例为 SELECT 语句创建了一个滚动游标:
DECLARE sc_cur SCROLL CURSOR FOR SELECT * FROM orders;
您可以将 Select 和 Function 游标创建滚动游标,但不可为 Insert 游标创建滚动游标。不可将滚动游标声明为 FOR UPDATE。
使用 WITH HOLD 关键字创建保持游标
使用 WITH HOLD 关键字创建 hold 游标。Hold 游标允许跨多个事务对行的集合进行不中断的访问。
通常,所有的游标在事务结束时关闭。Hold 游标不会关闭;它在事务结束之后仍保持打开状态。
Hold 游标可以是一个顺序游标或(在 ESQL/C 中)滚动游标。
在 SPL 例程中 WITH HOLD 关键字只对 Select 游标有效。有关 DECLARE 语句在 SPL 例程中的语法,请参阅 在 SPL 例程中声明动态游标。
在 ESQL/C 中,可以使用 WITH HOLD 关键字声明 Select 和函数游标(具有顺序和滚动属性)也可以声明 Insert 游标。这些关键字在 DECLARE 语句中跟在 CURSOR 关键字之后。以下示例为 SELECT 创建了一个顺序 Hold 游标:
DECLARE hld_cur CURSOR WITH HOLD FOR
SELECT customer_num, lname, city FROM customer;
您可以如下面的 GBase 8s ESQL/C 代码示例所示使用选择保持游标。此代码段使用一个 Hold 游标作为主游标来扫描一个记录集合,使用一个顺序游标作为细节游标俩指向位于不同表中的记录。主游标扫描的记录是更新细节游标指向的记录的基础。在第一个 WHILE 循环的每个迭代的结束处的 COMMIT WORK 语句将 Hold 游标 c_master 保留为打开状态,但关闭顺序游标 c_detail 并释放所有锁。这种技术最小化了数据库服务器必须分配给锁和未完成的事务的资源,并且它使其它用户能够立即访问更新的行。
EXEC SQL BEGIN DECLARE SECTION;
int p_custnum, int save_status; long p_orddate;
EXEC SQL END DECLARE SECTION;
EXEC SQL prepare st_1 from
'select order_date from orders where customer_num = ? for update';
EXEC SQL declare c_detail cursor for st_1;
EXEC SQL declare c_master cursor with hold for
select customer_num from customer where city = 'Pittsburgh';
EXEC SQL open c_master;
if(SQLCODE==0) /* the open worked */
EXEC SQL fetch c_master into :p_custnum; /* discover first customer */
while(SQLCODE==0) /* while no errors and not end of pittsburgh customers */
{
EXEC SQL begin work; /* start transaction for customer p_custnum */
EXEC SQL open c_detail using :p_custnum;
if(SQLCODE==0) /* detail open succeeded */
EXEC SQL fetch c_detail into :p_orddate; /* get first order */
while(SQLCODE==0) /* while no errors and not end of orders */
{
EXEC SQL update orders set order_date = '08/15/94'
where current of c_detail;
if(status==0) /* update was ok */
EXEC SQL fetch c_detail into :p_orddate; /* next order */
}
if(SQLCODE==SQLNOTFOUND) /* correctly updated all found orders */
EXEC SQL commit work; /* make updates permanent, set status */
else /* some failure in an update */
{
save_status = SQLCODE; /* save error for loop control */
EXEC SQL rollback work;
SQLCODE = save_status; /* force loop to end */
}
if(SQLCODE==0) /* all updates, and the commit, worked ok */
EXEC SQL fetch c_master into :p_custnum; /* next customer? */
}
EXEC SQL close c_master;
使用 CLOSE 语句显式地关闭 Hold 游标,或使用 CLOSE DATABASE 或 DISCONNECT 语句隐式地关闭 Hold 游标。CLOSE DATABASE 语句关闭所有游标。