首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 顯式游標完整指南:宣告、開啟、擷取、關閉與參數化實戰

     編輯:Oracle 資料庫基礎

顯式游標 vs 隱式游標

特性顯式游標隱式游標
控制權程式設計師完全控制PL/SQL 引擎自動管理
適用場景多列查詢、需逐列處理、複雜邏輯單列查詢、DML 操作
生命週期OPEN → FETCH → CLOSE自動開啟關閉
參數支援支援參數化不支援
屬性存取cursor_name%ROWCOUNT 等SQL%ROWCOUNT 等

語法結構

CURSOR cursor_name [(parameter_list)] [RETURN return_type] IS
  select_statement;

參數化游標——核心優勢

參數化游標允許同一游標定義以不同參數重複開啟,避免重複撰寫相似查詢。

完整實戰範例

SET SERVEROUTPUT ON

DECLARE
  -- 1. 宣告參數化游標
  CURSOR emp_cur(p_dept_id IN NUMBER) IS
    SELECT employee_id, first_name, last_name, salary
    FROM employees
    WHERE department_id = p_dept_id;

  -- 2. 定義接收變數(使用 %ROWTYPE 自適應)
  l_emp employees%ROWTYPE;
BEGIN
  -- 3. 第一次開啟:部門 30
  DBMS_OUTPUT.PUT_LINE('=== 部門 30 員工 ===');
  OPEN emp_cur(30);
  LOOP
    FETCH emp_cur INTO l_emp;
    EXIT WHEN emp_cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('ID: ' || l_emp.employee_id || 
                         ' Name: ' || l_emp.first_name || ' ' || l_emp.last_name ||
                         ' Salary: ' || l_emp.salary);
  END LOOP;
  CLOSE emp_cur;

  -- 4. 第二次開啟:部門 90(重用同一游標定義)
  DBMS_OUTPUT.PUT_LINE('=== 部門 90 員工 ===');
  OPEN emp_cur(90);
  LOOP
    FETCH emp_cur INTO l_emp;
    EXIT WHEN emp_cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('ID: ' || l_emp.employee_id || 
                         ' Name: ' || l_emp.first_name || ' ' || l_emp.last_name);
  END LOOP;
  CLOSE emp_cur;
END;
/

進階模式

1. 游標 FOR 迴圈(自動 OPEN/FETCH/CLOSE)

FOR emp_rec IN emp_cur(30) LOOP
  DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id || ': ' || emp_rec.first_name);
END LOOP;
-- 等同於隱式游標,但保有參數化優勢

2. 帶 RETURN 型別的強型別游標

CREATE TYPE emp_rec_type AS OBJECT (
  emp_id NUMBER, emp_name VARCHAR2(100), dept_name VARCHAR2(50)
);
CREATE TYPE emp_tab_type AS TABLE OF emp_rec_type;

CURSOR emp_cur(p_dept_id NUMBER) RETURN emp_rec_type IS
  SELECT emp_obj(e.employee_id, e.first_name||' '||e.last_name, d.department_name)
  FROM employees e JOIN departments d ON e.department_id = d.department_id
  WHERE e.department_id = p_dept_id;

3. 多參數游標

CURSOR emp_cur(p_dept_id NUMBER, p_min_sal NUMBER, p_max_sal NUMBER) IS
  SELECT * FROM employees
  WHERE department_id = p_dept_id
  AND salary BETWEEN p_min_sal AND p_max_sal;

4. 預設參數值

CURSOR emp_cur(p_dept_id NUMBER := 10) IS ...  -- 未傳參數時預設 10

游標屬性完整對照

屬性意義範例用法
%FOUND最近 FETCH 找到資料IF emp_cur%FOUND THEN ...
%NOTFOUND最近 FETCH 未找到資料EXIT WHEN emp_cur%NOTFOUND;
%ROWCOUNT目前已擷取列數DBMS_OUTPUT.PUT_LINE('Fetched ' || emp_cur%ROWCOUNT);
%ISOPEN游標是否開啟中IF emp_cur%ISOPEN THEN CLOSE emp_cur; END IF;

常見錯誤與除錯

錯誤原因解決
ORA-06511: cursor already open重複 OPEN 未 CLOSE檢查邏輯路徑,確保每個 OPEN 有對應 CLOSE;或用 FOR 迴圈
ORA-01001: invalid cursor操作未開啟或已關閉的游標檢查 %ISOPEN;避免在 CLOSE 後 FETCH
ORA-06504: rowtype mismatchFETCH 目標變數與 SELECT 列不符使用 %ROWTYPE 或明確宣告對應 RECORD 型別
無限迴圈忘記 FETCH 或 EXIT 條件確保迴圈內有 FETCH 且有 EXIT WHEN %NOTFOUND

效能最佳實務

  1. 使用 BULK COLLECT + LIMIT:大量資料分批提取,減少 Context Switch
    OPEN emp_cur(30);
    LOOP
      FETCH emp_cur BULK COLLECT INTO l_emp_tab LIMIT 1000;
      EXIT WHEN l_emp_tab.COUNT = 0;
      FORALL i IN 1..l_emp_tab.COUNT
        UPDATE ...;  -- 批次 DML
    END LOOP;
    CLOSE emp_cur;
  2. 避免在迴圈內 COMMIT**:除非業務要求,否則批次處理完再 COMMIT
  3. 參數化游標重用**:同一邏輯不同參數,優於動態 SQL
  4. 關閉游標**:例外處理區塊務必確保 CLOSE

完整範本:安全的參數化游標處理

DECLARE
  CURSOR c_data(p_param NUMBER) IS SELECT * FROM big_table WHERE id > p_param;
  TYPE t_tab IS TABLE OF big_table%ROWTYPE;
  l_tab t_tab;
BEGIN
  OPEN c_data(1000);
  <>
  LOOP
    BEGIN
      FETCH c_data BULK COLLECT INTO l_tab LIMIT 500;
      EXIT WHEN l_tab.COUNT = 0;
      -- 處理邏輯
      FORALL i IN 1..l_tab.COUNT
        INSERT INTO target_table VALUES l_tab(i);
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
        RAISE;
    END;
  END LOOP fetch_loop;
  CLOSE c_data;
EXCEPTION
  WHEN OTHERS THEN
    IF c_data%ISOPEN THEN CLOSE c_data; END IF;
    RAISE;
END;
/
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved