天天看點

Oracle遊标詳解

一、 什麼是遊标? 1. 從表中檢索出結果集,從中每次指向一條記錄進行互動的機制

2. 關系資料庫中的操作是在完整的行集合上執行的

由SELECT語句傳回的行集合包括滿足該語句的WHERE子句所列條件的所有行。由該語句傳回完整的行集合叫做結果集

應用程式,尤其是互動和線上應用程式,把完整的結果集作為一個單元處理并不總是有效的 這些應用程式需要一種機制來一次處理一行或連續的幾行。而遊标是對提供這一機制的結果集的擴充  

遊标是通過遊标庫來實作的。遊标庫是常常作為資料庫系統或資料通路API的一部分而得以實作的軟體,

用來管理從資料源傳回的資料的屬性(結果集)。這些屬性包括并發管理、在結果集中的位置、傳回的行數,

以及是否能夠在結果集中向前和/或向後移動(可滾動性)。

遊标跟蹤結果集中的位置,并允許對結果集逐行執行多個操作,在這個過程中可能傳回至原始表,也可能不傳回至原始表。

換句話說,遊标從概念上講基于資料庫的表傳回結果集。

由于它訓示結果集中的目前位置 ,就像計算機螢幕上的光标訓示目前位置一樣,“遊标”由此得名。

二、 遊标有什麼作用?

1. 指定結果集中特定行的位置

2. 基于目前的結果集位置檢索一行或連續的幾行

3. 在結果集的目前位置修改行中的資料

4. 對其他使用者所做的資料更改定義不同的敏感性級别

5. 可以以程式設計的方式通路資料庫

三、 為什麼避免使用遊标?

1. 在建立遊标時,最需要考慮的事情是,“是否有辦法避免使用遊标?”

   因為遊标的效率較差,如果遊标操作的資料超過1萬行,那麼就應該改寫;

   如果使用了遊标,就要盡量避免在遊标循環中再進行表連接配接的操作。

四、 Oracle遊标的類型?

1. 靜态遊标:結果集已經确定(靜态定義)的遊标。分為隐式和顯示遊标

   ⑴ 隐式遊标: 系統自動定義的遊标,用于處理select into 和DML語句

   ⑵ 顯示遊标:即使用者自定義遊标,專門用于處理select語句傳回的多行資料 2. REF遊标(動态遊标):動态關聯結果集的臨時對象

靜态遊标指的是程式執行的時候不需要再去解析sql語言,對于sql語句的解析在編譯的時候就可以完成的。 動态遊标由于含有參數,對于sql語句的解析必須要等到參數确定的時候才能完成。 從這個角度來說,靜态遊标的效率也比動态遊标更高一些。 

五、 Oracle遊标的狀态有哪些,怎麼使用遊标屬性? 1. 遊标的狀态是通過屬性來表示

   %Found :Fetch語句(擷取記錄)執行情況True or False

   %NotFound : 最後一條記錄是否提取出True or False

   %ISOpen : 遊标是否打開True or False

   %RowCount :遊标目前提取的行數 

2. 使用遊标的屬性

   例子:

   Begin

      Update emp Set  SAL = SAL + 0.1  Where JOB = 'CLERK';

      If SQL%Found  Then

     DBMS_OUTPUT.PUT_LINE('已經更新!');

      Else

     DBMS_OUTPUT.PUT_LINE('更新失敗!');

      End If;

   End;

六、 如何使用顯示遊标?如何周遊循環遊标?

1. 使用顯示遊标

   ⑴ 聲明遊标:劃分存儲區域,注意此時并沒有執行Select 語句。

      CURSOR  遊标名(參數 清單) [傳回值類型] IS Select語句;

   ⑵ 打開遊标:執行Select 語句,獲得結果集存儲到遊标中,此時遊标指向結果集頭, 而不是第一條記錄。

      Open  遊标名(參數 清單);

   ⑶ 擷取記錄:移動遊标取一條記錄

      Fetch  遊标名InTo  臨時記錄或屬性類型變量;

   ⑷ 關閉遊标:将遊标放入緩沖池中,沒有完全釋放資源,可重新打開。

      Close  遊标名; 2. 周遊循環遊标

   ⑴ For循環遊标

      循環遊标隐式打開遊标,自動滾動擷取一條記錄,并自動建立臨時記錄類型變量存儲記錄,處理完後自動關閉遊标。

      For 變量名 In 遊标名 

      Loop

       資料處理語句;

      End Loop;

   ⑵ Loop循環遊标

      ...

      Loop

      Fatch  遊标名InTo  臨時記錄或屬性類型變量;

      Exit  When   遊标名%NotFound;

      End   Loop;

      ...   例子1:

   Declare

     Cursor myCur is select empno,ename,sal from emp;

     vna varchar2(10);

     vno number(4);

     vsal number(7,2);

   Begin

     open myCur;

     fetch myCur into vno,vna,vsal;

     dbms_output.put_line(vno||' '||vna||' '||vsal);

     close myCur;

   End;

  /

例子2:使用loop周遊遊标。

  Declare

     Cursor myCur is select ename,job,sal,empno from emp;

     varE myCur%rowType;

  Begin

     if myCur%isopen = false then

       open myCur;

       dbms_output.put_line('Opening...');

     end if;

     loop

        fetch myCur into varE;

        exit when myCur%notfound;

        dbms_output.put_line(myCur%rowCount||'    '||vare.empno||'    '||vare.ename||'    '||vare.sal);

     end loop;

     if myCur%isopen then

        Close myCur;

        dbms_output.put_line('Closing...');

     end if;

  End;

  /

  例子3:使用For循環周遊遊标,

  Declare

     Cursor myCur is select * from emp;

  Begin

     for varA in myCur

       loop

         dbms_output.put_line(myCur%rowCount||'    '||varA.empno||'    '||varA.ename||'  '||varA.sal);

       end loop;

  End;

  /

七、 怎樣更新和删除顯示遊标中的記錄?

1. UPDATE或DELETE語句中的WHERE CURRENT OF子串專門處理要執行UPDATE或DELETE操作的表中取出的最近的資料。

   要使用這個方法,在聲明遊标時必須使用FOR UPDATE子串,當對話使用FOR UPDATE子串打開一個遊标時,

   所有傳回集中的資料行都将處于行級(ROW-LEVEL)獨占式鎖定,其他對象隻能查詢這些資料行,

   不能進行UPDATE、DELETE或SELECT...FOR UPDATE操作。

   在多表查詢中,使用OF子句來鎖定特定的表,如果忽略了OF子句,那麼所有表中選擇的資料行都将被鎖定。

   如果這些資料行已經被其他會話鎖定,那麼正常情況下ORACLE将等待,直到資料行解鎖。

2. 使用更新或删除:

   ⑴ 聲明更新或删除顯示遊标:

      Cursor 遊标名 IS SELECT語句  For Update [ Of 更新列列名];

      Cursor 遊标名 IS SELECT語句  For Delete [ Of 更新列列名];

   ⑵ 使用顯示遊标目前記錄來更新或删除:

      Update 表名 SET 更新語句 Where Current Of 遊标名;       Delete From 表名 Where Current Of 遊标名;

  例子1:更新顯示遊标記錄

   Declare

     Cursor myCur is select job from emp for update;

       vjob empa.job%type;

       rsal empa.sal%type;

    Begin

       open myCur;

       loop

          fetch myCur into vjob;

          exit when myCur%notFound;

          case  (vjob)

             when 'ANALYST' then  rsal := 0.1;

             when  'CLERK' then  rsal := 0.2;

             when  'MANAGER' then  rsal := 0.3;

             else

             rsal := 0.5;

          end case;

          update emp set sal = sal + rsal where current of myCur;

       end loop;

    End;

    /

例子2:删除顯示遊标記錄

    Declare

      Cursor MyCursor  Select   JOB  From  empa  For  Update;

      vSal   emp.Sal%TYPE;

    Begin

      Loop

        Fetch  MyCursor  InTo  vSal;

        Exit  When  MyCursor%NotFound;

        If   vSal < 800 Then

           Delete  From empa  Where  Cursor  Of   MyCursor;

        End  If;  

      End    Loop;

    End;/

八、 什麼是帶參數的顯示遊标?

1. 與過程和函數相似,可以将參數傳遞給遊标并在查詢中使用。

   參數隻定義資料類型,沒有大小(所有Oracle中的形參隻定義資料類型,不指定大小)。

   與過程不同的是,遊标隻能接受傳遞的值,而不能傳回值。

  可以給參數設定一個預設值,當沒有參數值傳遞給遊标時,就使用預設值。

   遊标中定義的參數隻是一個占位符,在别處引用該參數不一定可靠。 2. 使用帶參數的顯示遊标

   ⑴ 聲明帶參數的顯示遊标:

      CURSOR 遊标名 [(parameter[,parameter],...)] IS Select語句;

      參數形式:       1. 參數名 資料類型         2. 參數名 資料類型 DEFAULT 預設值

      例子:

    Declare

      Cursor MyCursor(pSal  Number  Default   800)  Select   JOB  From  empa Where  SAL >  pSal ;

      varA  MyCursor%ROWTYPE;

    Begin

       Loop

          Fetch  MyCursor  InTo  varA;

          Exit  When  MyCursor%NotFound;

          DBMS_OUTPUT.PUT_LINE(MyCursor%RowCount||'    '||varA.empno||'    '||varA.ename||'  '||varA.sal); 

       End    Loop;

    End;/

REF CURSOR(動态遊标)

一、 什麼是REF遊标 ? 動态關聯結果集的臨時對象。即在運作的時候動态決定執行查詢。

  二、 REF遊标有什麼作用?

實作在程式間傳遞結果集的功能,利用REF CURSOR也可以實作BULK SQL,進而提高SQL性能。

三、 靜态遊标和REF遊标的差別是什麼?

1. 靜态遊标是靜态定義,REF遊标是動态關聯

2. 使用REF遊标需REF遊标變量

3. REF遊标能做為參數進行傳遞,而靜态遊标是不可能的

四、 什麼是REF遊标變量?

REF遊标變量是一種引用REF遊标類型的變量,指向動态關聯的結果集。

五、 怎麼使用REF遊标 ?

1. 聲明REF 遊标類型,确定REF 遊标類型

   ⑴ 強類型REF遊标:指定retrun type,REF 遊标變量的類型必須和return type一緻。

      文法:Type  REF遊标名  IS  Ref Cursor Return  結果集傳回記錄類型;

   ⑵ 弱類型REF遊标:不指定return type,能和任何類型的CURSOR變量比對,用于擷取任何結果集。

      文法:Type  REF遊标名  IS  Ref Cursor;

  2. 聲明Ref遊标類型變量

   文法:變量名  已聲明Ref遊标類型;

3. 打開REF遊标,關聯結果集

   文法:Open  Ref遊标類型變量  For  查詢語句傳回結果集;

4. 擷取記錄,操作記錄

   文法:Fatch  REF遊标名  InTo  臨時記錄類型變量或屬性類型變量清單;

5. 關閉遊标,完全釋放資源

   文法:Close  REF遊标名;

例子:強類型REF遊标

 Declare

   Type MyRefCurA IS  REF CURSOR RETURN emp%RowType;

   Type MyRefCurB IS  REF CURSOR RETURN emp.ename%Type;

   vRefCurA  MyRefCurA;

   vRefCurB  MyRefCurB;

   vTempA  vRefCurA%RowType;

   vTempB  vRefCurB.ename%Type;

 Begin

   Open  vRefCurA  For Select  *  from   emp   Where  SAL > 2000;

   Loop

     Fatch  vRefCurA InTo  vTempA;

     Exit  When  vRefCurA%NotFound;

     DBMS_OUTPUT.PUT_LINE(vRefCurA%RowCount||'  '|| vTempA.eno||'  '||vTempA.ename ||'  '||vTempA.sal)

   End Loop;

 Close vRefCurA;

 DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------'); 

     Open  vRefCurB  For Select  ename  from   emp   Where  SAL > 2000;

     Loop

       Fatch  vRefCurB InTo  vTempB;

       Exit  When  vRefCurB%NotFound;

       DBMS_OUTPUT.PUT_LINE(vRefCurB%RowCount||'  '||vTempB)

     End Loop;

 Close vRefCurB;

 DBMS_OUTPUT.PUT_LINE('----------------------------------------------------------------');  

     Open  vRefCurA  For Select  *  from   emp   Where  JOB = 'CLERK';

     Loop

       Fatch  vRefCurA InTo  vTempA;

       Exit  When  vRefCurA%NotFound;

       DBMS_OUTPUT.PUT_LINE(vRefCurA%RowCount||'  '|| vTempA.eno||'  '||vTempA.ename ||'  '||vTempA.sal)

     End Loop;

 Close vRefCurA; End;

例子:弱類型REF遊标

 Declare

   Type MyRefCur  IS  Ref  Cursor;

   vRefCur MyRefCur;

   vtemp  vRefCur%RowType;

 Begin

   Case(&n)

     When  1 Then Open vRefCur  For Select   *   from emp;

     When  2 Then Open vRefCur  For Select   *   from dept;

     Else

     Open vRefCur  For Select   eno,  ename  from emp Where JOB = 'CLERK';

   End Case;

 Close  vRefCur; 

 End;