一、 游標的概念:
游標是SQL的一個內存工作區,由系統或用戶以變量的形式定義。游標的作用就是用於臨時存儲從數據庫中提取的數據塊。在某些情況下,需要把數據從存放在磁盤的表中調到計算機內存中進行處理,最后將處理結果顯示出來或最終寫回數據庫。這樣數據處理的速度才會提高,否則頻繁的磁盤數據交換會降低效率。
二、游標分類:
游標有兩種類型:顯式游標和隱式游標。在前述程序中用到的SELECT...INTO...查詢語句,一次只能從數據庫中提取一行數據,對於這種形式的查詢和DML操作,系統都會使用一個隱式游標。但是如果要提取多行數據,就要由程序員定義一個顯式游標,並通過與游標有關的語句進行處理。顯式游標對應一個返回結果為多行多列的SELECT語句。
游標一旦打開,數據就從數據庫中傳送到游標變量中,然后應用程序再從游標變量中分解出需要的數據,並進行處理。
1、 隱式游標:DML操作和單行SELECT語句會使用隱式游標,它們是:
* 插入操作:INSERT。
* 更新操作:UPDATE。
* 刪除操作:DELETE。
* 單行查詢操作:SELECT ... INTO ...。
當系統使用一個隱式游標時,可以通過隱式游標的屬性來了解操作的狀態和結果,進而控制程序的流程。隱式游標可以使用名字SQL來訪問,但要注意,通過SQL游標名總是只能訪問前一個DML操作或單行SELECT操作的游標屬性。所以通常在剛剛執行完操作之后,立即使用SQL游標名來訪問屬性。游標的屬性有四種,如下所示。
隱式游標的屬性
|
返回值類型
|
意 義
|
SQL%ROWCOUNT
|
整型
|
代表DML語句成功執行的數據行數
|
SQL%FOUND
|
布爾型
|
值為TRUE代表插入、刪除、更新或單行查詢操作成功
|
SQL%NOTFOUND
|
布爾型
|
與SQL%FOUND屬性返回值相反
|
SQL%ISOPEN
|
布爾型
|
DML執行過程中為真,結束后為假
|
【例1】 使用隱式游標的屬性,判斷對雇員工資的修改是否成功(
通過SQL%FOUND屬性判斷修改是否成功,並給出相應信息)。
SQL> set serveroutput on
SQL> begin
2 update emp set sal=sal+2300 where id=1;
3 if SQL%FOUND then
4 dbms_output.put_line('成功修改員工工資!');
5 commit;
6 else
7 dbms_output.put_line('修改員工工資失敗!');
8 end if;
9 end;
10 /
成功修改員工工資!
PL/SQL 過程已成功完成。
2、 顯式游標 : 游標的定義和操作分成以下4個步驟。
(1) 聲明游標 :在DECLEAR部分按以下格式聲明游標:
CURSOR 游標名[(參數1 數據類型[,參數2 數據類型...])]
IS SELECT語句;
參數是可選部分,所定義的參數可以出現在SELECT語句的WHERE子句中。如果定義了參數,則必須在打開游標時傳遞相應的實際參數。 SELECT語句是對表或視圖的查詢語句,甚至也可以是聯合查詢。可以帶WHERE條件、ORDER BY或GROUP BY等子句,但不能使用INTO子句。在SELECT語句中可以使用在定義游標之前定義的變量。
(2) 打開游標 :在可執行部分,按以下格式打開游標:
OPEN 游標名[(實際參數1[,實際參數2...])];
打開游標時,SELECT語句的查詢結果就被傳送到了游標工作區。
(3) 提取數據 :在可執行部分,按以下格式將游標工作區中的數據取到變量中。提取操作必須在打開游標之后進行。
FETCH 游標名 INTO 變量名1[,變量名2...];
或
FETCH 游標名 INTO 記錄變量;
游標打開后有一個指針指向數據區,FETCH語句一次返回指針所指的一行數據,要返回多行需重復執行,可以使用循環語句來實現。控制循環可以通過判斷游標的屬性來進行。 下面對這兩種格式進行說明:
第一種格式中的變量名是用來從游標中接收數據的變量,需要事先定義。變量的個數和類型應與SELECT語句中的字段變量的個數和類型一致。
第二種格式一次將一行數據取到記錄變量中,需要使用%ROWTYPE事先定義記錄變量,這種形式使用起來比較方便,不必分別定義和使用多個變量。 定義記錄變量的方法如下:
變量名 表名|游標名%ROWTYPE;
其中的表必須存在,游標名也必須先定義。
(4) 關閉游標
CLOSE 游標名;
顯式游標打開后,必須顯式地關閉。游標一旦關閉,游標占用的資源就被釋放,游標變成無效,必須重新打開才能使用。
【例2】用游標提取emp表中1號雇員的名稱和薪水。
SQL> set serveroutput on
SQL> declare
2 v_ename varchar2(10);
3 v_job varchar2(10);
4 cursor emp_cursor is --聲明游標
5 select ename,sal from emp where id=1;
6 begin
7 open emp_cursor; --打開游標
8 fetch emp_cursor into v_ename,v_job; --提取數據
9 dbms_output.put_line(v_ename||','||v_job);
10 close emp_cursor; --關閉游標
11 end;
12 /
James,12300
【例3】顯示工資最高的前3名雇員的名稱和工資。
SQL> set serveroutput on
SQL> declare
2 v_ename varchar2(20);
3 v_sal number(5);
4 cursor emp_cursor is select ename,sal from emp order by sal desc;
5 begin
6 open emp_cursor;
7 for i in 1..3 loop
8 fetch emp_cursor into v_ename,v_sal;
9 dbms_output.put_line(v_ename||','||v_sal);
10 end loop;
11 close emp_cursor;
12 end;
13 /
James,12300
孫權,9000
周瑜,8500
【例4】 使用特殊的FOR循環形式顯示全部雇員的編號和名稱。
SQL> set serveroutput on
SQL> declare
2 cursor emp_cursor is
3 select id,ename from emp;
4 begin
5 for emp_record in emp_cursor loop
6 dbms_output.put_line(emp_record.id||' '||emp_record.ename);
7 end loop;
8 end;
9 /
1 James
2 Tom
3 Smith
4 劉備
5 關羽
6 張飛
7 孫權
8 周瑜
【例5】 另一種形式的游標循環。
SQL> set serveroutput on
SQL> begin
2 for re in(select ename from emp) loop
3 dbms_output.put_line(re.ename);
4 end loop;
5 end;
6 /
James
Tom
Smith
劉備
關羽
張飛
孫權
周瑜
說明:該種形式更為簡單,省略了游標的定義,游標的SELECT查詢語句在循環中直接出現。
(5) 顯式游標屬性
雖然可以使用前面的形式獲得游標數據,但是在游標定義以后使用它的一些屬性來進行結構控制是一種更為靈活的方法。顯式游標的屬性如下所示。
顯式游標的屬性
|
返回值類型
|
意 義
|
%ROWCOUNT
|
整型
|
獲得FETCH語句返回的數據行數
|
%FOUND
|
布爾型
|
最近的FETCH語句返回一行數據則為真,否則為假
|
%NOTFOUND
|
布爾型
|
與%FOUND屬性返回值相反
|
%ISOPEN
|
布爾型
|
游標已經打開時值為真,否則為假
|
可按照以下形式取得游標的屬性:
游標名%屬性
要判斷游標emp_cursor是否處於打開狀態,可以使用屬性emp_cursor%ISOPEN。如果游標已經打開,則返回值為“真”,否則為“假”。
【訓練1】 使用游標的屬性練習。
SQL> set serveroutput on
SQL> declare
2 v_ename varchar2(20);
3 cursor emp_cursor is
4 select ename from emp;
5 begin
6 open emp_cursor;
7 if emp_cursor%ISOPEN then
8 loop
9 fetch emp_cursor into v_ename;
10 exit when emp_cursor%NOTFOUND;
11 dbms_output.put_line(to_char(emp_cursor%ROWCOUNT)||'-'||v_ename);
12 end loop;
13 else
14 dbms_output.put_line('用戶信息:游標沒有打開!');
15 end if;
16 close emp_cursor;
17 end;
18 /
說明:本例使用emp_cursor%ISOPEN判斷游標是否打開;使用emp_cursor%ROWCOUNT獲得到目前為止FETCH語句返回的數據行數並輸出;使用循環來獲取數據,在循環體中使用FETCH語句;使用emp_cursor%NOTFOUND判斷FETCH語句是否成功執行,當FETCH語句失敗時說明數據已經取完,退出循環。
【訓練2】 帶參數的游標。
SQL> set serveroutput on
SQL> declare
2 v_empid number(5);
3 v_ename varchar2(20);
4 cursor emp_cursor(pid number,jname varchar2) is
5 select id,ename from emp
6 where id = pid and ename = jname;
7 begin
8 open emp_cursor(6,'張飛');
9 loop
10 fetch emp_cursor into v_empid,v_ename;
11 exit when emp_cursor%NOTFOUND;
12 dbms_output.put_line(v_empid||','||v_ename);
13 end loop;
14 end;
15 /
【訓練2】 通過變量傳遞參數給游標。
SQL> set serveroutput on
SQL> declare
2 v_id number(5);
3 v_ename varchar2(20);
4 v_sal number(5);
5 cursor emp_cursor is
6 select id,ename from emp
7 where sal>v_sal;
8 begin
9 v_sal:=5000;
10 open emp_cursor;
11 loop
12 fetch emp_cursor into v_id,v_ename;
13 exit when emp_cursor%NOTFOUND;
14 dbms_output.put_line(v_id||','||v_ename);
15 end loop;
16 end;
17 /