Oracle學習筆記之顯式游標和隱式游標


游標的概念

    游標是SQL的一個內存工作區,由系統或用戶以變量的形式定義。游標的作用就是用於臨時存儲從數據庫中提取的數據塊。在某些情況下,需要把數據從存放在磁盤的表中調到計算機內存中進行處理,最后將處理結果顯示出來或最終寫回數據庫。這樣數據處理的速度才會提高,否則頻繁的磁盤數據交換會降低效率。 
游標有兩種類型:顯式游標和隱式游標。我們常用到的SELECT...INTO...查詢語句,一次只能從數據庫中提取一行數據,對於這種形式的查詢和DML操作,系統都會使用一個隱式游標。但是如果要提取多行數據,就要由程序員定義一個顯式游標,並通過與游標有關的語句進行處理。顯式游標對應一個返回結果為多行多列的SELECT語句。 
游標一旦打開,數據就從數據庫中傳送到游標變量中,然后應用程序再從游標變量中分解出需要的數據,並進行處理。 

隱式游標 


如前所述,DML操作和單行SELECT語句會使用隱式游標,它們是: 
* 插入操作:INSERT。 
* 更新操作:UPDATE。 
* 刪除操作:DELETE。 
* 單行查詢操作:SELECT ... INTO ...。 
當系統使用一個隱式游標時,可以通過隱式游標的屬性來了解操作的狀態和結果,進而控制程序的流程。隱式游標可以使用名字SQL來訪問,但要注意,通過SQL游標名總是只能訪問前一個DML操作或單行SELECT操作的游標屬性。所以通常在剛剛執行完操作之后,立即使用SQL游標名來訪問屬性。游標的屬性有四種,如下所示:

sql%found (布爾類型,默認值為null)

sql%notfound(布爾類型,默認值為null)

sql%rowcount(數值類型默認值為0)

sql%isopen(布爾類型)

當執行一條DML語句后,DML語句的結果保存在四個游標屬性中,這些屬性用於控制程序流程或者了解程序的狀態。當運行DML語句時,PL/SQL打開一個內建游標並處理結果,游標是維護查詢結果的內存中的一個區域,游標在運行DML語句時打開,完成后關閉。隱式游標只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三個屬性.SQL%FOUND,SQL%NOTFOUND是布爾值,SQL%ROWCOUNT是整數值。

SQL%FOUND和SQL%NOTFOUND

在執行任何DML語句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在執行DML語句后,SQL%FOUND的屬性值將是:

     . TRUE :INSERT

  . 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);

然后我們通過在匿名塊中通過顯示游標來實現以上需求:

declare

 v_year varchar2(20);
 v_month number;
 p_rate exchangerate%rowtype;
cursor c_rate is select * from exchangerate t where t.currentmonth is null;
begin open c_rate; loop
fetch c_rate into p_rate; v_year:=substr(p_rate.quarter, 0, 4); v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;
for i in 1 .. 3 loop insert into exchangerate(id,quarter,currentmonth,rate,currency,Description)
values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); end loop;
exit when c_rate%notfound;
end loop;
close c_rate;
end; /

 我們把上面的例子有游標的for循環來改寫一下。

 

顯式游標的for循環

declare

 v_year varchar2(20);
 v_month number;

 cursor c_rate is  select * from exchangerate t where t.currentmonth is null;

 begin

   for p_rate in c_rate loop

   v_year:=substr(p_rate.quarter, 0, 4);

   v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;

    for i in 1 .. 3 loop

    insert into exchangerate(id,quarter,currentmonth,rate,currency,Description)
values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); end loop; end loop; end; /

 

我們可以看到游標FOR循環確實很好的簡化了游標的開發,我們不在需要open、fetch和close語句,不在需要用%FOUND屬性檢測是否到最后一條記錄,這一切Oracle隱式的幫我們完成了。

 

隱式游標的for循環

 

declare

 v_year varchar2(20);
 v_month number;

 begin

   for p_rate in (select * from exchangerate t where t.currentmonth is null) loop

   v_year:=substr(p_rate.quarter, 0, 4);
   v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;
for i in 1 .. 3 loop insert into exchangerate(id,quarter,currentmonth,rate,currency,Description)
values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description); end loop; end loop; end; /

 

顯示游標中游標參數的傳遞


例子:就以上面的表來說 加入我們在定義游標時不確定查詢條件中的值,這時我們可以通過游標參數來解決

declare

 v_year varchar2(20);
 v_month number;
 p_rate exchangerate%rowtype;

 cursor c_rate(p_quarter varchar2) --聲明游標帶參數
is
select
* from exchangerate t where t.quarter<=p_quarter; begin open c_rate(p_quarter=>'2011Q3');--打開游標,傳遞參數值 loop fetch c_rate into p_rate; update exchangerate set rate=p_rate.rate+1 where id=p_rate.id; exit when c_rate%notfound; end loop; close c_rate; 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是弱引用,有較好的靈活性.

游標變量的操作

 例子:

declare

 v_year varchar2(20);
 v_month number;
 p_rate exchangerate%rowtype;

 type  rate is ref cursor;--定義游標變量
  c_rate rate; --聲明游標變量
  
 begin
   
   
   open c_rate for select * from exchangerate t where t.quarter='2011Q3';--打開游標變量
    loop

    fetch c_rate into p_rate;--提取游標變量
    
    update exchangerate set rate=p_rate.rate+1 where id=p_rate.id;

    exit when c_rate%notfound;

   end loop;
  
  --將同一個游標變量對應到另一個SELECT語句
   
   open c_rate for select * from exchangerate t where t.quarter='2011Q2';--打開游標變量
    loop

    fetch c_rate into p_rate;--提取游標變量
    
    update exchangerate set rate=p_rate.rate-1 where id=p_rate.id;

    exit when c_rate%notfound;

   end loop;
   close c_rate;--關閉游標變量

 end;

 

游標表達式

OracleSQL語言中提供了一個強有力的工具:游標表達式。一個游標表達式從一個查詢中返回一個內嵌的游標。在這個內嵌游標的結果集中,每一行數據包含了在SQL查詢中的可允許的數值范圍;它也能包含被其他子查詢所產生的游標。

因此,你能夠使用游標表達式來返回一個大的和復雜的,從一張或多張表獲取的數據集合。游標表達式的復雜程度,取決於查詢和結果集。然而,了解所有從Oracle RDBMS提取數據的可能途徑,還有大有好處的。

你能夠在以下任何一種情況使用游標表達式:

   (1)、 顯式游標聲明

   (2)、動態SQL查詢。

   (3)、REF CURSOR 聲明和變量。

你不能在一個隱式查詢中使用游標表達式。

游標表達式的語法是相當簡單的:

    CURSOR (查詢語句)

Oracle從父游標或外圍游標那里檢取包含游標表達式的數據行時,Oracle就會隱式地打開一個內嵌的游標,這個游標就是被上述的游標表達式所定義。在以下情況發生時,這個內遷游標將會被關閉:

    (1)、你顯式地關閉這個游標。

    (2)、外圍或父游標被重新執行,關閉或撤銷。

    (3)、當從父游標檢取數據時,發生異常。內嵌游標就會與父游標一起被關閉。

 使用游標表達式

你可以通過兩種不同的,但是非常有用的方法來使用游標表達式:

   1.  在一個外圍查詢中把字查詢作為一列來檢取數據。

   2.  把一個查詢轉換成一個結果集,而這個結果集就可以被當成一個參數傳遞給一個流型或變換函數。

例子:

CREATE OR REPLACE PROCEDURE emp_report(p_locid NUMBER)
IS
    TYPE refcursor IS REF CURSOR;
    CURSOR all_in_one IS
        SELECT l.city, CURSOR(
            SELECT  d.department_name, CURSOR (
                SELECT e.last_name
                FROM employees e
                WHERE e.DEPARTMENT_ID = d.DEPARTMENT_ID
            ) as ename
            FROM departments d
            WHERE d.LOCATION_ID = l.LOCATION_ID
        ) as dname
        FROM locations l
        WHERE l.location_id = p_locid;
departments_cur refcursor;
employees_cur refcursor;
v_city locations.city%type;
v_dname departments.department_name%type;
v_ename employees.last_name%type;
i integer :=1;
j integer :=1;
k integer :=1;
BEGIN
    OPEN all_in_one;
    LOOP    
    FETCH all_in_one INTO v_city, departments_cur;
    EXIT WHEN all_in_one%NOTFOUND;
                                                  LOOP
        FETCH departments_cur INTO  v_dname, employees_cur;
        EXIT WHEN departments_cur%NOTFOUND;
        LOOP
            FETCH employees_cur INTO v_ename;
            EXIT WHEN employees_cur%NOTFOUND;
            dbms_output.put_line(i || ' , ' || j || ' , ' || k || '----' || v_city || ' ,' || v_dname || ' ,' || v_ename );
            k := k + 1;
        END LOOP;
        j := j + 1;
    END LOOP;
    i := i + 1;
    END LOOP;
END;
/

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM