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

Oracle PL/SQL 流程控制語句完整參考:條件、迴圈、標籤與 NULL

     編輯:Oracle 教學

Oracle PL/SQL 流程控制語句完整參考

PL/SQL 提供豐富的流程控制結構,包含條件判斷 (IF/CASE)、迴圈 (LOOP/WHILE/FOR)、無條件跳轉 (GOTO) 與空操作 (NULL)。本文系統整理語法規則、巢狀結構、常見陷阱與實戰範例,作為開發時的快速查閱手冊。

變數宣告與賦值基礎

DECLARE
    n  INTEGER;          -- 單一變數
    mm INTEGER := 100;   -- 宣告並初始化
BEGIN
    n := 12;             -- 賦值運算子為 :=
    -- ...
END;
/

1. 條件語句:IF-THEN-ELSIF-ELSE

三種基本形式

-- 單一條件
IF condition THEN
    statements;
END IF;

-- 雙向分支
IF condition THEN
    statements;
ELSE
    statements;
END IF;

-- 多重條件 (注意: ELSIF 非 ELSEIF)
IF condition1 THEN
    statements;
ELSIF condition2 THEN
    statements;
ELSIF condition3 THEN
    statements;
ELSE
    statements;
END IF;

實戰範例:薪資等級判斷

DECLARE
    v_empno    employees.employee_id%TYPE := &empno;
    v_salary   employees.salary%TYPE;
    v_comment  VARCHAR2(50);
BEGIN
    SELECT salary INTO v_salary FROM employees WHERE employee_id = v_empno;

    IF v_salary < 1500 THEN
        v_comment := '太少了,加點吧!';
    ELSIF v_salary < 3000 THEN
        v_comment := '還可以,繼續努力!';
    ELSE
        v_comment := '薪資不錯喔!';
    END IF;

    DBMS_OUTPUT.PUT_LINE(v_comment);

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('查無此員工編號!');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE(SQLCODE || ' - ' || SQLERRM);
END;
/

巢狀 IF 範例

IF v_sal < 10000 THEN
    DBMS_OUTPUT.PUT_LINE('低薪區間');
ELSE
    IF v_sal BETWEEN 10000 AND 20000 THEN
        DBMS_OUTPUT.PUT_LINE('中薪區間');
    ELSE
        DBMS_OUTPUT.PUT_LINE('高薪區間');
    END IF;
END IF;

2. CASE 表達式與陳述式

CASE 有兩種形式:簡單 CASE (比較單一運算式) 與 搜尋 CASE (評估多個布林條件)。

簡單 CASE (類似 switch)

DECLARE
    v_grade     CHAR(1) := UPPER('&p_grade');
    v_appraisal VARCHAR2(20);
BEGIN
    v_appraisal := CASE v_grade
                        WHEN 'A' THEN 'Excellent'
                        WHEN 'B' THEN 'Very Good'
                        WHEN 'C' THEN 'Good'
                        ELSE 'No such grade'
                     END;
    DBMS_OUTPUT.PUT_LINE('Grade: ' || v_grade || ' Appraisal: ' || v_appraisal);
END;
/

搜尋 CASE (支援複雜條件)

DECLARE
    v_job_id    employees.job_id%TYPE;
    v_salary    employees.salary%TYPE;
    v_raise     NUMBER(3,2);
BEGIN
    SELECT job_id, salary INTO v_job_id, v_salary FROM employees WHERE employee_id = &emp_id;

    CASE
        WHEN v_job_id = 'PU_CLERK' THEN
            v_raise := CASE WHEN v_salary < 3000 THEN 0.08 ELSE 0.07 END;
        WHEN v_job_id = 'SH_CLERK' THEN
            v_raise := CASE WHEN v_salary < 4000 THEN 0.06 ELSE 0.05 END;
        WHEN v_job_id = 'ST_CLERK' THEN
            v_raise := CASE WHEN v_salary < 3500 THEN 0.04 ELSE 0.03 END;
        ELSE
            DBMS_OUTPUT.PUT_LINE('該職位不調薪: ' || v_job_id);
            v_raise := 0;
    END CASE;

    DBMS_OUTPUT.PUT_LINE('調薪幅度: ' || v_raise);
END;
/

3. 迴圈結構

3.1 基本 LOOP + EXIT WHEN

DECLARE
    v_cnt NUMBER := 0;
BEGIN
    LOOP
        v_cnt := v_cnt + 1;
        DBMS_OUTPUT.PUT_LINE('Counter: ' || v_cnt);
        EXIT WHEN v_cnt = 10;  -- 條件滿足時跳出
    END LOOP;
END;
/

3.2 WHILE 迴圈

DECLARE
    x NUMBER := 1;
BEGIN
    WHILE x <= 10 LOOP
        DBMS_OUTPUT.PUT_LINE('X = ' || x);
        x := x + 1;
    END LOOP;
END;
/

3.3 FOR 數值迴圈 (最常用)

[<<label>>]
FOR counter IN [REVERSE] lower_bound .. upper_bound LOOP
    statements;
END LOOP [label];
  • 計數器自動宣告為 INTEGER,不可指派值。
  • REVERSE 時下限必須小於上限 (如 REVERSE 1 .. 10 實際執行 10 遞減至 1)。
  • 可使用 EXIT 或 EXIT WHEN 提前結束。

範例:正向與反向迴圈

BEGIN
    FOR i IN 1 .. 5 LOOP
        DBMS_OUTPUT.PUT_LINE('Forward: ' || i);
    END LOOP;

    FOR i IN REVERSE 1 .. 5 LOOP
        DBMS_OUTPUT.PUT_LINE('Reverse: ' || i);
    END LOOP;
END;
/

範例:迴圈變數作用域與外部同名變數

CREATE TABLE temp_table (num_col NUMBER);

DECLARE
    v_counter NUMBER := 10;
BEGIN
    INSERT INTO temp_table VALUES (v_counter);  -- 插入 10

    FOR v_counter IN 20 .. 25 LOOP   -- 此處 v_counter 為迴區域變數,遮蔽外層
        INSERT INTO temp_table VALUES (v_counter);  -- 插入 20~25
    END LOOP;

    INSERT INTO temp_table VALUES (v_counter);  -- 插入 10 (外層變數未變)

    FOR v_counter IN REVERSE 20 .. 25 LOOP
        INSERT INTO temp_table VALUES (v_counter);  -- 插入 25~20
    END LOOP;
END;
/
DROP TABLE temp_table;

範例:遍歷集合 (VARRAY/Nested Table)

DECLARE
    TYPE jobids_varray IS VARRAY(12) OF VARCHAR2(10);
    v_jobids jobids_varray := jobids_varray('FI_ACCOUNT','FI_MGR','ST_CLERK','ST_MAN');
    v_howmany NUMBER;
BEGIN
    FOR i IN v_jobids.FIRST .. v_jobids.LAST LOOP
        SELECT COUNT(*) INTO v_howmany FROM employees WHERE job_id = v_jobids(i);
        DBMS_OUTPUT.PUT_LINE('Job ' || v_jobids(i) || ' 有 ' || v_howmany || ' 人');
    END LOOP;
END;
/

範例:巢狀迴圈尋找質數

DECLARE
    v_m NUMBER := 101;
    v_i NUMBER;
    v_n NUMBER := 0;
BEGIN
    WHILE v_m < 110 LOOP
        v_i := 2;
        LOOP
            EXIT WHEN v_i > v_m - 1;
            IF MOD(v_m, v_i) = 0 THEN
                v_i := 0;  -- 標記非質數
                EXIT;
            END IF;
            v_i := v_i + 1;
        END LOOP;

        IF v_i > 0 THEN
            v_n := v_n + 1;
            DBMS_OUTPUT.PUT_LINE('第 ' || v_n || ' 個質數: ' || v_m);
        END IF;
        v_m := v_m + 2;  -- 只檢查奇數
    END LOOP;
END;
/

4. 標籤與 GOTO (慎用)

GOTO label_name;
-- ...
<<label_name>>
statements;

限制規則 (編譯期檢查)

  • 不可跳入非執行敘述 (如宣告區)。
  • 不可跳入子區塊 (IF/LOOP/CASE 內部)。
  • 不可從例外處理區跳回執行區。
  • 不可在 IF/CASE 的不同分支間跳轉。

範例:以 GOTO 替代布林旗標跳出多層迴圈

DECLARE
    v_counter NUMBER := 1;
BEGIN
    LOOP
        DBMS_OUTPUT.PUT_LINE('Counter: ' || v_counter);
        v_counter := v_counter + 1;
        IF v_counter > 10 THEN
            GOTO exit_loop;
        END IF;
    END LOOP;
    <<exit_loop>>
    DBMS_OUTPUT.PUT_LINE('Final: ' || v_counter);
END;
/

5. NULL 陳述式 (空操作)

NULL; 是一個合法的可執行敘述,不執行任何動作。主要用途:

  • 作為佔位符,保持語法結構完整 (如 IF 分支暫無邏輯)。
  • 明確表達「此處故意不處理」,提升可讀性。
  • 搭配 GOTO 作為標籤目標。

範例 1:IF 分支無操作

IF v_salary <= 3000 THEN
    v_raise := 0.10;
    DBMS_OUTPUT.PUT_LINE('調薪 10%');
ELSE
    NULL;  -- 明確表示薪資 > 3000 時不調薪
END IF;

範例 2:GOTO 目標標籤

IF v_num IS NULL THEN
    GOTO print_label;
END IF;
-- ... 其他邏輯
<<print_label>>
NULL;  -- 標籤後必須跟隨可執行敘述,NULL 完美符合
END;

總結與風格建議

  1. 優先使用 FOR 迴圈 遍歷已知範圍,程式碼最簡潔。
  2. 複雜條件用搜尋 CASE 取代多層 IF-ELSIF,可讀性更佳。
  3. 避免 GOTO,改用布林旗標、子程式或 EXIT WHEN 控制流程。
  4. 善用 NULL 明確表達「無操作」意圖,而非留空分支。
  5. 例外處理:每個區塊建議加上 EXCEPTION WHEN OTHERS THEN ... 避免未處理例外中斷批次作業。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved