環境區域是用來處理SQL語句的一個oracle存儲區域。游標是指向它的指針或句柄。通過游標,PL/SQL程序可以控制這個環境區域中被處理的語句。
Oracle中的游標有兩種:顯式游標、隱式游標。
顯示游標是用cursor...is命令定義的游標,它可以對查詢語句(select)返回的多條記錄進行處理,而隱式游標是在執行插入(insert)、刪除(delete)、修改(update)和返回單條記錄的查詢(select)語句時由PL/SQL自動定義的。
1、顯式游標操作
顯式游標在塊定義部分、包或子程序中聲明。當聲明了顯式游標后,可以通過以下三條命令控制顯式游標的操作:打開游標、推進游標、關閉游標。
(1)聲明顯式游標
--例1
declare
v_auths auths%rowtype;
v_code auths.author_code%type;
cursor c_auths is
select * from auths where author_code=v_code;
上例是將PL/SQL變量綁定在WHERE子句中,下面將游標參數綁定在游標的WHERE子句中:
--例2
delcare
cursor c_auths(p_code auths.author_code%type) is
select * from auths where author_code=p_code;
(2)打開顯式游標
游標操作的第一步是打開游標。
例1,下面的語句是打開上節例1中聲明的顯式游標c_auths;
begin
--在打開游標前為綁定變量賦值。
v_code:='A00001';
--打開游標。
open c_auths;
例2,如果對于一個帶參數的游標
begin
--打開游標時將參數傳入。
open c_auths('A00001');
打開一個已打開的游標也是合法的。當第二次打開游標時,PL/SQL先自動關閉游標,然后再打開。一次打開多個游標也是PL/SQL所允許的。
(3)推進顯式游標
當打開顯式游標后,就可以使用FETCH語句來推進游標,返回查詢結果集中的一行。每執行完一條FETCH語句后,顯式游標會自動指向查詢結果集的下一行。
(4)關閉顯式游標
當整個結果集都檢索完以后,應當關閉游標。關閉游標用來通知PL/SQL游標操作已經結束,并且釋放游標所占用的資源(結果集所使用的資源空間)。
2、游標的屬性
游標有四個屬性:%found、%notfound、%isopen和%rowcount。要注意這些屬性只能使用在過程性語句中,而不能使用在SQL語句中。
表tableattribute,表中有兩列column1(number類型)和column2(varchar2類型),現在向表中插入兩條記錄:
insert into tableattribute values(10,'first');
insert into tableattribute values(20,'second');
...
3、顯式游標的推進循環
delcare
--聲明一個變量,這個變量用來接收游標返回的結果集。
v_salary auths.salary%type;
v_code auths.author_code%type;
/*聲明游標,該游標的查詢結果集是作家代碼為"A00001"到"A00006"的工資值。*/
cursor c_salary is select salary,author_code from auths where author_code<='A00006';
begin
--打開游標,并初始化結果集
open c_salary;
loop
--推進游標,將游標的查詢結果集中的一行存到變量v_salary中。
fetch c_salary into v_salary,v_code;
--當結果集中沒有行時退出循環。
exit when c_salary%notfound;
--如果查詢到的作家工資小于或等于200,則增加該作家的工資值。
if v_salary<=200 then
update auths set salary=salary+50 where author_code=v_code;
end if;
end loop;
--關閉游標,釋放游標占用資源。
close c_salary;
--提交所做的修改。
commit;
end;
PL/SQL還提供了一種簡單類型的循環,可以自動控制游標的打開、推進和關閉,叫做游標的FOR循環。
delcare
cursor c_salary is
select salary form auths where author_code<='A00006';
begin
--開始游標FOR循環,隱含地打開c_salary游標。
for v_salary in c_salary loop
--一個隱含的fetch語句在這里被執行。
if v_salary.salary<=200 then
update auths set salary=salary+50 where salary=v_salary.salary;
end if;
--在循環繼續前,一個隱含的c_auths%notfound被檢測。
end loop;
--現在循環已經結束,c_auths游標的一個隱含的close操作被執行。
commit;
end;
使用current of cursor子句作為條件
delcare
--聲明游標時在select語句中必須加for update of子句。
cursor c_salary is
select salary form auths where author_code<'A00006' for update of salary;
begin
for v_salary in c_salary loop
if v_salary.salary<=200 then
--下面的update語句中的current of子句用來表明結果集的當前行。
update auths set salary=salary+50 where current of c_salary;
end if;
end loop;
commit;
end;
如果在游標的FOR循環中使用子查詢,則不用在塊定義部分聲明顯式游標,在FOR循環中子查詢隱含聲明了一個顯式游標。
begin
--在下面的FOR循環中隱含地聲明了一個游標c_salary。
for c_salary in
(select salary form auths where author_code<='A00006') loop
if c_salary.salary<=200 then
update auths set salary=salary+50 where salary=c_salary.salary;
end if;
end loop;
commit;
end;
4、隱式游標處理
PL/SQL隱式地打開SQL游標,并在它內部處理SQL語句,然后關閉它。SQL游標用來處理insert、update、delete以及返回一行的select...into語句。
一個SQL游標不管是打開還是關閉,open、fetch和close命令都不能操作它。SQL游標與顯式游標類似,也有四個一樣的屬性。當打開SQL游標之前,SQL游標的屬性都為NULL。
begin
update auths set entry_date_time=sysdate where author_code='A00017';
--如果update語句中修改的行不存在(SQL%notfound返回值為true),則向auths表中插入一行。
if sql%nofound then
insert into auths(author_code,name,sex,birthdate,salary,entry_date_time)
values('A000017','qiuys',1,'30-apr-40',88.5,sysdate);
end if;
end;
--如果update語句中修改的行不存在(sql%rowcount=0)
declare
v_birthdate date;
begin
select birthdate into v_birthdate from auths where name='qiuys';
--如果查詢到一條記錄,則刪除該記錄。
if sql%found then
delete from auths where name='qiuys';
end if;
exception
when no_data_found then
dbms_output.put_line('該記錄不存在');
when too_many_rows then
dbms_output_line('存在同名的作家');
end;
5、游標變量
到目前為止前面所有顯式游標的例子都是靜態游標-即游標與一個SQL語句關聯,并且該SQL語句在編譯時已經確定。
而游標變量是一個引用類型(REF)的變量。
(1)游標變量的聲明
declare
--使用%rowtype定義一個游標變量類型。
type t_authsref is ref cursor return auths%rowtype;
--定義一個記錄類型。
type t_coderecord is record(
author_code article.author_code%type,
article_code article.article_code%type);
--聲明一個記錄類型的變量。
v_code t_coderecord;
--使用t_coderecord作為一個游標變量類型的結果集類型。
type t_coderef is ref cursor return t_codeRecord;
--使用v_code作為一個游標變量類型的結果集類型。
type t_coderef2 is ref cursor return v_code%type;
--使用上面的類型聲明的兩個游標變量。
v_authcv t_authsref;
v_codecv t_coderef;
PL/SQL2.8以上版本中,可以使用一個沒有指定結果集類型的游標變量(沒有RETURN)來指定多個不同類型的查詢。
type t_authsref if ref cursor;
v_cursorvar t_authsref;--聲明一個該類型的變量。
(2)打開游標變量
為了將一個游標變更與一個具體的select語句聯系起來,open的語法中增加了一個select語句。
open cursor_variable for select_statement;
declare
type t_authorsref is ref cursor return auths%rowtype;
v_authscv t_authorsref;
--然后打開
open v_authscv for select * from auths;
3)推進游標變更
(4)關閉游標變更
該操作用來釋放查詢所占用的資源。但沒有釋放游標變量占用的存儲空間。當變量超出作用域時,它所占用的空間才被釋放掉。
下面的塊中定義了一個沒有指定結果集的游標變量,這樣我們就可以使用這個游標變量指向不同的查詢,并能夠返回不同的記錄類型:
set serveroutput on size 100000 --設置存儲緩沖區大小。
declare
/*定義游標變更類型t_curref,該游標變量類型沒有指定結果集類型,所以該游標變量類型的變量可以返回不同的PL/SQL記錄類型。*/
type t_curref is ref cursor;
--聲明一個游標變量類型的變量
c_cursorref t_curref;
--定義PL/SQL記錄類型t_authorrec,該類型的變量用來接收游標變量的返回值。
type t_authorrec is record(
authorcode auths.author_code%type,
name auths.name%type);
--定義PL/SQL記錄類型t_articlerec,該類型的變量也用來接收游標變量的返回值。
type t_articlerec is record(
authorcode article.author_code%type,
title artitle.title%type);
--聲明兩個記錄類型變量。
v_author t_authorrec;
v_article t_articlerec;
begin
--打開游標變量c_cursorref,返回t_authorrec類型的記錄。
open c_cursorref for
select author_code,name from auths where author_code in('A00001','A00002','A00003','A00004','A00005');
--推進游標變量
fetch c_cursorref into v_author;
--游標變量的推進循環。
while c_cursorref%found loop
--將作家代碼和相應的作家名字輸出到屏幕上。
dbms_output.put(v_author.authorcode||':'||v_author.name||' ');
fetch c_cursorref into v_author;
end loop;
dbms_output.new_line;--向屏幕上輸出一個回車行。
--關閉游標變量,僅僅將游標變量指定的資源釋放掉,游標變量本身的存儲空間沒有釋放掉。
close c_cursorref;
--再次打開游標變量,返回t_articlerec類型的記錄。
open c_cursorref for
select author_code,title from article
where author_code in('A00001','A00002','A00003','A00004','A00005');
fetch c_cursorref into v_article;
while c_cursorref%found loop
...
end loop;
close c_cursorref;
end;
注意,在上例中,第一次關閉游標變量是可省略的,因為在第二次打開游標變量時,就將第一次的查詢丟失掉了。而且游標變量也有游標屬性,通常在推進游標變量時使用這些游標屬性,例如上例使用了%found屬性。