Oracle之PL/SQL學習筆記之遊標(五)

Oracle之PL/SQL學習筆記之遊標(五)


        在PL/SQL程序中,對於處理多行記錄的事務常常使用遊標來實現
sql


1. 遊標的概念

        爲了處理SQL語句,Oracle必須分配一片叫上下文(Context area)的區域來處理所必須的信息,其中包括要處理的行的數目,一個指向語句被分析之後的表示形式和指針以及查詢的活動集(active set)。
數據庫

        遊標是一個指向上下文的句柄(handle)或指針。經過遊標,PL/SQL能夠控制上下文區和處理語句時上下文區會發生些什麼事情。
express

       對於不一樣的SQL語句,遊標的使用狀況不一樣函數

SQL語句 遊標
非查詢語句 隱式的
結果是單行的查詢語句 隱式的或顯示的
結果是多行的查詢語句 顯示的


1.1 處理顯示遊標

 顯示遊標處理須要四個PL/SQL步驟:
oop




  • 定義遊標: 就是定義一個遊標名,以及與其相對應的SELECT語句。學習

    格式:  cursor cursor_name[(parameter[,parameter]...)] is select_statement;fetch

    遊標參數只能爲輸入參數,其格式爲spa

    parameter_name in datatype[{:=|DEFAULT} expression]指針

    注意:
    code

            在指定數據類型時,不能使用長度約束。如NUMBER(4)、CHAR(10)等都是錯誤的。

            定義遊標時,不能有into自居。

  • 打開遊標: 就是執行遊標所對應的SELECT 語句,將其查詢結果放入工做區,而且指針指向工做區的首部,表示遊標結果集合。若是遊標查詢語句中帶有FOR UPDATE選項,open語句還將鎖定數據庫表中游標結果集合對應的數據行。

     OPEN cursor_name[([parameter=>]value[,[parameter=>]value]...)]

    在向遊標傳遞參數時,可使用與函數參數相同的傳值方法,即位置表示法和名稱表示法。PL/SQL程序不能用OPEN語句重複打開一個遊標。

  • 提取遊標數據: 就是檢索結果集中的數據行,放入指定的輸出變量中。

    格式: 

             FETCH cursor_name into {variable_list|record_variable};

    對該記錄進行處理;

    繼續處理,直到活動集合中沒有記錄;

  • 關閉遊標: 當提取和處理完遊標結果集合數據後,應及時關閉遊標,以釋放該遊標所佔用的系統資源,並使該遊標的工做區變成無效,不能再使用FETCH語句取其中數據。關閉後的遊標可使用OPEN語句從新打開。

    格式:

        close cursor;

    實例:

declare
  v_sal emp.sal%type;
  v_empno emp.empno%type;
  cursor emp_cursor is select sal,empno from emp where deptno=30;
begin
  open emp_cursor;--打開遊標
  fetch emp_cursor into v_sal,v_empno;--提取遊標
  while emp_cursor%found loop
     dbms_output.put_line('僱員編號:'||v_empno||',salary:'||v_sal);
     fetch emp_cursor into v_sal,v_empno;--提取遊標
  end loop;
  close emp_cursor;--關閉遊標
end;

   頭腦風暴: 

    假如咱們的遊標查詢不少個字段的值,那麼fetch emp_cursor into v_1,v_2,....是否是很麻煩呢?想象咱們的複合數據類型。


顯示遊標的屬性

 

遊標屬性 值類型 說明
%FOUND 布爾型 當最近一次讀取記錄時成功返回,則值爲true
%NOTFOUND 布爾型 與%FOUND相反
%ISOPEN 布爾型 當遊標已打開時返回true
%ROWCOUNT 數字型 返回已從遊標中讀取的記錄數


1.2 處理隱式遊標

             隱式遊標和顯示遊標有所差別,它雖然沒有顯示遊標同樣的可操做性,可是在實際的工做當中也常常用到。

1.2.1 隱式遊標的特色

           每當容許SELECT或DML語句時,PL/SQL會打開一個隱式的遊標。隱式遊標不受用戶的控制,這一點和顯示遊標有明顯的不一樣。下面列出隱式遊標和顯示遊標的不一樣處。

  •  隱式遊標有PL/SQL自動管理;

  • 隱式遊標的默認名稱是SQL;

  • SELECT 或DML操做產生隱式遊標;

  • 隱式遊標的屬性值始終是最新執行的SQL語句的;

    實例:


--隱式遊標begin
---------------
--業務說明: 跟新指定員工編號員工的工資+11;若是存在該員工更新數據
-- 若是不存在,打印查無此人。
begin
  update emp set sal=sal+11 where empno=7788;
  if sql%notfound then 
    dbms_output.put_line('查無此人');
  end if;
---------------
--隱式遊標end

     

   隱式遊標的屬性

屬性名 值類型 說明
%ISOPEN true/false 該屬性返回false,由Oracle本身控制
%FOUND true/false

此屬性反應DML操做是否影響到了數據,SELECT INTO語句返回數據爲true

%NOTFOUND true/false 與%FOUND相反
%ROWCOUNT number 該屬性返回的是DML操做影響的行數。


2.  顯示遊標與循環控制


  案例:把僱員工資低於3000的調整到3000(loop)


-----------
--業務說明: 把僱員工資低於3000的調整到3000
-----------
declare
   v_sal emp.sal%type;--僱員工資,用於判斷工資是否大於3000
   v_empno emp.empno%type;--僱員編號,用於更新工資
   cursor emp_cursor is select sal,empno from emp;--定義遊標
begin
  --打開遊標
  open emp_cursor;
  loop
    --獲取數據
    fetch emp_cursor into v_sal,v_empno;
    exit when emp_cursor%notfound;
    if v_sal<3000 then 
      update emp set sal=3000 where empno=v_empno;
      dbms_output.put_line('僱員編號爲:'||v_empno||'執行了更新操做');
      commit;
     end if;
  end loop;
  close emp_cursor;--關閉遊標
end;


   咱們能夠對以上案例進行修改,通常狀況下使用cursor時是與for循環配置實用的:(for)

declare
   type emp_sal_empbno_record is record(
        sal emp.sal%type,--僱員工資,這兒須要注意,多個字段,須要用,隔開
        empno emp.empno%type--僱員編號
   );
   cursor emp_cursor is select sal,empno from emp;--定義遊標
begin
  --for循環自動打開遊標,獲取每行的數據,關閉遊標
  for emp_sal_empbno_record in emp_cursor loop
    if emp_sal_empbno_record.sal<3000 then 
      update emp set sal=3000 where empno=emp_sal_empbno_record.empno;
      dbms_output.put_line('僱員編號爲:'||emp_sal_empbno_record.empno||'執行了更新操做');
      commit;
     end if;
  end loop;
end;


    分析:   從上面的代碼中能夠看到,for循環中,沒有open cursor,fetch cursor,close cursor   可是這些都確切的發生了。

    for循環幫咱們在其內部處理了這些操做,簡化了用戶的操做,通常狀況下for循環+cursor 堪稱完美。(特殊狀況除外),

   這兒還有一個須要注意,emp_sal_empbno_record 是記錄類型,不是記錄類型的變量。


   案例: 工資在0-3000 的加薪5%, 3000-4000 加薪3%,4000-5000 加薪2% 5000-n 加薪1%(while)


declare 
  type emp_record is record(
       sal emp.sal%type,
       empno emp.empno%type
  );--定義一個記錄類型
  
  v_emp_record emp_record;--定義記錄類型的變量
  v_temp number(4,2);--加薪比例
  cursor emp_cursor is select sal,empno from emp;--定義遊標
  
begin
  open emp_cursor;--打開遊標
  fetch emp_cursor into v_emp_record;--提取數據
  while emp_cursor%found loop
    if v_emp_record.sal<3000 then
      v_temp:=0.05;--須要注意賦值操做使用:=
    elsif v_emp_record.sal<4000 then
      v_temp:=0.03;
    elsif v_emp_record.sal<5000 then 
      v_temp:=0.02;
    else 
      v_temp:=0.01;
    end if; --記住 if語句結束後必定要有end if
    update emp set sal=sal*(1+v_temp) where empno=v_emp_record.empno;
    commit;   
    --提取數據,必須在while循環內部提取數據,否則就是死循環了
    fetch emp_cursor into v_emp_record;
  end loop;
end;

   

    注意: 兩個案例,對應三種不一樣的循環處理遊標,你們作下對比。


3 使用cursor for loop(簡化迭代結果集)

      對上面的案例進行改進:

declare 
  v_temp number(4,2);--加薪比例
  cursor emp_cursor is select sal,empno from emp;--定義遊標
begin
  --從遊標中取數據,自動打開,關閉,判讀是否還有數據
  for v_emp in emp_cursor
    loop
      if v_emp.sal<3000 then 
        v_temp:=0.005;
      elsif v_emp.sal<4000 then
        v_temp:=0.03;
      elsif v_emp.sal<5000 then
        v_temp:=0.02;
      else 
        v_temp:=0.01;
      end if;
      update emp set sal=sal*(1+v_temp) where empno=v_emp.empno;
      commit;
    end loop;
end;


4 使用BULK COLLECT 和FOR 語句的遊標

      遊標中一般使用fetch.... into ... 語句取數據,這種方式是單條數據提取,在數據量很大的狀況下執行效率不是很理想。而FETCH ... BULK COLLECT INTO 語句能夠批量提取數據,

在數據量大的狀況下它的執行效率比單條提取數據高。

declare 
  cursor emp_cursor is select * from emp;
  type emp_tab is table of emp%rowtype;
  v_emp_tab emp_tab;
begin 
  open emp_cursor;--打開遊標
  loop
    fetch emp_cursor bulk collect into v_emp_tab limit 5;--使用limit顯示一次取得記錄數
    for i in 1 .. v_emp_tab.count loop
        dbms_output.put_line('僱員編號:'||v_emp_tab(i).empno||',僱員姓名:'||v_emp_tab(i).ename);
    end loop;
    exit when emp_cursor%notfound;
  end loop;
end;

   fetch emp_cursor bulk collect into v_emp_tab limit 5; 一次取5條記錄到集合中,減小了取的次數。


5.  帶參數的遊標

---------帶參數的遊標begin-------------
--業務說明: 查詢出某部門中僱員名中包含C的員工的姓名
declare 
    c_ename emp.ename%type:='%C%';
    v_ename emp.ename%type;--僱員姓名
    cursor emp_cursor(
           name varchar2,--僱員名,模糊查詢
           dtno number--部門編號
           ) is select ename from emp where ename like name and deptno=dtno;
begin
  open emp_cursor(c_ename,10);
  loop 
    fetch emp_cursor into v_ename;
    exit when emp_cursor%NOTFOUND;
    dbms_output.put_line(v_ename||'的名字包含C');
  end loop;
  close emp_cursor;
end;
---------帶參數的遊標end---------------


  注意: 申明遊標變量是,參數的類型,不能使用長度限制:

         錯誤的寫法: varchar2(50),number(7,2)

相關文章
相關標籤/搜索