常見的遊標可分爲顯示遊標、隱式遊標、靜態遊標和動態遊標四大類:html
顯式是相對與隱式cursor而言的,就是有一個明確的聲明的cursor。顯式遊標的聲明相似以下:sql
delcare 遊標關鍵字cursor 遊標名 is 數據集;oracle
遊標從declare、open、fetch、close是一個完整的生命旅程。固然了一個這樣的遊標是能夠被屢次open進行使用的,顯式cursor是靜態cursor,她的做用域是全局的,但也必須明白,靜態cursor也只有pl/sql代碼纔可使用她。下面看一個簡單的靜態顯式cursor的示例:app
1 declare 2 cursor get_subid(pid a_test.parentid%type) is 3 select subid from a_test where parentid = pid; 4 v_subid a_test.subid%type; 5 begin 6 open get_subid(1); 7 loop 8 fetch get_subid 9 into v_subid; 10 exit when get_subid%notfound; 11 dbms_output.put_line(v_subid); 12 end loop; 13 close get_subid; 14 dbms_output.put_line('--------這是分割線----------'); 15 open get_subid(4); 16 loop 17 fetch get_subid 18 into v_subid; 19 exit when get_subid%notfound; 20 dbms_output.put_line(v_subid); 21 end loop; 22 close get_subid; 23 end;
隱式cursor固然是相對於顯式而言的,就是沒有明確的cursor的declare。在Oracle的PL/SQL中,全部的DML操做都被Oracle內部解析爲一個cursor名爲SQL的隱式遊標,只是對咱們透明罷了。ide
begin for rec in (select user, sysdate from dual) loop dbms_output.put_line(rec.user || ':' || to_char(rec.sysdate, 'yyyy-mm-dd hh24:mi:ss')); end loop; end;
靜態遊標是相對於動態遊標而言的,普通顯示定義的遊標都是靜態遊標。函數
動態遊標是相對於靜態遊標而言的,要等到運行時才知道結果集查詢語句是什麼樣的。oop
1 declare 2 type atest_rec is record( 3 pid a_test.parentid%type, 4 subid a_test.subid%type); 5 6 type app_ref_cur_type is ref cursor return atest_rec; 7 my_cur app_ref_cur_type; 8 my_rec atest_rec; 9 begin 10 11 if (to_char(sysdate, 'dd') = 30) then 12 open my_cur for 13 select parentid, subid from a_test where parentid = 1; 14 else 15 open my_cur for 16 select parentid, subid from a_test where parentid = 2; 17 end if; 18 19 fetch my_cur 20 into my_rec; 21 while my_cur%found loop 22 --當前不是30號 執行else 結果: 23 --2#4 24 --2#5 25 dbms_output.put_line(my_rec.pid || '#' || my_rec.subid); 26 fetch my_cur 27 into my_rec; 28 end loop; 29 close my_cur; 30 31 end;
【注】Record爲記錄數據類型。它相似於C語言中的結構數據類型(STRUCTURE),PL/SQL提供了將幾個相關的、分離的、基本數據類型的變量組成一個總體的方法,即RECORD複合數據類型。在使用記錄數據類型變量時,須要在聲明部分先定義記錄的組成、記錄的變量,而後在執行部分引用該記錄變量自己或其中的成員。fetch
定義記錄數據類型的語法以下:編碼
1 TYPE RECORD_NAME IS RECORD( 2 V1 DATA_TYPE1 [NOT NULL][:=DEFAULT_VALUE], 3 V2 DATA_TYPE2 [NOT NULL][:=DEFAULT_VALUE], 4 VN DATA_TYPEN [NOT NULL][:=DEFAULT_VALUE]);
由上面的例子,可知cursor與REF cursor大體有如下幾點區別:spa
1)PL/SQL靜態遊標不能返回到客戶端,只有PL/SQL才能利用它。動態遊標可以被返回到客戶端,這就是從Oracle的存儲過程返回結果集的方式。
2)PL/SQL靜態遊標能夠是全局的,而動態遊標則不是,不能在包說明或包體中的過程或函數以外定義動態遊標。
3)動態遊標能夠從子例程傳遞到子例程,而普通遊標則不能。若是要共享靜態光標,必須在包說明或包體中把它定義爲全局光標。 由於使用全局變量一般不是一種很好的編碼習慣,所以能夠用動態遊標來共享PL/SQL中的遊標,無需混合使用全局變量。
4)靜態光標比動態遊標標效率要高,因此在使用遊標時首先考慮使用靜態遊標,也有人建議儘可能使用隱式遊標,避免編寫附加的遊標控制代碼(聲明,打開,獲取,關閉),也不須要聲明變量來保存從遊標中獲取的數據。這個就因人因事而定吧。
另外,在oracle9i之後系統定義的一個refcursor, 這是一個弱類型的遊標,至關於.Net中用戶var聲明的變量,主要用在過程當中返回結果集。
1 --建立存儲過程 2 create or replace procedure sp_get_subid(pid ina_test.parentid%type, 3 out_subid out SYS_REFCURSOR) as 4 begin 5 open out_subid for 6 SELECT * FROM a_test WHERE parentid = pid; 7 EXCEPTION 8 WHEN OTHERS THEN 9 RAISE_APPLICATION_ERROR(-20101, 'Error in sp_get_subid' || SQLCODE); 10 end sp_get_subid; 11 12 --調用存儲過程 13 declare 14 v_rent_rows SYS_REFCURSOR; 15 v_rent_row a_test%rowType; 16 begin 17 sp_get_subid(1, v_rent_rows); 18 Dbms_output.put_line('parentid subid'); 19 loop 20 fetch v _rows 21 into v _row; 22 exit when v _rows%NOTFOUND; 23 Dbms_output.put_line(v _row.parentid || ' ' || v _row.subid); 24 end loop; 25 close v_rows; 26 end;
1 %FOUND: bool - TRUE if >1 row returned 2 %NOTFOUND:bool - TRUE if 0 rows returned 3 %ISOPEN: bool - TRUE if cursor still open 4 %ROWCOUNT:int - number of rows affected by last SQL statement
【注】NO_DATA_FOUND和%NOTFOUND的用法是有區別的,小結以下:
1)SELECT . . . INTO 語句觸發 NO_DATA_FOUND;
2)當一個顯式遊標的 where 子句未找到時觸發 %NOTFOUND;
3)當UPDATE或DELETE 語句的where 子句未找到時觸發 SQL%NOTFOUND;
4)在遊標的提取(Fetch)循環中要用 %NOTFOUND 或%FOUND 來肯定循環的退出條件,不要用NO_DATA_FOUND
1 begin 2 3 update A_TEST set SUBID = '15' WHERE PARENTID = 4; 4 5 --SQL%ISOPEN是一個布爾值,若是遊標打開,則爲TRUE,若是遊標關閉,則爲FALSE. 6 7 if sql%isopen then 8 9 dbms_output.put_line('Openging'); 10 11 else 12 13 dbms_output.put_line('closing'); --對於隱式遊標而言SQL%ISOPEN老是FALSE,這是由於隱式遊標在DML語句執行時打開,結束時就當即關閉。 14 15 end if; 16 17 if sql%found then 18 19 dbms_output.put_line('遊標指向了有效行'); --判斷遊標是否指向有效行 20 21 else 22 23 dbms_output.put_line('Sorry'); 24 25 end if; 26 27 if sql%notfound then 28 29 dbms_output.put_line('Also Sorry'); 30 31 else 32 33 dbms_output.put_line('Haha'); 34 35 end if; 36 37 dbms_output.put_line(sql%rowcount); 38 39 exception 40 41 when no_data_found then 42 43 dbms_output.put_line('Sorry No data'); 44 45 when too_many_rows then 46 47 dbms_output.put_line('Too Many rows'); 48 49 end;
【注】SQL語言分爲DDL(Data Definition Language,數據定義語言,用來維護數據對象)和DML(Data Manipulation Language,數據操做語言,用於增刪改表中數據,DML是伴隨TCL事務控制的)。
1 declare 2 3 empNumber a_test.parentid%TYPE; 4 5 empName a_test.subid%TYPE; 6 7 begin 8 9 if sql%isopen then 10 11 dbms_output.put_line('Cursor is opinging'); 12 13 else 14 15 dbms_output.put_line('Cursor is Close'); 16 17 end if; 18 19 if sql%notfound then 20 21 dbms_output.put_line('No Value'); 22 23 else 24 25 dbms_output.put_line(empNumber); --沒有賦值,輸出爲空白 26 27 end if; 28 29 dbms_output.put_line(sql%rowcount); --沒有記錄,輸出爲空白 30 31 dbms_output.put_line('-------------'); 32 33 34 35 select parentid, subid into empNumber, empName from a_test where parentid = 4; 36 37 dbms_output.put_line(sql%rowcount); 38 39 40 41 if sql%isopen then 42 43 dbms_output.put_line('Cursor is opinging'); 44 45 else 46 47 dbms_output.put_line('Cursor is Closing'); 48 49 end if; 50 51 if sql%notfound then 52 53 dbms_output.put_line('No Value'); 54 55 else 56 57 dbms_output.put_line(empNumber); 58 59 end if; 60 61 exception 62 63 when no_data_found then 64 65 dbms_output.put_line('No Value'); 66 67 when too_many_rows then 68 69 dbms_output.put_line('too many rows'); 70 71 end;
【注】%Type是Oracle提供的一種數據定義方法,爲的是使一個新定義的變量與另外一個已經定義了的變量(一般是表的某一列)的數據類型保持一致,當被參照的那個變量的數據類型發生改變時,那麼這個新定義的變量的數據類型也會隨之發生改變。當不能確切的知道那個變量的類型是,就採用這種方法來定義變量的數據類型。
1 --聲明遊標:delcare 遊標關鍵字cursor 遊標名 is 數據集; 2 3 declare 4 5 cursorc_list is 6 7 selectp.fid, max(t.exp) exp 8 9 from view_pilot p 10 11 left join IO_FMS_BILLOFHEALTH t 12 13 ont.phr = p.fjobnumber 14 15 group by p.fid; 16 17 18 --For循環,相似.Net中的foreach方法: 19 20 --begin 21 22 --for 元素名 in 遊標名 循環關鍵字loop 23 24 --執行語句; 25 26 --endloop; 27 28 begin 29 30 for c_row in c_list loop 31 32 update alarm_pilotintelligence 33 34 set C = GetAlarmStateByExp(c_row.exp) 35 36 where isprimary = 0 37 38 and pid = c_row.fid; 39 40 end loop;
1 --定義遊標 2 3 declare 4 5 cursor c_job is 6 7 select * from a_test order by parentid; 8 9 --定義一個遊標變量 10 11 c_row c_job%rowtype; 12 13 begin 14 15 --使用的時候必需要明確的打開遊標 16 17 Open c_job; 18 19 --開始循環標記 20 21 loop 22 23 --提取一行數據到c_row,至關ADO.Net中的SqlDataReader.Read()方法 24 25 fetch c_job into c_row; 26 27 --判讀是否提取到值,沒取到值就退出 28 29 --取到值c_job%notfound 是false 30 31 --取不到值c_job%notfound 是true 32 33 exit when c_job%notfound; 34 35 dbms_output.put_line(c_row.parentid || '-' || c_row.subid); --用於輸出,這是oracle中最基礎的方法之一 36 37 --結束循環,並關閉遊標 38 39 end loop; 40 41 close c_job; 42 43 end;
【注】若是一個表有較多的列,使用%ROWTYPE來定義一個表示表中一行記錄的變量,比分別使用%TYPE來定義表示表中各個列的變量要簡潔得多,而且不容易遺漏、出錯。這樣會增長程序的可維護性。當不能確切地知道被參照的那個表的結構及其數據類型時,能夠採用這種方法定義變量的數據類型。
上面【示例二】中的結果還能夠經過While循環與Fetch相結合來實現:
1 --定義遊標 2 3 declare 4 5 cursor c_job is 6 7 select * from a_test order by parentid; 8 9 --定義一個遊標變量 10 11 c_row c_job%rowtype; 12 13 begin 14 15 --使用的時候必需要明確的打開遊標 16 17 Open c_job; 18 19 --開始循環標記 20 21 --提取一行數據到c_row,至關ADO.Net中的SqlDataReader.Read()方法 22 23 fetch c_job 24 25 into c_row; 26 27 --while循環 28 29 while c_job%found loop 30 31 dbms_output.put_line(c_row.parentid || '-' || c_row.subid); 32 33 fetch c_job 34 35 into c_row; 36 37 --結束循環,並關閉遊標 38 39 end loop; 40 41 close c_job; 42 43 end;
參考資料: