游標 游標存在意義:解決“select *”返回空、多行記錄問題,但凡select,就可能多行結果集,也就需要用游標。 游標分4步走:cursor、open、fetch、close 可能省略open、close,用for ... in ... loop ... end loop; 1、靜態游標: ( ...
游標
游標存在意義:解決“select *”返回空、多行記錄問題,但凡select,就可能多行結果集,也就需要用游標。
游標分4步走:cursor、open、fetch、close
可能省略open、close,用for ... in ... loop ... end loop;
1、靜態游標:
(1)隱式游標:
在PL/SQL中DML(select into、insert、update、delete)時,Oracle隱式定義一個叫SQL的游標,自動聲明、打開、關閉。
①sql%found
如果DML語句影響一行或多行,則sql%found為true
例如:
declare
empno emp.empno%type;
begin
select empno into empno -- 對於DML之select into
from emp
where empno =7369;
ifsql%foundthen
dbms_output.put_line('有一行記錄');-- 用sql%found、sql%notfound判斷是否返回一行記錄
endif;
Exception
when no_data_found then
dbms_output.put_line('查詢返回空行');-- 用no_data_found判斷是否返回空行記錄
when too_many_rows then
dbms_output.put_line('查詢返回多行');-- 用too_many_rows判斷是否返回多行記錄
When others then
dbms_output.put_line(‘系統錯誤');
end;
例如:
declare
total integer;
e_null exception;--自定義的異常
e_toomanyrow exception;
begin
select count(*) into total from emp where emp.deptno=20;
if total>1 then
raise e_toomanyrow;--拋出異常
elsif total<=0 then
raise e_null;
end if;
--捕獲並處理異常
Exception when e_toomanyrow then
dbms_output.put_line('Exception:返回多行記錄');
when e_null then
dbms_output.put_line('Exception:無返回記錄');
when others then
dbms_output.put_line('Exception:其他異常');
end;
②sql%notfound
如果DML語句沒有影響任何行,則sql%notfound為true
第一種情況:DML是insert、delete、update
例如:
declare
begin
deletefrom emp
where empno =47;
ifsql%notfoundthen
dbms_output.put_line('未找到值');
else
dbms_output.put_line('已經刪除');
endif;
end;
第二種情況:DML是select into
PL/SQL中的“select into”的結果必須“有且只有一行”,該行結果可以賦給列變數、或記錄變數
例如:
declare
type type_emp isrecord
(
empno emp.empno%type,
ename emp.ename%type,
dname dept.dname%type,
sal emp.sal%type
);
v_emp type_emp;
begin
select empno, ename, dname, sal into v_emp
from emp innerjoin dept
on emp.deptno = dept.deptno
where ename ='SMITH';
--if sql%notfound then
--dbms_output.put_line('查無此人'); -- 永遠執行不到,因為查詢返回空行時觸發no_data_found異常
--else
dbms_output.put_line(v_emp.empno||'*'||v_emp.ename||'*'||v_emp.dname||'*'||v_emp.sal);
--end if;
exception
whenno_data_foundthen
dbms_output.put_line('查無此人');
whenothersthen
dbms_output.put_line(sqlerrm);
end;
註意:
如果查詢返回空行,PL/SQL拋出no_data_found異常;
如果查詢返回多行,PL/SQL拋出too_many_rows異常;
所以在DML之“select into”引發異常時,不能使用sql%notfound屬性查看DML是否影響了行。
③sql%rowcount
declare
empno emp.empno%type;
begin
select empno into empno
from emp
where empno =7788;
ifsql%rowcount>0then
dbms_output.put_line('從表中選擇了'||sql%rowcount||'行');
else
dbms_output.put_line('從表中未選擇行');
endif;
end;
④sql%isopen
SQL%ISOPEN返回一個布爾值,如果游標打開,則為TRUE, 如果游標關閉,則為FALSE.對於隱式游標而言SQL%ISOPEN總是FALSE,這是因為隱式游標在DML語句執行時打開,結束時就立即關閉。
(2)顯式游標
重點在“步驟”:定義游標,打開游標, 抓取數據,關閉游標;
①游標%found
如果最後一條fetch語句成功提取行,則“游標%found”為true
②游標%notfound
如果最後一條fetch語句未能提取行,則“游標%notfound”為true
例如:
declare
cursor cur is
select ename,job from emp; --這裡enamel,job 兩列
item emp.ename%type; --定義item是emp表ename列的類型
item2 emp.job%type; --定義item2 是emp表job列類型。 這裡聲明的變數必須對性查詢--出的列對應。
begin
open cur;
loop
fetch cur into item,item2; --捕捉了游標cur 的元素放入,item,item2中。
exitwhencur%notfound; -- 當cur游標都便利完就exit退出。
endloop;
dbms_output.put_line(item||item2);
close cur; --註意關閉游標
end;
③游標%isopen,判斷該游標是否被打開;
例如:
Declare
--聲明一個游標
cursor cur is
select* from emp where deptno =10;
v_emp cur%rowtype;
begin
ifnot cur%isopenthen
open cur;--如果游標沒有被打開,那麼就代開游標
endif;
--迴圈,從游標中抓取數據
loop
fetch cur into v_emp;
exitwhen cur%notfound;--當最後一條數據被獲取後,exit退出迴圈
dbms_output.put_line(v_emp.ename||':'||v_emp.sal);
endloop;
close cur;--關閉游標
end;
④游標%rowcount:返回游標查詢記錄數;
註意:SQL%ROWCOUNT:在執行任何DML語句之前,SQL%ROWCOUNT的值都是NULL,對於SELECT INTO語句,如果執行成功,SQL%ROWCOUNT的值為1,如果沒有成功,SQL%ROWCOUNT的值為0,同時產生一個異常NO_DATA_FOUND.
例如:(上面例子多了幾行代碼) declare
cursor cur is
select*
from emp
where deptno =10;
v_emp cur%rowtype;
a number;
begin
ifnot cur%isopenthen
open cur;
endif;
loop
fetch cur into v_emp;
exitwhen cur%notfound;
dbms_output.put_line(v_emp.ename||':'||v_emp.sal);
endloop;
-----------------------------------------------------------
--下麵是sql%rowcount
select empno into a from emp where empno='7369';
dbms_output.put_line(sql%rowcount);--如果查詢sql 有記錄數,此時返回1否則為null
dbms_output.put_line(cur%rowcount);--返回游標查詢的最終記錄數
-----------------------------------------------------------
close cur;
end;
例如:
declare
cursor cur(i varchar2)is
select*from emp where deptno=i ;
item cur%rowtype;
begin
for r in cur('10') -- 註意這裡的in。。。 後面跟的是游標 cur 而不是 tiem
--這裡可以寫成:for admin in cur(&部門編號);用通配符,使鍵盤鍵入內容。
loop
dbms_output.put_line(r.ename|| cur%rowcount);--其中cur%rowcount 是有標明%rowcount 顯示游標序列
endloop;
end;
⑤使用顯式游標更新或刪除行:
declare
-- select語句必須只包括一個表
-- 某表不能是“含有distinct、order by的子查詢”查詢來的結果集(視圖)
cursor 游標名 is select 某列 from 某表 for update[ of 某列];
變數 某表.某列%type;
變數 某表%rowtype;
變數 游標%type;
變數 游標%rowtype;
begin
open 游標;
loop
fetch 游標 into 變數;
exit when 游標%notfound;
if 某條件 then
-- update、delete語句只有在打開游標、提取特定行之後才能使用
-- update語句中使用的列必須出現在“select-for update of”語句中
update 某表 set 某列 = 某值 where current of 游標;
end if;
end loop;
close 游標;
commit;
end;
例如:
-- 如果sal < 2000,長工資1000
declare
cursor cur is
select sal from emp
where sal <2000forupdate;
cursor bbb is
select*from emp;
begin
for v_sal in cur
loop
update emp
set sal = sal +1000
wherecurrentof cur;
endloop;
Commit;
for v_emp in bbb
loop
dbms_output.put_line(v_emp.ename||'*'||v_emp.sal);
endloop;
end;
動態游標:
(1)弱類型游標
例如:
declare -- //弱類型游標
type cursor_type isrefcursor;
cur cursor_type;
temp emp%rowtype;
begin
open cur for
select*from emp;
loop
fetch cur into temp;
exitwhencur%notfound;
dbms_output.put_line(temp.empno||temp.ename);
endloop;
end;
例如:
declare
type cur_type isrefcursor;-- 弱類型游標 通過is ref 定義
cur cur_type;
v_emp emp%rowtype;
v_dept dept%rowtype;
selection varchar2(1):= upper('D');
begin
if selection ='E'then
open cur for
select*from emp;
loop
fetch cur into v_emp;
exitwhen cur%notfound;
dbms_output.put_line(v_emp.ename||':'||v_emp.sal);
endloop;
close cur;
elsif selection ='D'then
open cur for
select*from dept;
loop
fetch cur intov_dept;
exitwhen cur%notfound;
dbms_output.put_line(v_dept.deptno||':'||v_dept.dname);
endloop;
close cur;
else
null;
endif;
end;
(2)強類型游標
例如:
declare --強類型游標 強制返回內容
type record_type isrecord(empno number,ename varchar2(20));
type cursor_type isrefcursorreturn record_type;
rec record_type;
cur cursor_type;
begin
open cur for
select empno,ename from emp;
loop
fetch cur into rec;
exitwhen cur%notfound;
dbms_output.put_line(rec.empno||' '||rec.ename);
endloop;
end;
例如:
-- 用強類型游標顯示員工姓名和工資
declare
type record_type isrecord(namevarchar2(10), sal number);-- 記錄類型
type cur_type isrefcursorreturn record_type;-- 強類型游標,將來必須返回record_type類型的結果集
emp_record record_type;-- 記錄類型變數
emp_cur cur_type;-- 強類型游標變數
begin
open emp_cur for
select ename, sal from emp;-- 動態SQL兩邊的單引號可以省略,必須返回record_type類型的結果集
loop
fetch emp_cur into emp_record;-- 把record_type類型的結果集向record_type類型的變數中填充
exitwhen emp_cur%notfound;
dbms_output.put_line(emp_record.name||':'||emp_record.sal);
endloop;
close emp_cur;
end;