oracle中 遊標實例

遊標-----內存中的一塊區域,存放的是select的結果 
    
    遊標用來處理從數據庫中檢索的多行記錄(使用SELECT語句)。利用遊標,程序能夠逐個地處理和遍歷一次檢索返回的整個記錄集。 
    爲了處理SQL語句,Oracle將在內存中分配一個區域,這就是上下文區。這個區包含了已經處理完的行數、指向被分析語句的指針,遊標就是指向上下文區句柄或指針。 

缺點:遊標使用時會對行加鎖,可能會影響其餘業務的正常進行。 
       並且,數據量大時其效率也較低。由於遊標實際上是至關於把磁盤數據總體放入了內存中,若是遊標數據量大會形成內存不足,也有可能致使cpu高,因此,在數據量小時才使用遊標。 

優勢:至關於程序中的FOR循環處理。一條一條的處理你讀取的記錄內容。 
       數據存放在內存中,數據量少的時候速度比較快。 

1、顯示遊標 
    顯示遊標被用於處理返回多行數據的SELECT 語句,遊標名經過CURSOR….IS 語句顯示地賦給SELECT 語句。 
     在PL/SQL中處理顯示遊標所必需的四個步驟: 
     1)聲明遊標;CURSOR cursor_name IS select_statement 
     2)爲查詢打開遊標;OPEN cursor_name 
     3)取得結果放入PL/SQL變量中; 
            FETCH cursor_name INTO list_of_variables; 
            FETCH cursor_name INTO PL/SQL_record; 
     4)關閉遊標。CLOSE cursor_name 
     注意:在聲明遊標時,select_statement不能包含INTO子句。當使用顯示遊標時,INTO子句是FETCH語句的一部分。 
   
2、隱式遊標 
     全部的隱式遊標都被假設爲只返回一條記錄。 
     使用隱式遊標時,用戶無需進行聲明、打開及關閉。PL/SQL隱含地打開、處理,而後關掉遊標。 
     例如: 
     SELECT studentNo,studentName INTO curStudentNo,curStudentName 
     FROM StudentRecord  WHERE name=’gg’; 
     上述遊標自動打開,並把相關值賦給對應變量,而後關閉。執行完後,PL/SQL變量curStudentNo,curStudentName中已經有了值。 
   
     單條sql語句所產生的結果集合 
     用關鍵字SQL表示隱式遊標 
     4個屬性 %rowcount  影響的記錄的行數  整數 
             %found     影響到了記錄  true 
             %notfound  沒有影響到記錄  true 
             %isopen    是否打開  布爾值 永遠是false 
     多條sql語句 隱式遊標SQL永遠指的是最後一條sql語句的結果 
     主要使用在update 和 delete語句上 
     
3、常見遊標實例     
(1)for循環遊標 
SQL> set timing on 
SQL> set serverout on 
SQL> set pagesize 1000 
SQL> set linesize 400 
SQL> declare 
        cursor c1 is select id,ins_time,random_string from mytest where random_id=50;  ---定義遊標 
        c1cur c1%rowtype;       ---定義一個遊標變量 
     begin 
        for c1cur in c1 loop    ---for循環 
          dbms_output.put_line(c1cur.id||':'||c1cur.ins_time||'-'||c1cur.random_string); 
        end loop; 
     end; 
     / 
113:2015-12-23 16:39:41-1TP6AU8U5JJ1O16FIUSZ 
176:2015-12-23 16:40:44-N77Y47YGZQJH2ONG504R 
187:2015-12-23 16:40:55-PIQG1HNPM6EVFZXD8N7G 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.02 


(2) fetch遊標    --使用的時候 必需要明確的打開和關閉 
      declare 
        cursor c1 is select id,ins_time,random_id,random_string from mytest where random_id=50;  ---定義顯示遊標 
        c1cur c1%rowtype;       ---定義一個遊標變量 
      begin 
         open c1;  --打開遊標 
         loop 
            fetch c1 into c1cur;     ---取一行數據到遊標變量      
            exit when c1%notfound;   ---判斷是否取到了值 
            dbms_output.put_line(c1cur.id||':'||c1cur.ins_time||'-'||c1cur.random_string);           
         end loop; 
         close c1;   ---關閉遊標 
      end; 
      / 
113:2015-12-23 16:39:41-1TP6AU8U5JJ1O16FIUSZ 
176:2015-12-23 16:40:44-N77Y47YGZQJH2ONG504R 
187:2015-12-23 16:40:55-PIQG1HNPM6EVFZXD8N7G 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.02 

(3)參數遊標(如下都利用scott自帶的幾張表測試) 
SQL> declare 
       cursor c1 is select deptno from dept; 
       cursor c2(pno number,pjob varchar2) is select * from emp where deptno=pno and job=pjob; --只能指定類型,不能指定長度 
       c1cur c1%rowtype; 
       c2cur c2%rowtype; 
     begin 
       for c1cur in c1 loop 
         for c2cur in c2(c1cur.deptno,'MANAGER')loop 
           dbms_output.put_line(c1cur.deptno||':'||c2cur.ename||'---'||c2cur.sal); 
         end loop; 
       end loop; 
     end; 
     / 
10:CLARK---2450 
20:JONES---2975 
30:BLAKE---2850 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.02 

(4)引用遊標/動態遊標 
SQL>declare 
       type c1 is ref cursor;     --定義一個類型(ref cursor)弱類型  
       type c2 is ref cursor return emp%rowtype;   --定義一個強類型(返回的結果集有要求) 
       c0cur c1;              ---定義一個弱類型的遊標變量 
       c1cur emp%rowtype; 
       c2cur dept%rowtype; 
    begin 
       dbms_output.put_line('all employees :'); 
       open c0cur for select * from emp; 
       loop 
          fetch c0cur into c1cur; 
          exit when c0cur%notfound; 
          dbms_output.put_line(c1cur.ename); 
       end loop; 
       dbms_output.put_line('all departments:'); 
       open c0cur for select * from dept; 
       loop 
         fetch c0cur into c2cur; 
         exit when c0cur%notfound; 
         dbms_output.put_line(c2cur.dname); 
       end loop; 
       close c0cur; 
    end; 
   / 
all employees: 
SMITH 
ALLEN 
WARD 
JONES 
MARTIN 
BLAKE 
CLARK 
SCOTT 
KING 
TURNER 
ADAMS 
JAMES 
FORD 
MILLER 
all departments: 
ACCOUNTING 
RESEARCH 
SALES 
OPERATIONS 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.02 

(5)while循環 
SQL> set timing on 
SQL> set serverout on 
SQL> set pagesize 1000 
SQL> set linesize 400 
SQL> declare 
       cursor c1 is select loc from dept; 
       c1cur c1%rowtype; 
     begin 
       open c1; 
       fetch c1 into c1cur; 
       while c1%found loop 
       dbms_output.put_line('location:  '||c1cur.loc); 
       fetch c1 into c1cur;   ---與for循環不一樣,while循環須要將下一行的值賦給遊標變量,不然會進入死循環,並報錯 
       end loop; 
       close c1; 
     end; 
    / 
location:  NEW YORK 
location:  DALLAS 
location:  CHICAGO 
location:  BOSTON 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.10 


死循環,報錯: 
SQL> declare 
       cursor c1 is select loc from dept; 
       c1cur c1%rowtype; 
     begin 
       open c1; 
       fetch c1 into c1cur; 
       while c1%found loop 
       dbms_output.put_line('location:  '||c1cur.loc); 
       end loop; 
       close c1;  
     end; 
     / 
location:  NEW YORK 
location:  NEW YORK 
location:  NEW YORK 
location:  NEW YORK 
location:  NEW YORK 
declare 

ERROR at line 1: 
ORA-04030: out of process memory when trying to allocate 16328 bytes (koh-kghu sessi,pl/sql vc2) 

執行時,單個進程就致使cpu 100%,所以遊標仍是要當心使用的! 


(6)利用遊標來更新(update) 
6.1 聲明更新顯示遊標: 
   Cursor 遊標名IS  SELECT 語句   For Update [ Of 更新列列名]; 
6.2 使用顯示遊標當前記錄來更新: 
   Update  表名   SET   更新語句  Where   Current  Of   遊標名; 

1.全部人普調 
SQL> declare 
       cursor c1 is select * from emp for update of sal; 
       c1cur c1%rowtype; 
       saladd emp.sal%type; 
       salnew emp.sal%type; 
     begin 
       for c1cur in c1 loop 
         saladd:=c1cur.sal*0.2;  --全部員工加薪20% 
           if saladd<300 then 
             salnew:=c1cur.sal+300;   --加薪不足300的,按300加 
             dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
           else 
             salnew:=c1cur.sal+saladd; 
             dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
           end if; 
             update emp set sal=salnew where current of c1; 
       end loop; 
     end ; 
     / 
SMITH:new salary 1300  ---原來工資1000 
ALLEN:new salary 2304 
WARD:new salary 1800 
JONES:new salary 4284 
MARTIN:new salary 1800 
BLAKE:new salary 4104 
CLARK:new salary 3528 
SCOTT:new salary 4320 
KING:new salary 7200 
TURNER:new salary 2160 
ADAMS:new salary 1620 
JAMES:new salary 1450 
FORD:new salary 4320 
MILLER:new salary 1872 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.04 

2.按照部門來調整 
declare                
   cursor c1 is select * from emp for update of sal; 
   c1cur c1%rowtype;   
   salnew emp.sal%type; 
begin                  
   for c1cur in c1 loop 
   case                ---必須包含全部的分類,不然會報錯ORA-06592: CASE not found while executing CASE statement 
      when c1cur.deptno=10 
      then salnew:=c1cur.sal*1.1; 
      dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
      when c1cur.deptno=20 
      then salnew:=c1cur.sal*1.15; 
      dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
      when c1cur.deptno=30 
      then salnew:=c1cur.sal*1.2; 
      dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
      when c1cur.deptno=40 
      then salnew:=c1cur.sal*1.3; 
      dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
   end case; 
      update emp set sal=salnew where current of c1; 
   end loop; 
end; 


SMITH:new salary 1495 
ALLEN:new salary 2764.8 
WARD:new salary 2160 
JONES:new salary 4926.6 
MARTIN:new salary 2160 
BLAKE:new salary 4924.8 
CLARK:new salary 3880.8 
SCOTT:new salary 4968 
KING:new salary 7920 
TURNER:new salary 2592 
ADAMS:new salary 1863 
JAMES:new salary 1740 
FORD:new salary 4968 
MILLER:new salary 2059.2 
LITING:new salary 7800 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.07 

3.符合if條件的 
declare                
   cursor c1 is select * from emp for update of sal; 
   c1cur c1%rowtype;   
   salnew emp.sal%type; 
begin                  
   for c1cur in c1 loop 
     if c1cur.deptno=20 then 
        salnew:=c1cur.sal*5-8000; 
        dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
     else   
        salnew:=c1cur.sal;        
     end if; 
        update emp set sal=salnew where current of c1; 
   end loop; 
end; 

SMITH:new salary 1525 
JONES:new salary 6633 
SCOTT:new salary 6840 
ADAMS:new salary 1315 
FORD:new salary 6840 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.05 


4.以平均值爲分界 
SQL> select avg(sal) from emp;  平均工資3715 
  AVG(SAL) 
---------- 
      3715 
      
SQL> declare 
  2  cursor c1 is select empno,ename,sal,deptno,avg(sal) over(partition by deptno) as depavg from emp for update of sal;
  3  c1cur c1%rowtype; 
  4  salnew emp.sal%type; 
  5  begin 
  6  for c1cur in c1 loop 
  7  if c1cur.sal>c1cur.depavg then 
  8  salnew:=c1cur.sal+50;    ---高於平均工資的獎金髮50 
  9  dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew);  
10  else 
11  salnew:=c1cur.sal+100;   ---低於平均工資的獎金髮100 
12  dbms_output.put_line(c1cur.ename||':'||'new salary '||salnew); 
13  end if; 
14  update emp set sal=salnew where current of c1; 
15  end loop; 
16  end; 
17  / 
CLARK:new salary 3980 
MILLER:new salary 2160 
KING:new salary 7970 
JONES:new salary 4976 
SMITH:new salary 1595 
SCOTT:new salary 5018 
FORD:new salary 5071 
ADAMS:new salary 1415 
WARD:new salary 2260 
TURNER:new salary 2692 
ALLEN:new salary 2814 
JAMES:new salary 1840 
MARTIN:new salary 2260 
BLAKE:new salary 4974 
LITING:new salary 7900 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.47      


(7)利用遊標來刪除(delete) 
7.1 聲明刪除顯示遊標: 
   Cursor 遊標名IS  SELECT 語句   For Delete; 
7.2 使用顯示遊標當前記錄來更新或刪除: 
   Delete  From  表名   Where   Current  Of   遊標名; 

SQL> create table emp1 as select * from emp; 
Table created. 
Elapsed: 00:00:03.05 

SQL> declare 
       cursor c1 is select ename,job from emp1 for update; 
       e_job emp1.job%type; 
       e_name emp1.ename%type; 
     begin 
       open c1; 
       loop 
       fetch c1 into e_name,e_job;  --順序要跟上面的select順序對應 
       exit when c1%notfound; 
          if e_job='ANALYST' then 
             delete from emp1 where current of c1; 
             dbms_output.put_line(e_name||':'||'deleted'); 
          end if; 
       end loop; 
       close c1; 
     end; 
     /  
SCOTT:deleted 
FORD:deleted 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.09 

(8)利用遊標來插入 
SQL> declare 
  2  cursor c1 is select * from emp1; 
  3  c1cur c1%rowtype; 
  4  begin 
  5  open c1; 
  6  loop 
  7  fetch c1 into c1cur; 
  8  exit when c1%notfound; 
  9  insert into emp values c1cur; 
10  dbms_output.put_line(c1cur.ename||': inserted'); 
11  end loop; 
12  close c1; 
13  end; 
14  / 
TINA: inserted 
BOBO: inserted 
LIO: inserted 

PL/SQL procedure successfully completed. 
Elapsed: 00:00:00.11 

SQL> select * from emp;  ---查看數據,插入成功 

     EMPNO ENAME      JOB         MGR HIREDATE     SAL       COMM     DEPTNO 
---------- ---------- ---------   ---------- --------- ---------- ---------- ---------- 
      7369 SMITH      CLERK       7902 17-DEC-80    1595    20 
      7499 ALLEN      SALESMAN   7698 20-FEB-81    2814        300    30 
      7521 WARD       SALESMAN   7698 22-FEB-81    2260        500    30 
      7566 JONES      MANAGER     7839 02-APR-81    4976    20 
      7654 MARTIN     SALESMAN   7698 28-SEP-81    2260       1400    30 
      7698 BLAKE      MANAGER     7839 01-MAY-81    4974    30 
      7782 CLARK      MANAGER     7839 09-JUN-81    3980    10 
      7788 SCOTT      ANALYST     7566 19-APR-87    5018    20 
      7839 KING       PRESIDENT      17-NOV-81    7970    10 
      7844 TURNER     SALESMAN   7698 08-SEP-81    2692       0      30 
      7876 ADAMS      CLERK       7788 23-MAY-87    1415    20 
      7900 JAMES      CLERK       7698 03-DEC-81    1840    30 
      7902 FORD       ANALYST     7566 03-DEC-81    5071    20 
      7934 MILLER     CLERK       7782 23-JAN-82    2160    10 
      7200 LITING     CLERK       7788 03-DEC-81    7900        500    40 
      6000 TINA       CLERK       7788 02-APR-86    4503        300    10 
      6221 BOBO       CLERK       7788 02-APR-86    4000        200    10 
      6307 LIO       CLERK       7788 02-APR-86    4409       0      10sql

相關文章
相關標籤/搜索