游標是一種私有的工作區,用於保存SQL語句的執行結果。
在執行一條SQL語句時,數據庫服務器會打開一個工作區,將SQL語句的執行結果保存在這裡。
在Oracle數據庫中有兩種形式的游標:隱式游標和顯式游標。
隱式游標是由數據庫服務器定義的,顯式游標是用戶根據需要自己定義的。
隱式游標
隱式游標是數據庫服務器定義的一種游標。
在執行一條DML語句或SELECT語句時,數據庫服務器將自動打開一個隱式游標,存放該語句的執行結果。
在一個PL/SQL塊中可能有多條DML或SELECT語句,隱式游標始終存放最近一條語句的執行結果。
隱式游標有幾個很有用的屬性,可以幫助我們了解游標的信息。
下面列出了隱式游標的幾個常用屬性。
SQL%ISOPEN 判斷當前游標是否打開。如果打開,該屬性值為TRUE,否則為FALSE
SQL%ROWCOUNT 對於DML語句,該屬性值為受影響的數據行數。對於SELECT語句,如果不發生異常,其值為1
SQL%FOUND 對於DML語句,該屬性表明表中是否有數據受到影響。如果DML語句沒有影響任何數據,該屬性值為FALSE,否則為TRUE。對於SELECT語句,如果不發生異常,其值為TRUE
SQL%NOTFOUND 與屬性SQL%FOUND正好相反
其中SQL%FOUND屬性值為布爾值,表示是否找到了滿足條件的數據,如果找到了相應的數據,其值為TRUE ,否則為FALSE 。
SQL%ROWCOUNT屬性表示某個操作影響的數據行數,對於UPDATE語句,表示修改的行數;對於INSERT語句,表示插入的行數;對於DELETE語句,表示被刪除的行數。
例如,可以在每一條DML語句之後輸出該操作影響的行數。
BEGIN
UPDATE emp set sal=sal+100;
if SQL%FOUND then
dbms_output.put_line('被修改的行數:' || SQL%ROWCOUNT);
END if;
DELETE FROM emp;
if SQL%FOUND then
dbms_output.put_line ('被刪除的行數:' || SQL%ROWCOUNT);
END if;
INSERT INTO dept VALUES(70 ,' aaa ',' aaaa ');
INSERT INTO dept VALUES(80 ,'bbb ',' bbbb' ) ;
if SQL%FOUND then
dbms_output.put_line ('最近插入的行數:' || SQL%ROWCOUNT);
END if;
ROLLBACK;
END;
從這個例子可以看出,當有多條DML語句時,隱式游標只記錄最近一條DML語句的執行情況。
如果是SELECT語句,情況則比較特殊。
因為當SELECT語句沒有檢索到滿足條件的數據時,將引發NO_DATA_FOUND 異常,而當檢索到多行滿足條件的數據時,將引發TOO_MANY ROWS異常。
所以只有當SELECT語句正好檢索到一行數據時,才可以使用隱式游標的這些屬性。
如果要處理這兩種特殊情況,就需要借助於顯式游標。
顯式游標
對於PL/SQL塊中的SELECT語句,可以用顯式講標來處理。
顯式游標是一個打開的工作區,
在這個工作區裡保存SELECT語句的執行結果。
用顯式游標可以處理返回0行、一行、多行等各種情況,並且在返回0行或多行數據這兩種特殊情況下,不會引發NO_DATA_FOUND 和TOO_MANY ROWS異常。
使用游標處理SELECT語句的步驟如下:
1 )聲明游標
2 )打開游標
3 )逐行取出游標中的行,並分別進行處理
4 )關閉游標
游標的工作過程如下所示。
DECLARE OPEN FETCH CLOSE
聲明游標 打開游標 從游標中取數據 關閉游標
注:在從游標中取數據時,要做個判斷,判斷是和否取完了,如果是,則CLOSE游標;如果否,則繼續FETCH游標。
游標的聲明在PL/SQL塊的聲明部分進行。
聲明的語法格式為:
DECLARE
CURSOR 游標名 IS
SELECT 語句;
例如,下面的語句聲明了一個名為cur_1 的游標:
DECLARE
CURSOR cur_1 IS
SELECT * FROM emp;
由於SELECT語句的執行結果將存放在工作區中,因此不需要使用INTO子句將返回的數據賦給變量。
為了處理游標中的數據,首先要打開游標。
打開游標意味著將指定的SELECT語句交給數據庫服務器執行,並將返回結果存放在工作區中。
打開游標的命令是OPEN ,其語法格式為:
OPEN 游標名
例如,打開游標cur_1的語句為:
OPEN cur_1;
游標打開後,就可以取出游標中的數據,並對其進行處理了。
從游標中取出數據的命令是FETCH 。
FETCH命令一次取出一行數據,並將其賦給指定的變量。
FETCH命令的格式為:
FETCH 游標 INTO 變量1,變量2, ...
游標中的數據只有在取出後才能進行處理。
為此,需要在FETCH語句中用INTO子句指定多個變量,分別存放一行數據中各個列的值。
FETCH命令將一行數據中各列的值依次賦給指定的變量。
需要注意的是,變量的類型、數目要與游標中一行的各列相對應。
在用FETCH命令取出游標中的數據時,可以設想有一個指針,指向游標中的一行數據。
當游標剛剛打開時,指針指向第一行,以後每取出一行,指針自動指向下一行,直到將所有的數據都取出為止。
游標在使用完後,應該及時關閉,以釋放它所占用的內存空間。
關閉游標的命令是CLOSE,其語法格式為:
CLOSE 游標名;
當游標關閉後,不能再從游標中獲取數據。
如果需要,可以再次打開游標。
考慮下面的游標。
從emp表中檢索員工7902 的姓名、工資、工作時間。
由於SELECT命令僅返回一行數據,所以處理的過程很簡單。
DECLARE
name emp.ename%type;
salary emp.sal%type;
hire_date emp.hiredate%type;
CURSOR cur_1 IS
SELECT ename,sal,hiredate FROM emp WHERE empno=7902;
BEGIN
open cur_1;
fetch cur_1 INTO name,salary,hire_date;
dbms_output.put_line ('姓名:' || name || '工資:' || salary || '工作時間:' || hire_date);
close cur_1;
END;
為了使程序更加簡潔,在PL/SQL塊中可以使用記錄變量。
首先聲明一個記錄變量,它的結構與游標的結構相同。
然後可以使用FETCH語句將游標中的一行數據取出後存放在記錄變量中,接下來就可以對這個記錄變量進行處理了。
例如,對上面的PL/SQL塊進行修改,在PL/SQL塊中使用記錄變量。
修改後的代碼如下:
DECLARE
CURSOR cur_1 IS
SELECT ename,sal,hiredate FROM emp WHERE empno=7902;
e cur_1%rowtype;
BEGIN
open cur_1;
fetch cur_1 INTO e.ename, e.sal, e.hiredate;
dbms_output.put_line ('姓名:' || e.ename || '工資:' || e.sal|| '工作時間:' || e.hiredate);
close cur_1;
END;
在上面的塊中通過一個簡單的游標,處理一行數據。
在使用游標時,必須考慮各種特殊情況。
如果SELECT語句沒有返回結果,游標是空的,這時FETCH語句將取不到數據。
如果SELECT語句返回多行數據,這時用一條FETCH語句僅能取到游標中的一行數據。
利用游標的屬性可以了解游標當前的狀態,防止各種意外情況的發生。
下面出了顯式游標的若干屬性。
CURSOR%ISOPEN 判斷當前游標是否打開。如果打開,該屬性值為TRUE,否則為FALSE
CURSOR%ROWCOUNT 表示到目前為止,用FETCH語句取到的行數
CURSOR%FOUND 表示最後一次FETCH操作是否從游標中取到一行數據,如果已經取到,其值為TRUE,否則為FALSE
CURSOR%NOTFOUND 與屬性CURSOR%FOUND相反
備注:CURSOR為PL/SQL塊中游標的名稱。