游標一般用來迭代結果集中的行
為了在一個過程中處理一個游標的結果,需要做以下事情:
在存儲過程塊的開頭部分 DECLARE 游標。
打開該游標。
將游標的結果取出到之前已聲明的本地變量中(隱式游標處理除外,在下面的 FOR 語句中將對此加以解釋)。
關閉該游標。(注意:如果現在不關閉游標,當過程終止時將隱式地關閉游標)。
注:游標的申明如果放在中間段,要用”begin。。。end;”.段分割標志分割開;
游標使用的步驟如下:(游標用循環體實現向下取數)
1、說明游標。說明游標的時候並不執行select語句。
declare <游標名> cursor for <select語句>;
2、打開游標。打開游標實際上是執行相應的select語句,把查詢結果讀取到緩沖區中。這時候游標處於活動狀態,指針指向查詢結果集的第一條紀錄。
open <游標名>;
3、推進游標指針並讀取當前紀錄。用fetch語句把游標指針向前推進一條紀錄,同時將緩沖區中的當前紀錄讀取出來送到變量中。fetch語句通常用在一個循環結構體中,通過循環執行fetch語句逐條取出結果集中的行進行處理。現在好多數據庫中,還允許任意方向任意步長易懂游標指針,而不僅僅是把游標指針向前推進一行了。
fetch <游標名> into <變量1>,<變量2>...
4、關閉游標。用close語句關閉游標,釋放結果集占用的緩沖區及其他資源。游標關閉后,就不再和原來的查詢結果集相聯系。但游標可以再次打開,與新的查詢結果相聯系。
close <游標名>;
基本結構:
定義游標:
DECLARE 游標名 CURSOR FOR
Select 語句;
打開游標:
OPEN 游標名;
取值:
FETCH 游標名 INTO 變量列表
游標例子:
--先插入測試數據
create table test(id int,city char(20))
insert into test values(1,'wuhan'),(2,'hangzhou'),(3,'chengdu')
create procedure Test(
out v_message varchar(500)
)
LANGUAGE SQL
BEGIN
DECLARE v_city char(20);
DECLARE v_count int;
SET v_message = '';
select count(*) into v_count from test;
BEGIN
DECLARE v_CUR CURSOR FOR SELECT city FROM test FOR READ ONLY;
OPEN v_CUR;
WHILE v_count > 0 DO
FETCH v_CUR INTO v_city;
set v_message = v_message ||v_city||' ';
set v_count = v_count -1;
end while;
END;
END@
運行結果為:
call Test(?)
completed successfully.
輸出參數的值
--------------------------
參數名: V_MESSAGE
參數值: wuhan hangzhou chengdu
返回狀態 = 0
Statement processed successfully in 4.39 secs.
除了這種結構外,還有一種使用for的游標的結構,例子如下:
create procedure Test(
out v_message varchar(500)
)
LANGUAGE SQL
BEGIN
DECLARE v_city char(20);
DECLARE v_count int;
SET v_message = '';
FOR V1 AS CURSOR1 CURSOR FOR select city as v_city from test
DO
set v_message = v_message||v_city||' ';
END FOR;
END@
運行結果:
call Test(?)
completed successfully.
輸出參數的值
--------------------------
參數名: V_MESSAGE
參數值: wuhan hangzhou chengdu
返回狀態 = 0
Statement processed successfully in 0.18 secs.
可以看到第二種游標使用起來非常簡單。但是它不能使用 with hold 選項,這個with hold有什么用呢?默認情況下,,在Commit和Rollback時,游標將被關閉。所以如果游標循環體內有Commit或Rollback時,不能使用for形式的游標。但是第一種游標可以使用,可以在第一種游標定義時加上with hold 選項,那么在游標循環體內Commit和Rollback時,游標也不會關閉。
使用Commit和Rollback也不會關閉的游標,如下:
DECLARE v_CUR CURSOR with hold for SELECT city FROM test FOR READ ONLY;
如果要修改游標當前記錄,需要定義可修改的游標,如下:
DECLARE v_CUR CURSOR for SELECT city FROM test FOR update;;
注意:for update 不能和 GROUP BY、 DISTINCT、 ORDER BY、 FOR READ ONLY及UNION, EXCEPT但 UNION ALL除外)一起使用。
在 DB2存儲 過程中,除了迭代結果集中的行以外,游標還可以做更多的事情。游標還可用於將結果集返回給調用程序或其他過程。
WITHOUT RETURN/WITH return 選項指定游標的結果表是否用於作為從一個過程中返回的結果集。
WITH RETURN TO CALLER 選項指定將來自游標的結果集返回給調用者,后者可以是另一個過程或一個客戶機應用程序。這是默認選項。
WITH RETURN TO CLIENT 選項指定將來自游標的結果集返回給客戶機應用程序,繞過任何中間的嵌套過程。
若要從一個過程中返回結果集,需要:
創建一個過程,創建時指定 DYNAMIC RESULT SETS 子句。
聲明游標,聲明時指定 WITH RETURN 子句。
打開該游標,並使之保持 open 狀態。
如果關閉該游標,則結果集將不能返回給調用者應用程序。
下例 演示了一個游標的聲明,該游標從一個過程中返回一個結果集:
create procedure Test(
out v_message varchar(500)
)
DYNAMIC RESULT SETS 1
LANGUAGE SQL
BEGIN
--異常處理
DECLARE SQLCODE INT;
DECLARE v_errCode INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET v_errCode = SQLCODE;
END;
--使用游標返回結果集
BEGIN
DECLARE v_cur CURSOR WITH HOLD WITH RETURN TO CALLER FOR
SELECT * FROM test;
OPEN v_cur;
END;
RETURN v_errCode;
END@
運行結果:
call Test(?)
completed successfully.
輸出參數的值
--------------------------
參數名: V_MESSAGE
參數值: -
結果集 1
--------------
ID CITY
----------- --------------------
1 wuhan
2 hangzhou
3 chengdu
3 條記錄已選擇。
返回狀態 = 0
游標的使用注意點:
注意 commit和rollback
使用游標時要特別注意如果沒有加with hold 選項,在Commit和Rollback時,該游標將被關閉。Commit 和Rollback有很多東西要注意。特別小心
游標的兩種定義方式
一種為
declare continue handler for not found
begin
set v_notfound = 1;
end;
declare cursor1 cursor with hold for select market_code from tb_market_code for update;
open cursor1;
set v_notfound=0;
fetch cursor1 into v_market_code;
while v_notfound=0 Do
--work
set v_notfound=0;
fetch cursor1 into v_market_code;
end while;
close cursor1;
這種方式使用起來比較復雜,但也比較靈活。特別是可以使用 with hold 選項。如果循環內有commit或rollback 而要保持該cursor不被關閉,只能使用這種方式。
另一種為
pcursor1: for loopcs1 as cousor1 cursor as
select market_code as market_code
from tb_market_code
for update
do
end for;
這種方式的優點是比較簡單,不用(也不允許)使用 open,fetch,close。
但不能使用with hold 選項。如果在游標循環內要使用commit,rollback則不能使用這種方式。如果沒有commit或rollback的要求,推薦使用這種方式(看來For這種方式有問題)。
修改游標的當前記錄的方法
update tb_market_code set market_code='0' where current of cursor1;
不過要注意將cursor1定義為可修改的游標
declare cursor1 cursor for select market_code from tb_market_code
for update;
for update 不能和 GROUP BY、 DISTINCT、 ORDER BY、 FOR READ ONLY及UNION, EXCEPT, or INTERSECT 但 UNION ALL除外)一起使用。
自己平時寫的實例:
create procedure db2inst1.test_overdue()
language sql
begin
declare v_count int;
declare v_id varchar(10);
declare v_shuld_repay varchar(8);
declare v_natural_mon varchar(8);
declare v_over_mon varchar(10);
declare v_day_num varchar(10);
begin
declare v_cur cursor for select id from OVERDUE_HIS for read only;
open v_cur;
set v_count=3;
--select MONTH(to_date(ACTUAL_REPAY,'YYYYMMDD'))-MONTH(to_date(shuld_repay,'YYYYMMDD'))+1 into v_count from db2inst1.overdue_his
--fetch v_cur into v_id;
while v_count>0
do
insert into OVERDUE_HIS_TAB(id,shuld_repay,natural_mon,over_mon,day_num)
select a.id,a.shuld_repay,
a.natural_mon,
0,
days(to_date(a.natural_mon,'yyyymmdd'))-days(to_date(a.shuld_repay,'yyyymmdd')) as day_num
from (select id,
shuld_repay,
to_char(TO_DATE(YEAR(to_date(shuld_repay,'YYYYMMDD')+v_count month)||'-'||MONTH(to_date(shuld_repay,'YYYYMMDD')+v_count month)||'-'||'01','YYYYMMDD')-1 day,'YYYYMMDD') as natural_mon
from OVERDUE_HIS) a;
set v_count=v_count-1;
end while;
close v_cur;
end;
end;