1. 程式人生 > 資料庫 >Oracle中游標Cursor基本用法詳解

Oracle中游標Cursor基本用法詳解

查詢

  SELECT語句用於從資料庫中查詢資料,當在PL/SQL中使用SELECT語句時,要與INTO子句一起使用,查詢的
返回值被賦予INTO子句中的變數,變數的宣告是在DELCARE中。SELECT INTO語法如下:

  SELECT [DISTICT|ALL]{*|column[,column,...]}
  INTO (variable[,variable,...] |record)
  FROM {table|(sub-query)}[alias]
  WHERE............

  PL/SQL中SELECT語句只返回一行資料。如果超過一行資料,那麼就要使用顯式遊標(對遊標的討論我們將

在後面進行),INTO子句中要有與SELECT子句中相同列數量的變數。INTO子句中也可以是記錄變數。

%TYPE屬性

  在PL/SQL中可以將變數和常量宣告為內建或使用者定義的資料型別,以引用一個列名,同時繼承他的資料類
型和大小。這種動態賦值方法是非常有用的,比如變數引用的列的資料型別和大小改變了,如果使用了%TYPE,
那麼使用者就不必修改程式碼,否則就必須修改程式碼。

例:

  v_empno SCOTT.EMP.EMPNO%TYPE;
  v_salary EMP.SALARY%TYPE;

  不但列名可以使用%TYPE,而且變數、遊標、記錄,或宣告的常量都可以使用%TYPE。這對於定義相同資料類

型的變數非常有用。

  DELCARE
   V_A NUMBER(5):=10;
  V_B V_A%TYPE:=15;
  V_C V_A%TYPE;
  BEGIN
  DBMS_OUTPUT.PUT_LINE
  ('V_A='||V_A||'V_B='||V_B||'V_C='||V_C);
  END
  SQL>/
  V_A=10 V_B=15 V_C=
  PL/SQL procedure successfully completed.
  SQL>

其他DML語句

其它操作資料的DML語句是:INSERT、UPDATE、DELETE和LOCK TABLE,這些語句在PL/SQL中的語法與在SQL中

的語法相同。我們在前面已經討

論過DML語句的使用這裡就不再重複了。在DML語句中可以使用任何在DECLARE部分宣告的變數,如果是巢狀
塊,那麼要注意變數的作用範圍。

例:

  CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number)
  AS
  v_ename EMP.ENAME%TYPE;
  BEGIN
  SELECT ename INTO v_ename
  FROM emp
  WHERE empno=p_empno;
  INSERT INTO FORMER_EMP(EMPNO,ENAME)
  VALUES (p_empno,v_ename);
  DELETE FROM emp
  WHERE empno=p_empno;
  UPDATE former_emp
  SET date_deleted=SYSDATE
  WHERE empno=p_empno;
  EXCEPTION
  WHEN NO_DATA_FOUND THEN
  DBMS_OUTPUT.PUT_LINE('Employee Number Not Found!');
  END

  DML語句的結果

當執行一條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,如果沒有成功,SQL%ROWCOUNT的值為0,同時產生一個異常NO_DATA_FOUND.

SQL%ISOPEN

SQL%ISOPEN是一個布林值,如果遊標開啟,則為TRUE,如果遊標關閉,則為FALSE.對於隱式遊標而言SQL%
ISOPEN總是FALSE,這是因為隱式遊

標在DML語句執行時開啟,結束時就立即關閉。

事務控制語句

事務是一個工作的邏輯單元可以包括一個或多個DML語句,事物控制幫助使用者保證資料的一致性。如果事務控制邏輯單元中的任何一個DML
語句失敗,那麼整個事務都將回滾,在PL/SQL中使用者可以明確地使用COMMIT、ROLLBACK、SAVEPOINT以及
SET TRANSACTION語句。

COMMIT語句終止事務,永久儲存資料庫的變化,同時釋放所有LOCK,ROLLBACK終止現行事務釋放所有LOCK,
但不儲存資料庫的任何變化,SAVEPOI

NT用於設定中間點,當事務呼叫過多的資料庫操作時,中間點是非常有用的,SET TRANSACTION用於設定事
務屬性,比如read-write和隔離級等。

顯式遊標

查詢返回結果超過一行時,就需要一個顯式遊標,此時使用者不能使用select into語句。PL/SQL管理隱式
遊標,當查詢開始時隱式遊標開啟,查詢結束時隱式遊標自動關閉。顯式遊標在PL/SQL塊的宣告部分宣告,在執行部分或異常處理部分開啟,取資料,關閉。

使用遊標

這裡要做一個宣告,我們所說的遊標通常是指顯式遊標,因此從現在起沒有特別指明的情況,我們所說的
遊標都是指顯式遊標。要在程式中使用遊標,必須首先宣告遊標。

宣告遊標

語法:

CURSOR cursor_name IS select_statement;

在PL/SQL中游標名是一個未宣告變數,不能給遊標名賦值或用於表示式中。

例:

  DELCARE
  CURSOR C_EMP IS SELECT empno,ename,salary
  FROM emp
  WHERE salary>2000
  ORDER BY ename;
  ........
  BEGIN

  在遊標定義中SELECT語句中不一定非要表可以是檢視,也可以從多個表或檢視中選擇的列,甚至可以使用*
來選擇所有的列 。

開啟遊標

使用遊標中的值之前應該首先開啟遊標,開啟遊標初始化查詢處理。開啟遊標的語法是:

  OPEN cursor_name

 cursor_name是在宣告部分定義的遊標名。

例:

OPEN C_EMP;

  關閉遊標

語法:

 CLOSE cursor_name

例:

CLOSE C_EMP;

從遊標提取資料

從遊標得到一行資料使用FETCH命令。每一次提取資料後,遊標都指向結果集的下一行。語法如下:

FETCH cursor_name INTO variable[,...]

對於SELECT定義的遊標的每一列,FETCH變數列表都應該有一個變數與之相對應,變數的型別也要相同。

例:

  SET SERVERIUTPUT ON
  DECLARE
  v_ename EMP.ENAME%TYPE;
  v_salary EMP.SALARY%TYPE;
  CURSOR c_emp IS SELECT ename,salary FROM emp;
  BEGIN
  OPEN c_emp;
  FETCH c_emp INTO v_ename,v_salary;
DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  FETCH c_emp INTO v_ename,v_salary;
  DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  FETCH c_emp INTO v_ename,v_salary;
  DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  CLOSE c_emp;
  END

這段程式碼無疑是非常麻煩的,如果有多行返回結果,可以使用迴圈並用遊標屬性為結束迴圈的條件,以這
種方式提取資料,程式的可讀性和簡潔性都大為提高,下面我們使用迴圈重新寫上面的程式:

  SET SERVERIUTPUT ON
  DECLARE
  v_ename EMP.ENAME%TYPE;
  v_salary EMP.SALARY%TYPE;
  CURSOR c_emp IS SELECT ename,salary FROM emp;
  BEGIN
  OPEN c_emp;
  LOOP
  FETCH c_emp INTO v_ename,v_salary;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
  END

  記錄變數

定義一個記錄變數使用TYPE命令和%ROWTYPE,關於%ROWsTYPE的更多資訊請參閱相關資料。

記錄變數用於從遊標中提取資料行,當遊標選擇很多列的時候,那麼使用記錄比為每列宣告一個變數要方
便得多。

當在表上使用%ROWTYPE並將從遊標中取出的值放入記錄中時,如果要選擇表中所有列,那麼在SELECT子句
中使用*比將所有列名列出來要安全得多。

例:

  SET SERVERIUTPUT ON
  DECLARE
  R_emp EMP%ROWTYPE;
  CURSOR c_emp IS SELECT * FROM emp;
  BEGIN
  OPEN c_emp;
  LOOP
  FETCH c_emp INTO r_emp;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
  END LOOP;
  CLOSE c_emp;
  END;

%ROWTYPE也可以用遊標名來定義,這樣的話就必須要首先宣告遊標:

  SET SERVERIUTPUT ON
  DECLARE
  CURSOR c_emp IS SELECT ename,salary FROM emp;
  R_emp c_emp%ROWTYPE;
  BEGIN
  OPEN c_emp;
  LOOP
  FETCH c_emp INTO r_emp;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
  END LOOP;
  CLOSE c_emp;
  END;

帶引數的遊標

與儲存過程和函式相似,可以將引數傳遞給遊標並在查詢中使用。這對於處理在某種條件下開啟遊標的情
況非常有用。它的語法如下:

CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;

定義引數的語法如下:

Parameter_name [IN] data_type[{:=|DEFAULT} value]

與儲存過程不同的是,遊標只能接受傳遞的值,而不能返回值。引數只定義資料型別,沒有大小。

另外可以給引數設定一個預設值,當沒有引數值傳遞給遊標時,就使用預設值。遊標中定義的引數只是一個佔位符,在別處引用該引數不一定可靠。

在開啟遊標時給引數賦值,語法如下:

OPEN cursor_name[value[,value]....];

引數值可以是文字或變數。

例:

  DECALRE
  CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
  CURSOR c_emp (p_dept VARACHAR2) IS
  SELECT ename,salary
  FROM emp
  WHERE deptno=p_dept
  ORDER BY ename
  r_dept DEPT%ROWTYPE;
  v_ename EMP.ENAME%TYPE;
  v_salary EMP.SALARY%TYPE;
  v_tot_salary EMP.SALARY%TYPE;
  BEGIN
  OPEN c_dept;
  LOOP
  FETCH c_dept INTO r_dept;
  EXIT WHEN c_dept%NOTFOUND;
  DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  v_tot_salary:=0;
  OPEN c_emp(r_dept.deptno);
  LOOP
  FETCH c_emp INTO v_ename,v_salary;
  EXIT WHEN c_emp%NOTFOUND;
  DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
  v_tot_salary:=v_tot_salary+v_salary;
  END LOOP;
  CLOSE c_emp;
  DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  END LOOP;
  CLOSE c_dept;
  END;

  遊標FOR迴圈

在大多數時候我們在設計程式的時候都遵循下面的步驟:

1、開啟遊標
2、開始迴圈
3、從遊標中取值
4、檢查那一行被返回
5、處理
6、關閉迴圈
7、關閉遊標

可以簡單的把這一類程式碼稱為遊標用於迴圈。但還有一種迴圈與這種型別不相同,這就是FOR迴圈,用於
FOR迴圈的遊標按照正常的宣告方式宣告,它的優點在於不需要顯式的開啟、關閉、取資料,測試資料的存在、定義存放資料的變數等等。

遊標FOR迴圈的語法如下:

  FOR record_name IN
  (corsor_name[(parameter[,parameter]...)]
  | (query_difinition)
  LOOP
  statements
  END LOOP;

  下面我們用for迴圈重寫上面的例子:

  DECALRE
  CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
  CURSOR c_emp (p_dept VARACHAR2) IS
  SELECT ename,salary
  FROM emp
  WHERE deptno=p_dept
  ORDER BY ename
  v_tot_salary EMP.SALARY%TYPE;
BEGIN
  FOR r_dept IN c_dept LOOP
  DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  v_tot_salary:=0;
  FOR r_emp IN c_emp(r_dept.deptno) LOOP
  DBMS_OUTPUT.PUT_LINE('Name:' || v_ename || 'salary:' || v_salary);
  v_tot_salary:=v_tot_salary+v_salary;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  END LOOP;
  END;

  在遊標FOR迴圈中使用查詢

在遊標FOR迴圈中可以定義查詢,由於沒有顯式宣告所以遊標沒有名字,記錄名通過遊標查詢來定義。

  DECALRE
  v_tot_salary EMP.SALARY%TYPE;
  BEGIN
  FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
  DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
  v_tot_salary:=0;
  FOR r_emp IN (SELECT ename,salary
  FROM emp
  WHERE deptno=p_dept
  ORDER BY ename) LOOP
  DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
  v_tot_salary:=v_tot_salary+v_salary;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
  END LOOP;
  END;

  遊標中的子查詢

  語法如下:

  CURSOR C1 IS SELECT * FROM emp
  WHERE deptno NOT IN (SELECT deptno
  FROM dept
  WHERE dname!='ACCOUNTING');

  可以看出與SQL中的子查詢沒有什麼區別。

  遊標中的更新和刪除

  在PL/SQL中依然可以使用UPDATE和DELETE語句更新或刪除資料行。顯式遊標只有在需要獲得多行資料的情
況下使用。PL/SQL提供了僅僅使  用遊標就可以執行刪除或更新記錄的方法。
  UPDATE或DELETE語句中的WHERE CURRENT OF子串專門處理要執行UPDATE或DELETE操作的表中取出的最近的
資料。要使用這個方法,在宣告遊標  時必須使用FOR UPDATE子串,當對話使用FOR UPDATE子串開啟一個遊標時,所有返回集中的資料行都將處於行級(ROW-LEVEL)獨佔式鎖定,其
  他物件只能查詢這些資料行,不能進行UPDATE、DELETE或SELECT...FOR UPDATE操作。

語法:

  FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
  [nowait]

  在多表查詢中,使用OF子句來鎖定特定的表,如果忽略了OF子句,那麼所有表中選擇的資料行都將被鎖定。
如果這些資料行已經被其他會話鎖定,那麼正常情況下ORACLE將等待,直到資料行解鎖。

  在UPDATE和DELETE中使用WHERE CURRENT OF子串的語法如下:

  WHERE{CURRENT OF cursor_name|search_condition}

  例:

  DELCARE
  CURSOR c1 IS SELECT empno,salary
  FROM emp
  WHERE comm IS NULL
  FOR UPDATE OF comm;
  v_comm NUMBER(10,2);
  BEGIN
  FOR r1 IN c1 LOOP
  IF r1.salary<500 THEN
  v_comm:=r1.salary*0.25;
  ELSEIF r1.salary<1000 THEN
  v_comm:=r1.salary*0.20;
  ELSEIF r1.salary<3000 THEN
  v_comm:=r1.salary*0.15;
  ELSE
  v_comm:=r1.salary*0.12;
  END IF;
  UPDATE emp;
  SET comm=v_comm
  WHERE CURRENT OF c1l;
  END LOOP;
  END
 -宣告遊標
--宗地表的調查日期LANDINFO_RESEARCHDATE
--複製到流程表的權屬調查時間FLOW_REASEARCHTIME
DECLARE 
cursor cur_sel_all is select LANDINFO_RESEARCHDATE,LANDINFO_LANDNO from t_leoa_landinfo; --定義遊標
   l_date t_leoa_landinfo.landinfo_researchdate%type; --宣告變數分別儲存t_leoa_landinfo的各列
   l_landNo t_leoa_landinfo.landinfo_landno%type; 
begin
open cur_sel_all;
  loop                 --迴圈取數,並將遊標資料填充到返回紀錄集合中
   fetch cur_sel_all into l_date,l_landNo;
   exit when cur_sel_all%NOTFOUND; --迴圈退出條件
   if cur_sel_all%FOUND then --獲取資料 
    update T_LEOA_BOOKFLOW t2 set FLOW_REASEARCHTIME = l_date where l_landNo = t2.landinfo_landno; 
   end if;
  end loop;
close cur_sel_all;
end;

下面再分享一下另外一則遊標使用方法的程式碼,具體如下:

-- 宣告遊標;CURSOR cursor_name IS select_statement
--For 循環遊標
--(1)定義遊標
--(2)定義遊標變數
--(3)使用for迴圈來使用這個遊標
declare
    --型別定義
    cursor c_job
    is
    select empno,job,sal
    from emp
    where job='MANAGER';
    --定義一個遊標變數v_cinfo c_emp%ROWTYPE ,該型別為遊標c_emp中的一行資料型別
    c_row c_job%rowtype;
begin
    for c_row in c_job loop
     dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
    end loop;
end;
--Fetch遊標
--使用的時候必須要明確的開啟和關閉
declare 
    --型別定義
    cursor c_job
    is
    select empno,sal
    from emp
    where job='MANAGER';
    --定義一個遊標變數
    c_row c_job%rowtype;
begin
    open c_job;
     loop
      --提取一行資料到c_row
      fetch c_job into c_row;
      --判讀是否提取到值,沒取到值就退出
      --取到值c_job%notfound 是false 
      --取不到值c_job%notfound 是true
      exit when c_job%notfound;
      dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
     end loop;
    --關閉遊標
   close c_job;
end;
--1:任意執行一個update操作,用隱式遊標sql的屬性%found,%notfound,%rowcount,%isopen觀察update語句的執行情況。
    begin
     update emp set ENAME='ALEARK' WHERE EMPNO=7469;
     if sql%isopen then
      dbms_output.put_line('Openging');
      else
       dbms_output.put_line('closing');
       end if;
     if sql%found then
      dbms_output.put_line('遊標指向了有效行');--判斷遊標是否指向有效行
      else
       dbms_output.put_line('Sorry');
       end if;
       if sql%notfound then
        dbms_output.put_line('Also Sorry');
        else
         dbms_output.put_line('Haha');
         end if;
          dbms_output.put_line(sql%rowcount);
          exception 
           when no_data_found then
            dbms_output.put_line('Sorry No data');
            when too_many_rows then
             dbms_output.put_line('Too Many rows');
             end;
declare
    empNumber emp.EMPNO%TYPE;
    empName emp.ENAME%TYPE;
    begin
     if sql%isopen then
      dbms_output.put_line('Cursor is opinging');
      else
       dbms_output.put_line('Cursor is Close');
       end if;
       if sql%notfound then
        dbms_output.put_line('No Value');
        else
         dbms_output.put_line(empNumber);
         end if;
         dbms_output.put_line(sql%rowcount);
         dbms_output.put_line('-------------');
         select EMPNO,ENAME into empNumber,empName from emp where EMPNO=7499;
         dbms_output.put_line(sql%rowcount);
        if sql%isopen then
        dbms_output.put_line('Cursor is opinging');
        else
        dbms_output.put_line('Cursor is Closing');
        end if;
         if sql%notfound then
         dbms_output.put_line('No Value');
         else
         dbms_output.put_line(empNumber);
         end if;
         exception 
          when no_data_found then
           dbms_output.put_line('No Value');
           when too_many_rows then
            dbms_output.put_line('too many rows');
            end;
--2,使用遊標和loop迴圈來顯示所有部門的名稱
--遊標宣告
declare 
    cursor csr_dept
    is
    --select語句
    select DNAME
    from Depth;
    --指定行指標,這句話應該是指定和csr_dept行型別相同的變數
    row_dept csr_dept%rowtype;
begin
    --for迴圈
    for row_dept in csr_dept loop
      dbms_output.put_line('部門名稱:'||row_dept.DNAME);
    end loop;
end;
--3,使用遊標和while迴圈來顯示所有部門的的地理位置(用%found屬性)
declare
    --遊標宣告
    cursor csr_TestWhile
    is
    --select語句
    select LOC
    from Depth;
    --指定行指標
    row_loc csr_TestWhile%rowtype;
begin
 --開啟遊標
    open csr_TestWhile;
    --給第一行喂資料
    fetch csr_TestWhile into row_loc;
    --測試是否有資料,並執行迴圈
     while csr_TestWhile%found loop
      dbms_output.put_line('部門地點:'||row_loc.LOC);
      --給下一行喂資料
      fetch csr_TestWhile into row_loc;
     end loop;
    close csr_TestWhile;
end; 
select * from emp
    
--4,接收使用者輸入的部門編號,用for迴圈和遊標,打印出此部門的所有僱員的所有資訊(使用循環遊標)
--CURSOR cursor_name[(parameter[,...)] IS select_statement;
--定義引數的語法如下:Parameter_name [IN] data_type[{:=|DEFAULT} value] 
declare 
   CURSOR 
   c_dept(p_deptNo number)
   is
   select * from emp where emp.depno=p_deptNo;
   r_emp emp%rowtype;
begin
    for r_emp in c_dept(20) loop
      dbms_output.put_line('員工號:'||r_emp.EMPNO||'員工名:'||r_emp.ENAME||'工資:'||r_emp.SAL);
    end loop;
end;
select * from emp  
--5:向遊標傳遞一個工種,顯示此工種的所有僱員的所有資訊(使用引數遊標)
declare 
    cursor
    c_job(p_job nvarchar2)
    is 
    select * from emp where JOB=p_job;
    r_job emp%rowtype;
begin 
    for r_job in c_job('CLERK') loop
      dbms_output.put_line('員工號'||r_job.EMPNO||' '||'員工姓名'||r_job.ENAME);
    end loop;
end;
SELECT * FROM EMP
--6:用更新遊標來為僱員加佣金:(用if實現,建立一個與emp表一摸一樣的emp1表,對emp1表進行修改操作),並將更新前後的資料輸出出來 
--http://zheng12tian.iteye.com/blog/815770 
    create table emp1 as select * from emp;
declare
    cursor
    csr_Update
    is
    select * from emp1 for update OF SAL;
    empInfo csr_Update%rowtype;
    saleInfo emp1.SAL%TYPE;
begin
  FOR empInfo IN csr_Update LOOP
   IF empInfo.SAL<1500 THEN
    saleInfo:=empInfo.SAL*1.2;
    elsif empInfo.SAL<2000 THEN
    saleInfo:=empInfo.SAL*1.5;
    elsif empInfo.SAL<3000 THEN
    saleInfo:=empInfo.SAL*2;
   END IF;
   UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_Update;
   END LOOP;
END;
--7:編寫一個PL/SQL程式塊,對名字以‘A'或‘S'開始的所有僱員按他們的基本薪水(sal)的10%給他們加薪(對emp1表進行修改操作)
declare 
   cursor
   csr_AddSal
   is
   select * from emp1 where ENAME LIKE 'A%' OR ENAME LIKE 'S%' for update OF SAL;
   r_AddSal csr_AddSal%rowtype;
   saleInfo emp1.SAL%TYPE;
begin
   for r_AddSal in csr_AddSal loop
     dbms_output.put_line(r_AddSal.ENAME||'原來的工資:'||r_AddSal.SAL);
     saleInfo:=r_AddSal.SAL*1.1;
     UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_AddSal;
   end loop;
end;
--8:編寫一個PL/SQL程式塊,對所有的salesman增加佣金(comm)500
declare
   cursor
     csr_AddComm(p_job nvarchar2)
   is
     select * from emp1 where  JOB=p_job FOR UPDATE OF COMM;
   r_AddComm emp1%rowtype;
   commInfo emp1.comm%type;
begin
  for r_AddComm in csr_AddComm('SALESMAN') LOOP
    commInfo:=r_AddComm.COMM+500;
     UPDATE EMP1 SET COMM=commInfo where CURRENT OF csr_AddComm;
  END LOOP;
END;
--9:編寫一個PL/SQL程式塊,以提升2個資格最老的職員為MANAGER(工作時間越長,資格越老)
--(提示:可以定義一個變數作為計數器控制遊標只提取兩條資料;也可以在宣告遊標的時候把僱員中資格最老的兩個人查出來放到遊標中。)
declare
  cursor crs_testComput
  is
  select * from emp1 order by HIREDATE asc;
  --計數器
  top_two number:=2;
  r_testComput crs_testComput%rowtype;
begin
  open crs_testComput;
    FETCH crs_testComput INTO r_testComput;
     while top_two>0 loop
       dbms_output.put_line('員工姓名:'||r_testComput.ENAME||' 工作時間:'||r_testComput.HIREDATE);
       --計速器減一
       top_two:=top_two-1;
       FETCH crs_testComput INTO r_testComput;
      end loop;
   close crs_testComput;
end;
--10:編寫一個PL/SQL程式塊,對所有僱員按他們的基本薪水(sal)的20%為他們加薪,
--如果增加的薪水大於300就取消加薪(對emp1表進行修改操作,並將更新前後的資料輸出出來) 
declare
  cursor
    crs_UpadateSal
  is
    select * from emp1 for update of SAL;
    r_UpdateSal crs_UpadateSal%rowtype;
    salAdd emp1.sal%type;
    salInfo emp1.sal%type;
begin
    for r_UpdateSal in crs_UpadateSal loop
      salAdd:= r_UpdateSal.SAL*0.2;
      if salAdd>300 then
       salInfo:=r_UpdateSal.SAL;
       dbms_output.put_line(r_UpdateSal.ENAME||': 加薪失敗。'||'薪水維持在:'||r_UpdateSal.SAL);
       else 
       salInfo:=r_UpdateSal.SAL+salAdd;
       dbms_output.put_line(r_UpdateSal.ENAME||': 加薪成功.'||'薪水變為:'||salInfo);
      end if;
      update emp1 set SAL=salInfo where current of crs_UpadateSal;
    end loop;
end;
--11:將每位員工工作了多少年零多少月零多少天輸出出來  
--近似
 --CEIL(n)函式:取大於等於數值n的最小整數
 --FLOOR(n)函式:取小於等於數值n的最大整數
 --truc的用法 http://publish.it168.com/2005/1028/20051028034101.shtml
declare
 cursor
  crs_WorkDay
  is
  select ENAME,HIREDATE,trunc(months_between(sysdate,hiredate) / 12) AS SPANDYEARS,trunc(mod(months_between(sysdate,hiredate),12)) AS months,trunc(mod(mod(sysdate - hiredate,365),12)) as days
  from emp1;
 r_WorkDay crs_WorkDay%rowtype;
begin
  for  r_WorkDay in crs_WorkDay loop
  dbms_output.put_line(r_WorkDay.ENAME||'已經工作了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天');
  end loop;
end;
--12:輸入部門編號,按照下列加薪比例執行(用CASE實現,建立一個emp1表,修改emp1表的資料),並將更新前後的資料輸出出來
-- deptno raise(%)
-- 10   5%
-- 20   10%
-- 30   15%
-- 40   20%
-- 加薪比例以現有的sal為標準
--CASE expr WHEN comparison_expr THEN return_expr
--[,WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END
declare
   cursor
     crs_caseTest
     is
     select * from emp1 for update of SAL;
     r_caseTest crs_caseTest%rowtype;
     salInfo emp1.sal%type;
   begin
     for r_caseTest in crs_caseTest loop
     case 
      when r_caseTest.DEPNO=10
      THEN salInfo:=r_caseTest.SAL*1.05;
      when r_caseTest.DEPNO=20
      THEN salInfo:=r_caseTest.SAL*1.1;
      when r_caseTest.DEPNO=30
      THEN salInfo:=r_caseTest.SAL*1.15;
      when r_caseTest.DEPNO=40
      THEN salInfo:=r_caseTest.SAL*1.2;
     end case;
     update emp1 set SAL=salInfo where current of crs_caseTest;
    end loop;
end;
--13:對每位員工的薪水進行判斷,如果該員工薪水高於其所在部門的平均薪水,則將其薪水減50元,輸出更新前後的薪水,員工姓名,所在部門編號。
--AVG([distinct|all] expr) over (analytic_clause)
---作用:
--按照analytic_clause中的規則求分組平均值。
 --分析函式語法:
 --FUNCTION_NAME(<argument>,<argument>...)
 --OVER
 --(<Partition-Clause><Order-by-Clause><Windowing Clause>)
   --PARTITION子句
   --按照表達式分割槽(就是分組),如果省略了分割槽子句,則全部的結果集被看作是一個單一的組
   select * from emp1
DECLARE
   CURSOR 
   crs_testAvg
   IS
   select EMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL) OVER (PARTITION BY DEPNO ) AS DEP_AVG
   FROM EMP1 for update of SAL;
   r_testAvg crs_testAvg%rowtype;
   salInfo emp1.sal%type;
   begin
   for r_testAvg in crs_testAvg loop
   if r_testAvg.SAL>r_testAvg.DEP_AVG then
   salInfo:=r_testAvg.SAL-50;
   end if;
   update emp1 set SAL=salInfo where current of crs_testAvg;
   end loop;
end;

總結

以上就是本文關於Oracle中游標Cursor基本用法詳解的全部內容,希望對大家有所幫助,歡迎參閱:oracle資料庫匯入匯出命令解析、ORACLE SQL語句優化技術要點解析、淺談oracle中單引號轉義等,有什麼問題可以隨時留言,感謝大家!