1. 程式人生 > >Oracle遊標 CURSOR實例詳解

Oracle遊標 CURSOR實例詳解

www 取值 alt 內存 sel www. 輸出 lose info

作者:gqk


遊標 CURSOR:

一、遊標概述:

遊標(cursor)是數據庫系統在內存中開設的一個數據緩沖區,存放SQL語句的執行結果。

每個遊標都有一個名字,用戶可以用SQL語句逐一從遊標中獲取記錄,並賦給變量做進一步處理。

作用:用於定位結果集的行 和 遍歷結果集。

二、遊標分類:

  • 顯式遊標:在前述程序中用到的SELECT...INTO...查詢語句,一次只能從數據庫中提取一行數據,對於這種形式的查詢和DML操作,系統都會使用一個隱式遊標
  • 隱式遊標:但是如果要提取多行數據,就要由程序員定義一個顯式遊標,並通過與遊標有關的語句進行處理。顯式遊標對應一個返回結果為多行多列的SELECT語句。

技術分享圖片

三:顯示遊標:

處理顯示遊標需要的四個步驟:

  • 定義遊標:就是定義一個遊標的名稱,以及與其對應的SELECT語句,形式如下
      • CURSOR 遊標名稱 IS select_statement
  • 打開遊標 :open 遊標變量(不能重復打開遊標)
      • 打開遊標時,SELECT語句的查詢結果就被傳送到了遊標工作區。
  • 提取數據 :fetch 遊標變量 into 變量1,變量2,……
      • 提取操作必須在打開遊標之後進行。
  • 關閉遊標:close 遊標變量 

  顯式遊標打開後,必須顯式地關閉。遊標一旦關閉,遊標占用的資源就被釋放,遊標變成無效,必須重新打開才能使用。

--查詢輸出所有員工的編號,姓名,工資:(當執行fetch時提取數據指針下移一行)

DECLARE
       v_empid employees.employee_id%TYPE;
       v_name  employees.last_name%TYPE;
       v_salary employees.salary%TYPE;
       
       CURSOR emp_cursor IS 
       SELECT employee_id,last_name,salary 
       FROM employees 
       WHERE department_id=90;
BEGIN       
       OPEN emp_cursor;
             
       FETCH emp_cursor INTO v_empid,v_name,v_salary;--默認指向第一行
       FETCH emp_cursor INTO v_empid,v_name,v_salary;
       FETCH emp_cursor INTO v_empid,v_name,v_salary;
             
       dbms_output.put_line(‘編號:‘ || v_empid);
       dbms_output.put_line(‘姓名:‘ || v_name);
       dbms_output.put_line(‘工資:‘ || v_salary);  
       
       CLOSE emp_cursor;             
END;

四:遊標的四個屬性

  遊標變量%found:當最近一次讀記錄時成功返回,則值為TRUE
   遊標變量%notfound:同上,求反
   遊標變量%isopen:判斷遊標是否已經打開
   遊標變量%rowcount:返回已從遊標中讀取的記錄數
  

  --查詢輸出所有員工的編號,姓名,工資

  loop循環

DECLARE
       v_empid employees.employee_id%TYPE;
       v_name  employees.last_name%TYPE;
       v_salary employees.salary%TYPE;
       
       CURSOR emp_cursor IS 
       SELECT employee_id,last_name,salary 
       FROM employees;  
BEGIN       
       OPEN emp_cursor;
       
       LOOP      
           FETCH emp_cursor INTO v_empid,v_name,v_salary;
           EXIT WHEN emp_cursor%NOTFOUND;--讀取最後一次失敗後跳出循環
                       
           dbms_output.put_line(‘編號:‘ || v_empid);
           dbms_output.put_line(‘姓名:‘ || v_name);
           dbms_output.put_line(‘工資:‘ || v_salary);  
           dbms_output.put_line(‘--------------------‘); 
       END LOOP;
       
       CLOSE emp_cursor;             
END;

  while 循環

DECLARE
       v_empid employees.employee_id%TYPE;
       v_name  employees.last_name%TYPE;
       v_salary employees.salary%TYPE;
       
       CURSOR emp_cursor IS 
       SELECT employee_id,last_name,salary 
       FROM employees;  
BEGIN       
       OPEN emp_cursor;
       
       FETCH emp_cursor INTO v_empid,v_name,v_salary;
       
       WHILE emp_cursor%FOUND LOOP         
           dbms_output.put_line(‘編號:‘ || v_empid);
           dbms_output.put_line(‘姓名:‘ || v_name);
           dbms_output.put_line(‘工資:‘ || v_salary);  
           dbms_output.put_line(‘--------------------‘);
           
           FETCH emp_cursor INTO v_empid,v_name,v_salary; 
       END LOOP;
       
       CLOSE emp_cursor;             
END;

  --查詢輸出所有員工的編號,姓名,工資(PLSQL表類型)

DECLARE
       TYPE emp_table_type IS TABLE OF employees%ROWTYPE
       INDEX BY BINARY_INTEGER;

       e emp_table_type;
       
       CURSOR emp_cursor IS 
       SELECT * 
       FROM employees;  
BEGIN       
       OPEN emp_cursor;
       
       FETCH emp_cursor BULK COLLECT INTO e;
       
       FOR i IN 1..e.COUNT LOOP
           dbms_output.put_line(‘編號:‘ || e(i).employee_id);
           dbms_output.put_line(‘姓名:‘ || e(i).last_name);
           dbms_output.put_line(‘工資:‘ || e(i).salary); 
           dbms_output.put_line(‘--------------------‘);
       END LOOP;
              
       CLOSE emp_cursor;             
END;

 --查詢輸出所有員工的編號,姓名,工資(通過limit控制提取的數據量)每次提取6條數據

DECLARE
       TYPE emp_table_type IS TABLE OF employees%ROWTYPE
       INDEX BY BINARY_INTEGER;
       e emp_table_type;
       
       v_count NUMBER := 1;
       
       CURSOR emp_cursor IS 
       SELECT * 
       FROM employees;  
BEGIN       
       OPEN emp_cursor;
       
       LOOP
           dbms_output.put_line(‘第‘ || v_count || ‘次FETCH‘);
           
           FETCH emp_cursor BULK COLLECT INTO e LIMIT 6;
           
           FOR i IN 1..e.COUNT LOOP
               dbms_output.put_line(‘編號:‘ || e(i).employee_id);
               dbms_output.put_line(‘姓名:‘ || e(i).last_name);
               dbms_output.put_line(‘工資:‘ || e(i).salary); 
               dbms_output.put_line(‘--------------------‘);
           END LOOP;
           
           EXIT WHEN emp_cursor%NOTFOUND;
           
           v_count := v_count + 1;       
       END LOOP;
              
       CLOSE emp_cursor;             
END;

 --查詢輸出某個部門的員工的編號,姓名,工資(參數化遊標) 

DECLARE
       v_empid employees.employee_id%TYPE;
       v_name  employees.last_name%TYPE;
       v_salary employees.salary%TYPE;
       
       CURSOR emp_cursor(p_deptid employees.department_id%TYPE) IS 
       SELECT employee_id,last_name,salary 
       FROM employees
       WHERE department_id=p_deptid;  
BEGIN       
       OPEN emp_cursor(50);
       
       LOOP      
           FETCH emp_cursor INTO v_empid,v_name,v_salary;
           EXIT WHEN emp_cursor%NOTFOUND;
                       
           dbms_output.put_line(‘編號:‘ || v_empid || ‘,姓名:‘ || v_name || ‘,工資:‘ || v_salary);                      
       END LOOP;
       
       CLOSE emp_cursor; 
       
       dbms_output.put_line(‘----------------------‘);  
       
       OPEN emp_cursor(90);
       
       LOOP      
           FETCH emp_cursor INTO v_empid,v_name,v_salary;
           EXIT WHEN emp_cursor%NOTFOUND;
                       
           dbms_output.put_line(‘編號:‘ || v_empid || ‘,姓名:‘ || v_name || ‘,工資:‘ || v_salary);                      
       END LOOP;
              
       CLOSE emp_cursor;          
END;

  五:遊標for循環:

 PL/SQL中提供了遊標for循環語句,可以自動的執行遊標的open,fetch,close語句和循環語句的功能,當進入循環時,遊標for循環語句自動打開遊標,並提取第一行數據,提取

  提取完成後自動進入下一個提取,提取完成後自動關閉遊標:


--查詢輸出所有員工的編號,姓名,工資

DECLARE 
       CURSOR emp_cursor IS 
       SELECT *
       FROM employees;  
BEGIN       
       FOR e IN emp_cursor LOOP
           dbms_output.put_line(e.employee_id||‘,‘||e.last_name||‘,‘||e.salary);
       END LOOP;              
END;

--查詢輸出所有員工的編號,姓名,工資(帶參數)

DECLARE 
       CURSOR emp_cursor(p_deptid employees.department_id%TYPE) IS 
       SELECT *
       FROM employees
       WHERE department_id=p_deptid;  
BEGIN       
       FOR e IN emp_cursor(90) LOOP
           dbms_output.put_line(e.employee_id||‘,‘||e.last_name||‘,‘||e.salary);
       END LOOP;              
END;


--查詢輸出所有員工的編號,姓名,工資(最精簡寫法)

DECLARE 
       
BEGIN       
       FOR e IN (SELECT * FROM employees) LOOP
           dbms_output.put_line(e.employee_id||‘,‘||e.last_name||‘,‘||e.salary);
       END LOOP;              
END;

 --輸出每個部門的部門編號,部門名稱以及這個部門的下屬員工的編號,姓名,工資
/*
10 nec
100,tom,2400
101,jack,17000
20 ge
102,rose,2600
xxx,xxx,xxx
xxx,xxx,xxx
40 hsw
xxx,xxx,xxx
*/

DECLARE
       CURSOR dept_cursor IS
       SELECT * FROM departments;
       
       CURSOR emp_cursor(p_deptid NUMBER) IS
       SELECT * FROM employees
       WHERE  department_id=p_deptid;
BEGIN
       FOR d IN dept_cursor LOOP
           dbms_output.put_line(d.department_id || ‘ ‘ || d.department_name);
           
           FOR e IN emp_cursor(d.department_id) LOOP
               dbms_output.put_line(‘    ‘ || e.employee_id || ‘,‘ || e.last_name || ‘,‘ || e.salary);
           END LOOP;
       END LOOP;
END;
/*
隱式遊標:固定名稱SQL 遊標四個屬性:
SQL%FOUND:如果操作有影響的行,就為TRUE,否則為FALSE
SQL%NOTFOUND:求反
SQL%ISOPEN:在隱式遊標中,取值永遠為FALSE
SQL%ROWCOUNT:操作影響的行數 必須在事務結束之前讀取遊標屬性,只能讀取最近的一次DML操作的遊標狀態
*/ --用戶輸入一個任意的部門編號,更新這個部門的員工的工資
--如果有員工被更新,輸出“更新成功,有XX個員工被更新”
--如果沒有任何員工被更新,輸出“這個部門不存在,沒有任何員工被更新”
DECLARE
       v_deptid NUMBER := &input;
BEGIN
        UPDATE employees
        SET    salary = salary + 1
        WHERE  department_id = v_deptid;
                        
        IF SQL%FOUND THEN
           dbms_output.put_line(‘更新成功,有‘ || SQL%ROWCOUNT || ‘個員工被更新‘);
        ELSE
           dbms_output.put_line(‘這個部門不存在,沒有任何員工被更新‘);
        END IF;
        
        COMMIT;
END;

  --通過遊標去更新員工的工資,如果工資低於5000塊,則把工資改為5000

DECLARE
       CURSOR emp_cursor IS
       SELECT * FROM new_emp FOR UPDATE;
BEGIN
       FOR e IN emp_cursor LOOP
           IF e.salary<5000 THEN
              UPDATE new_emp SET salary=5000 WHERE CURRENT OF emp_cursor;
           END IF;
       END LOOP;
END;

  --通過遊標刪除工資=5000的員工

DECLARE
       CURSOR emp_cursor IS
       SELECT * FROM new_emp FOR UPDATE;
BEGIN
       FOR e IN emp_cursor LOOP
           IF e.salary=5000 THEN
              DELETE FROM new_emp WHERE CURRENT OF emp_cursor;
           END IF;
       END LOOP;
END;

 --遊標變量(動態遊標)
--用戶輸入一個字母,輸入E,輸出所有員工姓名,如果輸入D,輸出所有部門的名稱

DECLARE
       v_cmd CHAR(1) := ‘&input‘;
       v_name VARCHAR2(50);
       
       --聲明自定義的遊標變量類型
       --TYPE c_type IS REF CURSOR;
       --聲明遊標變量
       --c c_type;
       
       c SYS_REFCURSOR;--代替TYPE c_type IS REF CURSOR
BEGIN
       IF v_cmd=‘E‘ THEN
          dbms_output.put_line(‘員工姓名‘); 
          OPEN c FOR SELECT last_name FROM employees;
       ELSIF v_cmd=‘D‘ THEN
          dbms_output.put_line(‘部門名稱‘); 
          OPEN c FOR SELECT department_name FROM departments;
       ELSE
          dbms_output.put_line(‘輸入無效‘); 
          RETURN;
       END IF;
       
       LOOP
           FETCH c INTO v_name;
           EXIT WHEN c%NOTFOUND;
           dbms_output.put_line(v_name);
       END LOOP;
       
       CLOSE c;
END;

Oracle遊標 CURSOR實例詳解