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

Oracle 迴圈語句完整指南:LOOP、WHILE、FOR 三大類型語法與效能實戰

     編輯:Oracle 資料庫基礎

三大迴圈類型對照表

迴圈類型適用場景終止條件效能特性
基本 LOOP至少執行一次、複雜終止邏輯EXIT WHEN / EXIT手動控制、靈活
WHILE LOOP先檢查條件再執行、可能零次條件為 FALSE條件前置檢查
FOR LOOP (數值)已知迭代次數、計數器需求達到上限自動管理計數器、不可修改
FOR LOOP (游標)查詢結果集遍歷游標耗盡自動 OPEN/FETCH/CLOSE

1. 基本 LOOP

語法

LOOP
  statements;
  EXIT [WHEN condition];
END LOOP;

範例:累加 1 到 5

DECLARE
  v_sum NUMBER := 0;
  v_i NUMBER := 1;
BEGIN
  LOOP
    v_sum := v_sum + v_i;
    v_i := v_i + 1;
    EXIT WHEN v_i > 5;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('Sum 1-5 = ' || v_sum);  -- 15
END;
/

範例:資料驗證迴圈(至少執行一次)

DECLARE
  v_input VARCHAR2(100);
  v_valid BOOLEAN := FALSE;
BEGIN
  LOOP
    v_input := get_user_input();  -- 假設函數
    IF validate(v_input) THEN
      v_valid := TRUE;
      EXIT;
    ELSE
      DBMS_OUTPUT.PUT_LINE('輸入無效,請重試');
    END IF;
  END LOOP;
  process(v_input);
END;
/

2. WHILE LOOP

語法

WHILE condition LOOP
  statements;
END LOOP;

範例:相同邏輯用 WHILE

DECLARE
  v_sum NUMBER := 0;
  v_i NUMBER := 1;
BEGIN
  WHILE v_i <= 5 LOOP
    v_sum := v_sum + v_i;
    v_i := v_i + 1;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('Sum 1-5 = ' || v_sum);
END;
/

範例:游標式 WHILE (手動控制)

DECLARE
  CURSOR c_emp IS SELECT * FROM employees WHERE department_id = 30;
  v_emp employees%ROWTYPE;
BEGIN
  OPEN c_emp;
  FETCH c_emp INTO v_emp;
  WHILE c_emp%FOUND LOOP
    process_emp(v_emp);
    FETCH c_emp INTO v_emp;
  END LOOP;
  CLOSE c_emp;
END;
/
-- 等同於 FOR 迴圈但手動控制,適合複雜邏輯

3. FOR LOOP (數值範圍)

語法

FOR counter IN [REVERSE] lower_bound .. upper_bound LOOP
  statements;
END LOOP;

關鍵特性

  • counter 隱式宣告為 INTEGER,唯讀,不可在迴圈中修改
  • 範圍在迴圈開始前評估一次,不受迴圈內變更影響
  • REVERSE 遞減迭代 (upper_bound 到 lower_bound)
  • 下限 > 上限時,迴圈零次執行 (不報錯)

範例:正向與反向

BEGIN
  DBMS_OUTPUT.PUT_LINE('正向:');
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE(i);
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('反向:');
  FOR i IN REVERSE 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE(i);
  END LOOP;
END;
/
-- 輸出: 1,2,3 然後 3,2,1

範例:動態邊界

DECLARE
  v_start NUMBER := 1;
  v_end NUMBER := 5;
BEGIN
  FOR i IN v_start..v_end LOOP
    DBMS_OUTPUT.PUT_LINE(i);
  END LOOP;
END;
/

4. FOR LOOP (游標) - 隱式游標

語法

FOR record_variable IN (SELECT_statement) LOOP
  statements;
END LOOP;

-- 或使用已宣告游標
FOR record_variable IN cursor_name[(parameters)] LOOP
  statements;
END LOOP;

範例:查詢遍歷

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;
/

範例:參數化游標 FOR 迴圈

DECLARE
  CURSOR c_emp(p_dept_id NUMBER) IS
    SELECT employee_id, first_name, salary
    FROM employees WHERE department_id = p_dept_id;
BEGIN
  FOR emp_rec IN c_emp(30) LOOP
    DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id || ': ' || emp_rec.first_name || ' - ' || emp_rec.salary);
  END LOOP;
  
  -- 同一游標不同參數
  FOR emp_rec IN c_emp(90) LOOP
    DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id || ': ' || emp_rec.first_name);
  END LOOP;
END;
/

進階:%ROWTYPE 變數搭配

DECLARE
  CURSOR c_emp IS SELECT * FROM employees WHERE department_id = 30;
  v_emp c_emp%ROWTYPE;  -- 使用游標型別
BEGIN
  FOR v_emp IN c_emp LOOP
    DBMS_OUTPUT.PUT_LINE(v_emp.employee_id);
  END LOOP;
END;
/

迴圈控制語句

語句作用適用迴圈
EXIT立即離開當前迴圈所有
EXIT WHEN condition條件成立時離開所有
CONTINUE跳過本次迭代剩餘語句,進入下一次11g+ 所有
CONTINUE WHEN condition條件成立時跳過11g+ 所有
GOTO label跳轉到標籤 (不推薦)所有

CONTINUE 範例 (11g+)

BEGIN
  FOR i IN 1..10 LOOP
    CONTINUE WHEN MOD(i, 2) = 0;  -- 跳過偶數
    DBMS_OUTPUT.PUT_LINE('奇數: ' || i);
  END LOOP;
END;
/

巢狀迴圈與標籤

BEGIN
  <>
  FOR i IN 1..3 LOOP
    <>
    FOR j IN 1..3 LOOP
      IF i = 2 AND j = 2 THEN
        EXIT outer_loop;  -- 直接跳出外層
      END IF;
      DBMS_OUTPUT.PUT_LINE('i=' || i || ' j=' || j);
    END LOOP inner_loop;
  END LOOP outer_loop;
END;
/

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

-- 方案 A:隱式 FOR 迴圈 (逐列 Context Switch)
DECLARE
  v_start NUMBER;
BEGIN
  v_start := DBMS_UTILITY.GET_TIME;
  FOR r IN (SELECT * FROM large_table) LOOP
    NULL;  -- 處理邏輯
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('FOR LOOP: ' || (DBMS_UTILITY.GET_TIME - v_start) || ' cs');
END;
/

-- 方案 B:顯式游標 + BULK COLLECT (批次 1000 列)
DECLARE
  CURSOR c IS SELECT * FROM large_table;
  TYPE t_tab IS TABLE OF large_table%ROWTYPE;
  l_tab t_tab;
  v_start NUMBER;
BEGIN
  v_start := DBMS_UTILITY.GET_TIME;
  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
      NULL;  -- 批次處理
  END LOOP;
  CLOSE c;
  DBMS_OUTPUT.PUT_LINE('BULK COLLECT: ' || (DBMS_UTILITY.GET_TIME - v_start) || ' cs');
END;
/
-- 典型效能提升: 10x - 100x

最佳實務檢核表

  • [ ] 已知次數迭代 → FOR 數值迴圈
  • [ ] 查詢結果遍歷 → FOR 游標迴圈 (簡單) 或 顯式游標 + BULK COLLECT (大量)
  • [ ] 至少一次、複雜終止 → 基本 LOOP + EXIT WHEN
  • [ ] 先檢查條件 → WHILE LOOP
  • [ ] 大量資料 DML → 必須用 BULK COLLECT + FORALL
  • [ ] 避免在迴圈中 COMMIT (除非業務要求)
  • [ ] 避免修改 FOR 迴圈計數器 (編譯錯誤)
  • [ ] 巢狀迴圈使用標籤管理 EXIT/CONTINUE 範圍
  • [ ] 11z+ 善用 CONTINUE WHEN 簡化邏輯

常見錯誤

錯誤原因修正
PLS-00363: expression 'I' cannot be used as an assignment target修改 FOR 迴圈計數器改用基本 LOOP 或 WHILE 自行管理變數
無限迴圈EXIT 條件永不滿足 / 忘記 FETCH檢查邏輯、加入安全計數器限制最大迭代
ORA-01001: invalid cursor迴圈中 COMMIT 導致游標失效移出 COMMIT 或用 FOR UPDATE + WHERE CURRENT OF
FOR 迴圈零次執行無提示下限 > 上限預期需至少一次時改用基本 LOOP
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved