PL/SQL 提供豐富的流程控制結構,包含條件判斷 (IF/CASE)、迴圈 (LOOP/WHILE/FOR)、無條件跳轉 (GOTO) 與空操作 (NULL)。本文系統整理語法規則、巢狀結構、常見陷阱與實戰範例,作為開發時的快速查閱手冊。
DECLARE
n INTEGER; -- 單一變數
mm INTEGER := 100; -- 宣告並初始化
BEGIN
n := 12; -- 賦值運算子為 :=
-- ...
END;
/
-- 單一條件
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 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;
CASE 有兩種形式:簡單 CASE (比較單一運算式) 與 搜尋 CASE (評估多個布林條件)。
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;
/
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;
/
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;
/
DECLARE
x NUMBER := 1;
BEGIN
WHILE x <= 10 LOOP
DBMS_OUTPUT.PUT_LINE('X = ' || x);
x := x + 1;
END LOOP;
END;
/
[<<label>>]
FOR counter IN [REVERSE] lower_bound .. upper_bound LOOP
statements;
END LOOP [label];
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;
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;
/
GOTO label_name;
-- ...
<<label_name>>
statements;
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;
/
NULL; 是一個合法的可執行敘述,不執行任何動作。主要用途:
IF v_salary <= 3000 THEN
v_raise := 0.10;
DBMS_OUTPUT.PUT_LINE('調薪 10%');
ELSE
NULL; -- 明確表示薪資 > 3000 時不調薪
END IF;
IF v_num IS NULL THEN
GOTO print_label;
END IF;
-- ... 其他邏輯
<<print_label>>
NULL; -- 標籤後必須跟隨可執行敘述,NULL 完美符合
END;
EXIT WHEN 控制流程。EXCEPTION WHEN OTHERS THEN ... 避免未處理例外中斷批次作業。