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

Oracle 隱式游標完全指南:SQL 隱式游標與 FOR 迴圈游標實戰詳解

     編輯:Oracle 資料庫基礎

隱式游標兩大類型

類型觸發時機存取前綴典型用途
DML 隱式游標INSERT/UPDATE/DELETE/MERGESQL%檢查影響列數、錯誤處理
FOR 迴圈隱式游標FOR rec IN (SELECT ...) LOOP無(直接用迴圈變數)簡單查詢遍歷

DML 隱式游標屬性

屬性說明資料型別
SQL%FOUND最近 DML 影響至少 1 列BOOLEAN
SQL%NOTFOUND最近 DML 未影響任何列BOOLEAN
SQL%ROWCOUNT最近 DML 影響列數NUMBER
SQL%ISOPEN永遠為 FALSE(隱式游標自動關閉)BOOLEAN

實戰範例 1:UPDATE 後檢查影響列數

SET SERVEROUTPUT ON

DECLARE
BEGIN
  -- 更新所有部門名稱(示範用,實務需 WHERE 條件)
  UPDATE departments 
  SET department_name = department_name || '_UPDATED';

  -- 檢查影響列數
  DBMS_OUTPUT.PUT_LINE('更新列數: ' || SQL%ROWCOUNT);

  -- 條件判斷範例
  IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('有資料被更新');
  ELSE
    DBMS_OUTPUT.PUT_LINE('無資料符合條件');
  END IF;

  -- 故意不符合條件
  UPDATE departments 
  SET department_name = 'TEST' 
  WHERE 1 = 2;  -- 永不成立

  IF SQL%NOTFOUND THEN
    DBMS_OUTPUT.PUT_LINE('第二次更新無影響列數: ' || SQL%ROWCOUNT);
  END IF;
END;
/

實戰範例 2:MERGE 操作後檢查

DECLARE
  v_updated NUMBER;
  v_inserted NUMBER;
BEGIN
  MERGE INTO target t
  USING source s ON (t.id = s.id)
  WHEN MATCHED THEN UPDATE SET t.val = s.val
  WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val);

  -- MERGE 後 SQL%ROWCOUNT = 更新列數 + 插入列數
  DBMS_OUTPUT.PUT_LINE('MERGE 總影響列數: ' || SQL%ROWCOUNT);
  
  -- 若需分開統計,需改用 SQL%BULK_ROWCOUNT(FORALL)或拆分語句
END;
/

FOR 迴圈隱式游標(Cursor FOR Loop)

語法糖:自動 OPEN、FETCH、CLOSE,最適合唯讀遍歷。

基本語法

FOR record_variable IN (SELECT_statement) LOOP
  -- 直接使用 record_variable.column_name
END LOOP;

範例:遍歷部門表

SET SERVEROUTPUT ON

BEGIN
  FOR dept_rec IN (SELECT department_id, department_name FROM departments) LOOP
    DBMS_OUTPUT.PUT_LINE(dept_rec.department_id || ': ' || dept_rec.department_name);
  END LOOP;
  -- 迴圈結束自動關閉游標
END;
/

進階:參數化查詢(需包裝在函數/程序中)

CREATE OR REPLACE PROCEDURE list_dept_emps(p_dept_id NUMBER) IS
BEGIN
  FOR emp_rec IN (SELECT employee_id, first_name, salary 
                  FROM employees WHERE department_id = p_dept_id) LOOP
    DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id || ': ' || emp_rec.first_name || ' - ' || emp_rec.salary);
  END LOOP;
END;
/
-- 呼叫
EXEC list_dept_emps(30);

隱式 vs 顯式:選擇決策樹

開始
  │
  ├─ 需要 DML (INSERT/UPDATE/DELETE) → 使用隱式游標 (SQL%ROWCOUNT 等)
  │
  ├─ 單純查詢遍歷,邏輯簡單 → 使用 FOR 迴圈隱式游標
  │
  ├─ 查詢需參數化、重複開啟、複雜控制流 → 使用顯式游標
  │
  ├─ 大量資料批次處理 → 顯式游標 + BULK COLLECT + FORALL
  │
  └─ 動態 SQL (執行時決定查詢) → 顯式游標變數 (SYS_REFCURSOR) + OPEN FOR

常見陷阱

陷阱說明正確做法
在 FOR 迴圈中修改迴圈變數FOR rec IN ... LOOP rec.sal := 100; END LOOP; 編譯錯誤(唯讀)改用顯式游標 FETCH INTO 變數再修改
FOR 迴圈中執行 COMMIT導致游標失效(ORA-01002)避免迴圈中 COMMIT;或用 FOR UPDATE + WHERE CURRENT OF
SQL%ROWCOUNT 在 SELECT 後使用SELECT 不設定 SQL%ROWCOUNT僅在 DML 後使用;查詢用顯式游標 %ROWCOUNT
巢狀 FOR 迴圈變數衝突外層內層同名變數使用區塊標籤 <> FOR ... 區分

效能對比:隱式 FOR 迴圈 vs 顯式 BULK COLLECT

-- 隱式 FOR 迴圈:逐列 Context Switch
FOR r IN (SELECT * FROM big_table) LOOP
  process(r);
END LOOP;

-- 顯式 BULK COLLECT:批次 1000 列
OPEN c;
LOOP
  FETCH c BULK COLLECT INTO l_tab LIMIT 1000;
  EXIT WHEN l_tab.COUNT = 0;
  FORALL i IN 1..l_tab.COUNT
    process(l_tab(i));
END LOOP;
CLOSE c;
-- 效能提升 10x-100x(視處理邏輯)

總結:隱式游標最佳實務

  • ✅ DML 後立即檢查 SQL%ROWCOUNT/%FOUND
  • ✅ 簡單唯讀遍歷用 FOR rec IN (SELECT ...)
  • ✅ 避免在 FOR 迴圈中 COMMIT 或修改迴圈變數
  • ✅ 大量資料、複雜邏輯、需參數化 → 改用顯式游標
  • ✅ 例外處理區塊中檢查 SQL%ROWCOUNT 判斷 DML 是否執行
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved