轉載整理:顯式遊標和隱式遊標和動態遊標

隱式遊標 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;

 

動態遊標

與遊標同樣,動態遊標(遊標變量)也是一個指向多行查詢結果集合中當前數據行的指針。但與遊標不一樣的是,遊標變量是動態的,而遊標是靜態的。

遊標只能與指定的查詢相連,即固定指向一個查詢的內存處理區域,而遊標變量則可與不一樣的查詢語句相連,它能夠指向不一樣查詢語句的內存處理區域(但不能同時指向多個內存處理區域,在某一時刻只能與一個查詢語句相連),只要這些查詢語句的返回類型兼容便可。

 

 

  1. DECLARE  
  2.    --定義一個遊標數據類型  
  3.    TYPE emp_cursor_type IS REF CURSOR;  
  4.    --聲明一個遊標變量  
  5.    c1 EMP_CURSOR_TYPE;  
  6.    --聲明兩個記錄變量  
  7.    v_emp_record employees%ROWTYPE;  
  8.    v_reg_record regions%ROWTYPE;  
  9.   
  10. BEGIN  
  11.    OPEN c1 FOR SELECT * FROM employees WHERE department_id = 20;  
  12.    LOOP  
  13.       FETCH c1 INTO v_emp_record;  
  14.       EXIT WHEN c1%NOTFOUND;  
  15.       DBMS_OUTPUT.PUT_LINE(v_emp_record.first_name||'的僱傭日期是'  
  16.                             ||v_emp_record.hire_date);  
  17.    END LOOP;  
  18.    --將同一個遊標變量對應到另外一個SELECT語句  
  19.    OPEN c1 FOR SELECT * FROM regions WHERE region_id IN(1,2);  
  20.    LOOP  
  21.       FETCH c1 INTO v_reg_record;  
  22.       EXIT WHEN c1%NOTFOUND;  
  23.       DBMS_OUTPUT.PUT_LINE(v_reg_record.region_id||'表示'  
  24.                             ||v_reg_record.region_name);  
  25.    END LOOP;  
  26.    CLOSE c1;  
  27. 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 /
相關文章
相關標籤/搜索