target
了解什么是游標(biāo)
掌握顯示游標(biāo)的使用
掌握隱士游標(biāo)的使用
1. 什么是游標(biāo)
游標(biāo)的使用可以讓用戶像操作數(shù)組一樣操作査詢出來(lái)的數(shù)據(jù)集,這使得使用PL/SQL編程 更加方便。
1.1 游標(biāo)的概念
可以將游標(biāo)(Cursor)形象地看成一個(gè)變動(dòng)的光標(biāo)。它實(shí)際上是一個(gè)指針,它在一段 Oracle存放數(shù)據(jù)査詢結(jié)果集或數(shù)據(jù)操作結(jié)果集的內(nèi)存中,這個(gè)指針可以指向結(jié)果集中的任何一 條記錄,這樣就可以得到它所指向的數(shù)據(jù)了。
初始時(shí)它指向首記錄。這種模型很像編程語(yǔ)言中的數(shù)組。
可以簡(jiǎn)單地理解游標(biāo)為指向結(jié)果集記錄的指針,利用游標(biāo)可以返回它當(dāng)前指向的行記錄 (只能返回一行記錄)。如果要返回多行,那么需要不斷地滾動(dòng)游標(biāo),把想要的數(shù)據(jù)査詢一遍。
1.2游標(biāo)的種類
Oracle中游標(biāo)分為靜態(tài)游標(biāo)和REF游標(biāo)兩類。其中,靜態(tài)游標(biāo)就像一個(gè)數(shù)據(jù)快照,打開(kāi)游標(biāo)后的結(jié)果集是對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)的一個(gè)備份,數(shù)據(jù)不隨著對(duì)表執(zhí)行DML操作后而改變。從這個(gè)特性來(lái)說(shuō),結(jié)果集是靜態(tài)的。
靜態(tài)游標(biāo)包含如下兩種類型:
- 顯式游標(biāo)
- 隱式游標(biāo)
2. 顯示游標(biāo)
2.1 語(yǔ)法
CURSOR cursor_name
[(parameter_name datatype,...))
IS select_statement;
【語(yǔ)法說(shuō)明】
- CURSOR cursor_name:聲明游標(biāo),cursor_name是游標(biāo)的名稱。
- parameter_name:參數(shù)名稱。
- datatype:參數(shù)類型。
- select_statement:游標(biāo)關(guān)聯(lián)的SELECT語(yǔ)句,但該語(yǔ)句不能是SELECT...INTO...語(yǔ)句。
2.2 顯式游標(biāo)的屬性
利用游標(biāo)屬性可以得到游標(biāo)執(zhí)行的相關(guān)信息。顯式游標(biāo)有以下4個(gè)屬性:
- %ISOPEN:用于判斷游標(biāo)是否打開(kāi),如果已經(jīng)打開(kāi)則返回TRUE,如果游標(biāo)未打開(kāi)則 返回FALSE。
- %FOUND:此屬性可用來(lái)檢測(cè)行數(shù)據(jù)是否有效。如果有效屬性返回TRUE,否則返回FALSE。
- %NOTFOUND:與%FOUND屬性恰好相反,如果沒(méi)有提取出數(shù)據(jù)則返回TRUE,否則返回FALSE。
- %ROWCOUNT:累計(jì)到當(dāng)前為止使用FETCH提取數(shù)據(jù)的行數(shù)。
??:%ISOPEN的使用
declare
cursor product_isopen_cur is
select * from productinfo;
cur_product_cd productinfo%rowtype;
begin
if product_isopen_cur%isopen then
fetch product_isopen_cur into cur_product_cd;
dbms_output.put_line('產(chǎn)品名稱' || cur_product_cd.productname || '產(chǎn)品價(jià)格:'|| cur_product_cd.productprice );
else
dbms_output.put_line('游標(biāo)沒(méi)有打開(kāi)!');
end if;
end;
/
輸出:
游標(biāo)沒(méi)有打開(kāi)!
PL/SQL 過(guò)程已成功完成。
2.3 游標(biāo)的使用步驟
(1) 聲明游標(biāo)
聲明游標(biāo)主要用來(lái)給游標(biāo)命名并且使得游標(biāo)關(guān)聯(lián)一個(gè)査詢。具體語(yǔ)句如下:
declare
cursor cursor_Name is select_statement;
(2) 打開(kāi)游標(biāo)
游標(biāo)中任何對(duì)數(shù)據(jù)的操作都是建立在游標(biāo)被打開(kāi)的前提下。打開(kāi)游標(biāo)初始化了游標(biāo)指針, 游標(biāo)一旦打開(kāi),其結(jié)果集都是靜態(tài)的。也就是說(shuō),結(jié)果集此時(shí)不會(huì)反映出數(shù)據(jù)庫(kù)中對(duì)數(shù)據(jù)進(jìn)行 的增加、刪除、修改操作。具體語(yǔ)句如下:
OPEN cursor_Name
(3) 讀取數(shù)據(jù)
讀取數(shù)據(jù)要利用FETCH語(yǔ)句完成,它可以把游標(biāo)指向位置的記錄放入到PL/SQL聲明的變量當(dāng)中。
它只能取出指針當(dāng)前行的記錄。
正常情況下,F(xiàn)ETCH要和循環(huán)語(yǔ)句一起使用,這樣指針會(huì)不斷前進(jìn),直到某個(gè)條件不符合要求而退出。
使用FETCH時(shí)游標(biāo)屬性%ROWCOUNT會(huì)不斷累加。具體語(yǔ)句如下:
FETCH cursor_Name INTO Record_Name;
(4) 關(guān)閉游標(biāo)
關(guān)閉某個(gè)名稱的游標(biāo)。此時(shí)釋放資源,結(jié)果集中的數(shù)據(jù)將不能做任何操作。
??:創(chuàng)建一個(gè)簡(jiǎn)單的游標(biāo)
declare
cursor pdct_cur
is select * from productinfo;
product_cd productinfo%rowtype;
begin
open pdct_cur;
fetch pdct_cur into product_cd;
dbms_output.put_line(product_cd.productid || '-' || product_cd.productname || '-' || product_cd.productprice);
close pdct_cur;
end;
/
輸出:
1-華為Mate40Pro-7999
PL/SQL 過(guò)程已成功完成。
只輸出了一條記錄,也就是前面說(shuō)到的游標(biāo)指針默認(rèn)指向第一條記錄,若想獲取其他記錄,要用循環(huán)去移動(dòng)指針,然后讀取記錄。
2.4 游標(biāo)中的loop語(yǔ)句
通常游標(biāo)提取的數(shù)據(jù)不會(huì)是一條,而是多條記錄。這樣就需要一個(gè)遍歷結(jié)果集的方式--loop語(yǔ)句。
??:使用loop循環(huán)獲取多條記錄
declare
cursor product_loop_cur
is select productid,productname,productprice from productinfo
where productprice > 3000;
cur_productid productinfo.productid%type;
cur_productname productinfo.productname%type;
cur_productprice productinfo.productprice%type;
begin
open product_loop_cur;
loop
fetch product_loop_cur into cur_productid,cur_productname,cur_productprice;
exit when product_loop_cur%notfound;
dbms_output.put_line('產(chǎn)品id:' || cur_productid || ' 產(chǎn)品名稱:' || cur_productname || ' 產(chǎn)品價(jià)格:' || cur_productprice);
end loop;
close product_loop_cur;
end;
/
輸出:
產(chǎn)品id:1 產(chǎn)品名稱:華為Mate40Pro 產(chǎn)品價(jià)格:7999
產(chǎn)品id:2 產(chǎn)品名稱:華為P40Pro 產(chǎn)品價(jià)格:6999
產(chǎn)品id:4 產(chǎn)品名稱:華為MatePadPro 產(chǎn)品價(jià)格:4999
產(chǎn)品id:5 產(chǎn)品名稱:華為智慧屏 產(chǎn)品價(jià)格:8999
產(chǎn)品id:6 產(chǎn)品名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999
產(chǎn)品id:7 產(chǎn)品名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999
PL/SQL 過(guò)程已成功完成。
2.5 使用bulk collect和for語(yǔ)句的游標(biāo)
游標(biāo)中通常使用FETCH...INTO...語(yǔ)句提取數(shù)據(jù),這種方式是單條數(shù)據(jù)提取,在數(shù)據(jù)量很大的情況下執(zhí)行效率不是很理想。而FETCH...BULK COLLECT INTO語(yǔ)句可以批量提取數(shù)據(jù),在數(shù)據(jù)量大的情況下它的執(zhí)行效率比單條提取數(shù)據(jù)的高。
??:
declare
cursor product_collect_cur
is select * from productinfo;
type prot_table is table of productinfo%rowtype;
pdct_rd prot_table;
begin
open product_collect_cur;
loop
--從游標(biāo)中提取數(shù)據(jù),每次取2條
fetch product_collect_cur bulk collect into pdct_rd limit 2;
for i in 1..pdct_rd.count loop
dbms_output.put_line('產(chǎn)品id:' || pdct_rd(i).productid || ' 產(chǎn)品名稱:' || pdct_rd(i).productname || ' 產(chǎn)品價(jià)格:' || pdct_rd(i).productprice);
end loop;
exit when product_collect_cur%notfound;
end loop;
close product_collect_cur;
end;
/
2.6 使用 CURSOR FOR LOOP
游標(biāo)很多機(jī)會(huì)都是迭代結(jié)果集,在PL/SQL這個(gè)過(guò)程中可以使用更簡(jiǎn)單的方式實(shí)現(xiàn), CURSOR FOR LOOP不需要特別的聲明變量。
declare
cursor cfl is
select productname,productprice from productinfo
where productprice > 3000;
begin
--把游標(biāo)里面的數(shù)據(jù)放到curcfl中,該類型是%rowtype
for curcfl in cfl loop
dbms_output.put_line('名稱:' || curcfl.productname || ' 產(chǎn)品價(jià)格:' || curcfl.productprice);
end loop;
end;
/
輸出:
名稱:華為Mate40Pro 產(chǎn)品價(jià)格:7999
名稱:華為P40Pro 產(chǎn)品價(jià)格:6999
名稱:華為MatePadPro 產(chǎn)品價(jià)格:4999
名稱:華為智慧屏 產(chǎn)品價(jià)格:8999
名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999
名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999
PL/SQL 過(guò)程已成功完成。
2.7 帶參數(shù)的游標(biāo)
在使用顯式游標(biāo)時(shí)是可以指定參數(shù)的,指定的參數(shù)包括參數(shù)的順序和參數(shù)的類型。參數(shù)可 以傳遞給游標(biāo)在査詢中使用,這樣就方便了用戶根據(jù)不同的査詢條件進(jìn)行査詢,也方便了游標(biāo) 在存儲(chǔ)過(guò)程中的使用。
declare
cur_productname productinfo.Productname%TYPE := '華為';
cur_productprice productinfo.Productprice%TYPE := 1200;
cur_prodrcd productinfo%ROWTYPE;
CURSOR pdct_parameter_cur (name VARCHAR,price NUMBER) IS
SELECT * FROM PRODUCTINFO
WHERE productname like name||'%' AND productprice > price;
begin
OPEN pdct_parameter_cur(cur_productname,cur_productprice);
Loop
FETCH pdct_parameter_cur INTO cur_prodrcd;
EXIT WHEN pdct_parameter_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('產(chǎn)品ID, ' || cur_prodrcd.productid || ' 產(chǎn)品名稱: ' || cur_prodrcd.productname || ' 產(chǎn)品價(jià)格:'|| cur_prodrcd.productprice);
END LOOP;
CLOSE pdct_parameter_cur;
END;
/
輸出:
產(chǎn)品ID, 1 產(chǎn)品名稱: 華為Mate40Pro 產(chǎn)品價(jià)格:7999
產(chǎn)品ID, 2 產(chǎn)品名稱: 華為P40Pro 產(chǎn)品價(jià)格:6999
產(chǎn)品ID, 3 產(chǎn)品名稱: 華為榮耀10 產(chǎn)品價(jià)格:1999
產(chǎn)品ID, 4 產(chǎn)品名稱: 華為MatePadPro 產(chǎn)品價(jià)格:4999
產(chǎn)品ID, 5 產(chǎn)品名稱: 華為智慧屏 產(chǎn)品價(jià)格:8999
產(chǎn)品ID, 6 產(chǎn)品名稱: 華為智慧屏2代 產(chǎn)品價(jià)格:9999
產(chǎn)品ID, 7 產(chǎn)品名稱: 華為智慧屏2代 產(chǎn)品價(jià)格:9999
PL/SQL 過(guò)程已成功完成。
3. 隱式游標(biāo)
隱式游標(biāo)和顯式游標(biāo)有所差異,它雖然沒(méi)有顯式游標(biāo)一樣的可操作性,但在實(shí)際的工作當(dāng) 中也經(jīng)常用到。
3.1隱式游標(biāo)的特點(diǎn)
每當(dāng)運(yùn)行SELECT或DML語(yǔ)句時(shí),PL/SQL會(huì)打開(kāi)一個(gè)隱式的游標(biāo)。隱式游標(biāo)不受用戶的控制,這一點(diǎn)和顯式游標(biāo)有明顯的不同。下面列出了隱式游標(biāo)和顯式游標(biāo)的不同處:
- 隱式游標(biāo)由PL/SQL自動(dòng)管理,
- 隱式游標(biāo)中的%ISOPEN屬性永遠(yuǎn)返回FALSE
- 隱式游標(biāo)的默認(rèn)名稱是SQL
- SELECT或DML操作產(chǎn)生隱式游標(biāo):
- 隱式游標(biāo)的屬性值始終是最新執(zhí)行的SQL語(yǔ)句的。
DECLARE
cur_productname productinfo.Productname%TYPE;
cur_productprice productinfo.Productprice%TYPE;
BEGIN
SELECT productname, productprice INTO cur_productname,cur_productprice
FROM PRODUCTINFO
where productid = '1';
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('產(chǎn)品名稱:' || cur_productname || ', 產(chǎn)品價(jià)格:' || cur_productprice);
END IF ;
END;
/
輸出:
產(chǎn)品名稱:華為Mate40Pro, 產(chǎn)品價(jià)格:7999
PL/SQL 過(guò)程已成功完成。
3.2 游標(biāo)中使用異常處理
使用游標(biāo)時(shí),某些情況下得到的數(shù)據(jù)超出了控制范圍,如果不加處理會(huì)出現(xiàn)腳本執(zhí)行中斷的情況。這種情況下,腳本開(kāi)發(fā)者通常會(huì)使用異常處理來(lái)維護(hù)腳本的穩(wěn)定性。
??:在游標(biāo)中使用異常處理
DECLARE
cur_productname productinfo.Productname%TYPE;
cur_productprice productinfo.Productprice%TYPE;
BEGIN
SELECT productname, productprice INTO cur_productname,cur_productprice
FROM PRODUCTINFO
where productid ='1';
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('產(chǎn)品名稱:' || cur_productname || ' ,產(chǎn)品價(jià)格:' || cur_productprice);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('沒(méi)有數(shù)據(jù)!');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('數(shù)據(jù)過(guò)多!');
END;
/
輸出:
產(chǎn)品名稱:華為Mate40Pro ,產(chǎn)品價(jià)格:7999
PL/SQL 過(guò)程已成功完成。
4. 游標(biāo)案例
本案例涉及到兩張表,分別是EMP(員工表)和DEPT(部門表)。
要求:
利用游標(biāo)轉(zhuǎn)換這兩張表的數(shù)據(jù),要求把月薪高于1500的,部門地址為“CHICAGO”和“NEW YORK”的員工放到EMP_TEMP表中,月薪低于2500的上調(diào)5%。
分析:
第一步:建表 EMP_TEMP
第二步:創(chuàng)建游標(biāo)存儲(chǔ)符合條件的數(shù)據(jù)。
第三步:把符合要求的數(shù)據(jù)放進(jìn)新表
第四步:把工資上調(diào)
操作步驟:
建表
create table EMP_TEMP as
select * from emp where 1=0;
編寫游標(biāo)案例:
declare
cur_empno emp.empno%type;
cur_ename emp.ename%type;
cur_emp_cd emp%rowtype;
--創(chuàng)建游標(biāo)存儲(chǔ)符合條件的數(shù)據(jù)
cursor cur_emp is
select * from emp where sal>1500 and DEPTNO
in (select DEPTNO from dept where loc in ('CHICAGO','NEW YORK'));
begin
--把符合條件的數(shù)據(jù)存到表中
open cur_emp;
loop
fetch cur_emp into cur_emp_cd;
if cur_emp%FOUND then
insert into emp_temp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
values(cur_emp_cd.empno,cur_emp_cd.ename,cur_emp_cd.job,cur_emp_cd.mgr,cur_emp_cd.hiredate,cur_emp_cd.sal,cur_emp_cd.comm,cur_emp_cd.deptno);
else
dbms_output.put_line('已經(jīng)取出所有數(shù)據(jù)!共' || cur_emp%rowcount || '條記錄');
exit;
end if;
end loop;
commit;
close cur_emp;
--調(diào)整工資
update emp_temp
set sal = sal * 1.05
where sal < 2500;
dbms_output.put_line('工資調(diào)整完畢!共調(diào)整' || SQL%ROWCOUNT || '條記錄');
commit;
end;
/
輸出:
已經(jīng)取出所有數(shù)據(jù)!共4條記錄
工資調(diào)整完畢!共調(diào)整2條記錄
PL/SQL 過(guò)程已成功完成。
習(xí)題
一、填空題
1.靜態(tài)游標(biāo)包含 ( )和( ) 兩種類型。
2.通常使用( )命令遍歷游標(biāo)的數(shù)據(jù)集。
3.游標(biāo)的使用步驟包括 ( )、( )、( )、( )
4.游標(biāo)4個(gè)屬性是 ( )、( )、( )、( )
二、選擇題
- 如果游標(biāo)沒(méi)有被打開(kāi)時(shí)就調(diào)用,會(huì)不會(huì)提示出錯(cuò)?( )
A.不會(huì) B.會(huì) - 下面有關(guān)隱式游標(biāo)中的%ISOPEN屬性,說(shuō)法正確的是( ).
A. 隱式游標(biāo)中的%ISOPEN屬性永遠(yuǎn)返回FALSE
B. 隱式游標(biāo)中的%ISOPEN屬性可以被人為地控制