隱式遊標 sql
如前所述,DML操做和單行SELECT語句會使用隱式遊標,它們是:
* 插入操做:INSERT。
* 更新操做:UPDATE。
* 刪除操做:DELETE。
* 單行查詢操做:SELECT ... INTO ...。
當系統使用一個隱式遊標時,能夠經過隱式遊標的屬性來了解操做的狀態和結果,進而控制程序的流程。隱式遊標可使用名字SQL來訪問,但要注意,經過SQL遊標名老是隻能訪問前一個DML操做或單行SELECT操做的遊標屬性。因此一般在剛剛執行完操做以後,當即使用SQL遊標名來訪問屬性。遊標的屬性有四種,以下所示:數據庫
sql%found (布爾類型,默認值爲null)函數
sql%notfound(布爾類型,默認值爲null)工具
sql%rowcount(數值類型默認值爲0)oop
sql%isopen(布爾類型)學習
當執行一條DML語句後,DML語句的結果保存在四個遊標屬性中,這些屬性用於控制程序流程或者瞭解程序的狀態。當運行DML語句時,PL/SQL打開一個內建遊標並處理結果,遊標是維護查詢結果的內存中的一個區域,遊標在運行DML語句時打開,完成後關閉。隱式遊標只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三個屬性.SQL%FOUND,SQL%NOTFOUND是布爾值,SQL%ROWCOUNT是整數值。fetch
SQL%FOUND和SQL%NOTFOUNDspa
在執行任何DML語句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在執行DML語句後,SQL%FOUND的屬性值將是:指針
. TRUE :INSERTcode
. TRUE :DELETE和UPDATE,至少有一行被DELETE或UPDATE.
. TRUE :SELECT INTO至少返回一行
當SQL%FOUND爲TRUE時,SQL%NOTFOUND爲FALSE。
SQL%ROWCOUNT
在執行任何DML語句以前,SQL%ROWCOUNT的值都是NULL,對於SELECT INTO語句,若是執行成功,SQL%ROWCOUNT的值爲1,若是沒有成功或者沒有操做(如update、insert、delete爲0條),SQL%ROWCOUNT的值爲0,而對於update和delete來講表示遊標所檢索數據庫行的個數即更新或者刪除的行數。
SQL%ISOPEN
SQL%ISOPEN是一個布爾值,若是遊標打開,則爲TRUE, 若是遊標關閉,則爲FALSE.對於隱式遊標而言SQL%ISOPEN老是FALSE,這是由於隱式遊標在DML語句執行時打開,結束時就當即關閉。
最後咱們來講一下隱式遊標中SELECT..INTO 語句,當執行的時候會有三種可能:
(1).結果集只含有一行,且select是成功的
(2).沒有查詢到任何結果集,引起NO_DATA_FOUND異常
(3).結果集中含有兩行或者更多行,引起TOO_MANY_ROWS異常。
例子:
BEGIN UPDATE exchangerate SET rate=7 where quarter='2011Q1'; DBMS_output.put_line('遊標所影響的行數:'||SQL%rowcount); if SQL%NotFound then DBMS_output.put_line('NotFound爲真'); DBMS_output.put_line('NofFound爲假'); end if; if SQL%Found then DBMS_output.put_line('Found爲真'); else DBMS_output.put_line('Found爲假'); end if; if SQL%isopen then DBMS_output.put_line('isOpen爲真'); else DBMS_output.put_line('isOpen爲假'); end if; END;
與遊標同樣,動態遊標(遊標變量)也是一個指向多行查詢結果集合中當前數據行的指針。但與遊標不一樣的是,遊標變量是動態的,而遊標是靜態的。
遊標只能與指定的查詢相連,即固定指向一個查詢的內存處理區域,而遊標變量則可與不一樣的查詢語句相連,它能夠指向不一樣查詢語句的內存處理區域(但不能同時指向多個內存處理區域,在某一時刻只能與一個查詢語句相連),只要這些查詢語句的返回類型兼容便可。
顯式遊標:
遊標的定義和操做
遊標的使用分紅如下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 遊標名;
顯式遊標打開後,必須顯式地關閉。遊標一旦關閉,遊標佔用的資源就被釋放,遊標變成無效,必須從新打開才能使用。
如今經過一個例子來學習一下顯示遊標的使用方法:
有一個表原來結構是以下的
create table EXCHANGERATE ( QUARTER VARCHAR2(20), RATE NUMBER(10,4), DESCRIPTION VARCHAR2(900), ID VARCHAR2(10) not null, CURRENCY VARCHAR2(100) )
這是一個匯率表裏面維護着的是季度 幣種和匯率的關係,如今有一個新的需求是在原來表的基礎上增長一列名字爲currentmonth,變爲季度、季度中月份、 幣種和匯率的關係,
而且使原來每一個季度對應的幣種和匯率變成每一個季度 對應該季度月份 幣種和匯率,每月的默認值爲原來季度對應的值。
例如 原來 2013Q2 CNY 6.2
如今咱們要變爲2013Q2 2013-04 CNY 6.2 2013Q2 2013-05 CNY 6.2
2013Q2 2013-06 CNY 6.2 三條記錄。
經過分析以上需求,咱們首先要增長一列:
alter table exchangerate add currentmonth varchar2(20);
而後咱們經過在匿名塊中經過顯示遊標來實現以上需求:
1 declare 2 3 v_year varchar2(20); 4 v_month number; 5 p_rate exchangerate%rowtype; 6 cursor c_rate is select * from exchangerate t where t.currentmonth is null; 7 begin 8 open c_rate; 9 loop 10 fetch c_rate into p_rate; 11 v_year:=substr(p_rate.quarter, 0, 4); 12 v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3; 13 for i in 1 .. 3 loop 14 15 insert into exchangerate(id,quarter,currentmonth,rate,currency,Description) 16 values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter, 17 to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); 18 19 end loop; 20 exit when c_rate%notfound; 21 end loop; 22 close c_rate; 23 end; 24 / 25 複製代碼 26 咱們把上面的例子有遊標的for循環來改寫一下。 27 28 29 30 顯式遊標的for循環 31 32 複製代碼 33 declare 34 35 v_year varchar2(20); 36 v_month number; 37 38 cursor c_rate is select * from exchangerate t where t.currentmonth is null; 39 40 begin 41 42 for p_rate in c_rate loop 43 44 v_year:=substr(p_rate.quarter, 0, 4); 45 46 v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3; 47 48 for i in 1 .. 3 loop 49 50 insert into exchangerate(id,quarter,currentmonth,rate,currency,Description) 51 values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter, 52 to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); 53 54 end loop; 55 56 end loop; 57 58 end; 59 60 /
咱們能夠看到遊標FOR循環確實很好的簡化了遊標的開發,咱們不在須要open、fetch和close語句,不在須要用%FOUND屬性檢測是否到最後一條記錄,這一切Oracle隱式的幫咱們完成了。
隱式遊標的for循環
1 declare 2 3 v_year varchar2(20); 4 v_month number; 5 6 begin 7 8 for p_rate in (select * from exchangerate t where t.currentmonth is null) loop 9 10 v_year:=substr(p_rate.quarter, 0, 4); 11 v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3; 12 for i in 1 .. 3 loop 13 14 insert into exchangerate(id,quarter,currentmonth,rate,currency,Description) 15 values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter, 16 to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); 17 18 end loop; 19 20 end loop; 21 22 end; 23 /
顯示遊標中游標參數的傳遞
例子:就以上面的表來講 加入咱們在定義遊標時不肯定查詢條件中的值,這時咱們能夠經過遊標參數來解決
1 declare 2 3 v_year varchar2(20); 4 v_month number; 5 p_rate exchangerate%rowtype; 6 7 cursor c_rate(p_quarter varchar2) --聲明遊標帶參數 8 is 9 select * from exchangerate t where t.quarter<=p_quarter; 10 11 begin 12 open c_rate(p_quarter=>'2011Q3');--打開遊標,傳遞參數值 13 loop 14 15 fetch c_rate into p_rate; 16 17 update exchangerate set rate=p_rate.rate+1 where id=p_rate.id; 18 19 20 exit when c_rate%notfound; 21 22 end loop; 23 24 close c_rate; 25 26 end;
遊標變量
遊標是數據庫中一個命名的工做區,當遊標被聲明後,他就與一個固定的SQL想關聯,在編譯時刻是已知的,是靜態的.它永遠指向一個相同的查詢工做區.
遊標變量是動態的能夠在運行時刻與不一樣的SQL語句關聯,在運行時能夠取不一樣的SQL語句.它能夠引用不一樣的工做區.
如何定義遊標類型
TYPE ref_type_name IS REF CURSOR
[RETURN return_type];
聲明遊標變量
cursor_name ref_type_name;
ref_type_name 是後面聲明遊標變量時要用到的咱們的遊標類型(自定義遊標類型,即CURSOR是系統默認的,ref_type_name是咱們定義的 );
return_type表明數據庫表中的一行,或一個記錄類型
TYPE ref_type_name IS REF CURSOR RETURN EMP%TYPE
RETURN 是可選的,若是有是強類型,能夠減小錯誤,若是沒有return是弱引用,有較好的靈活性.
遊標變量的操做
例子:
1 declare 2 3 v_year varchar2(20); 4 v_month number; 5 p_rate exchangerate%rowtype; 6 7 type rate is ref cursor;--定義遊標變量 8 c_rate rate; --聲明遊標變量 9 10 begin 11 12 13 open c_rate for select * from exchangerate t where t.quarter='2011Q3';--打開遊標變量 loop 14 15 fetch c_rate into p_rate;--提取遊標變量 16 17 update exchangerate set rate=p_rate.rate+1 where id=p_rate.id; 18 19 exit when c_rate%notfound; 20 21 end loop; 22 23 --將同一個遊標變量對應到另外一個SELECT語句 24 25 open c_rate for select * from exchangerate t where t.quarter='2011Q2';--打開遊標變量 loop 26 27 fetch c_rate into p_rate;--提取遊標變量 28 29 update exchangerate set rate=p_rate.rate-1 where id=p_rate.id; 30 31 exit when c_rate%notfound; 32 33 end loop; 34 close c_rate;--關閉遊標變量 35 36 end;
遊標表達式
Oracle在SQL語言中提供了一個強有力的工具:遊標表達式。一個遊標表達式從一個查詢中返回一個內嵌的遊標。在這個內嵌遊標的結果集中,每一行數據包含了在SQL查詢中的可容許的數值範圍;它也能包含被其餘子查詢所產生的遊標。
所以,你可以使用遊標表達式來返回一個大的和複雜的,從一張或多張表獲取的數據集合。遊標表達式的複雜程度,取決於查詢和結果集。然而,瞭解全部從Oracle RDBMS提取數據的可能途徑,還有大有好處的。
你可以在如下任何一種狀況使用遊標表達式:
(1)、 顯式遊標聲明
(2)、動態SQL查詢。
(3)、REF CURSOR 聲明和變量。
你不能在一個隱式查詢中使用遊標表達式。
遊標表達式的語法是至關簡單的:
CURSOR (查詢語句)
當Oracle從父遊標或外圍遊標那裏檢取包含遊標表達式的數據行時,Oracle就會隱式地打開一個內嵌的遊標,這個遊標就是被上述的遊標表達式所定義。在如下狀況發生時,這個內遷遊標將會被關閉:
(1)、你顯式地關閉這個遊標。
(2)、外圍或父遊標被從新執行,關閉或撤銷。
(3)、當從父遊標檢取數據時,發生異常。內嵌遊標就會與父遊標一塊兒被關閉。
你能夠經過兩種不一樣的,可是很是有用的方法來使用遊標表達式:
1. 在一個外圍查詢中把字查詢做爲一列來檢取數據。
2. 把一個查詢轉換成一個結果集,而這個結果集就能夠被當成一個參數傳遞給一個流型或變換函數。
例子:
1 CREATE OR REPLACE PROCEDURE emp_report(p_locid NUMBER) 2 IS 3 TYPE refcursor IS REF CURSOR; 4 CURSOR all_in_one IS 5 SELECT l.city, CURSOR( 6 SELECT d.department_name, CURSOR ( 7 SELECT e.last_name 8 FROM employees e 9 WHERE e.DEPARTMENT_ID = d.DEPARTMENT_ID 10 ) as ename 11 FROM departments d 12 WHERE d.LOCATION_ID = l.LOCATION_ID 13 ) as dname 14 FROM locations l 15 WHERE l.location_id = p_locid; 16 departments_cur refcursor; 17 employees_cur refcursor; 18 v_city locations.city%type; 19 v_dname departments.department_name%type; 20 v_ename employees.last_name%type; 21 i integer :=1; 22 j integer :=1; 23 k integer :=1; 24 BEGIN 25 OPEN all_in_one; 26 LOOP 27 FETCH all_in_one INTO v_city, departments_cur; 28 EXIT WHEN all_in_one%NOTFOUND; 29 LOOP 30 FETCH departments_cur INTO v_dname, employees_cur; 31 EXIT WHEN departments_cur%NOTFOUND; 32 LOOP 33 FETCH employees_cur INTO v_ename; 34 EXIT WHEN employees_cur%NOTFOUND; 35 dbms_output.put_line(i || ' , ' || j || ' , ' || k || '----' || v_city || ' ,' || v_dname || ' ,' || v_ename ); 36 k := k + 1; 37 END LOOP; 38 j := j + 1; 39 END LOOP; 40 i := i + 1; 41 END LOOP; 42 END; 43 /