語法結構規範
IF condition1 THEN
statements1;
[ELSIF condition2 THEN
statements2;]
[ELSE
statements3;]
END IF;
關鍵語法規則
- ELSIF 非 ELSE IF:Oracle 無
ELSE IF 關鍵字,必須寫作 ELSIF(無空格)
- END IF 必須配對:每個
IF 必須對應一個 END IF,巢狀時尤為關鍵
- THEN 不可省略:條件後必須跟
THEN
- 分號位置:
END IF; 後需分號,內部語句各自結尾分號
常見錯誤代碼分析
-- ❌ 錯誤 1:使用 ELSE IF
IF v_status = 'SHIPPED' THEN
...
ELSE IF v_status = 'ORDERED' THEN -- 語法錯誤!
...
END IF;
-- ❌ 錯誤 2:END IF 缺失/多餘
IF v_status = 'SHIPPED' THEN
UPDATE ...;
ELSIF v_status = 'ORDERED' THEN
UPDATE ...;
ELSE
UPDATE ...;
INSERT ...;
END IF;
END IF; -- 多餘的 END IF 導致 PLS-00103
-- ✅ 正確寫法
IF v_status = 'SHIPPED' THEN
UPDATE contract_item SET arrival_date = SYSDATE + 7
WHERE contract_no = p_contract_no AND item_no = p_item_no;
ELSIF v_status = 'ORDERED' THEN
UPDATE contract_item SET arrival_date = ADD_MONTHS(SYSDATE, 1)
WHERE contract_no = p_contract_no AND item_no = p_item_no;
ELSE
UPDATE contract_item SET arrival_date = ADD_MONTHS(SYSDATE, 2)
WHERE contract_no = p_contract_no AND item_no = p_item_no;
INSERT INTO orders (order_id, product_id, qty)
VALUES (seq_order.NEXTVAL, v_product_id, v_std_qty);
END IF;
實戰案例:庫存到貨日期邏輯
業務需求
| 產品狀態 | 到貨日期計算 | 額外動作 |
| SHIPPED(已發貨) | SYSDATE + 7 天 | 無 |
| ORDERED(已訂貨) | ADD_MONTHS(SYSDATE, 1) | 無 |
| 其他(無訂單/發貨) | ADD_MONTHS(SYSDATE, 2) | 創建新訂單記錄 |
完整存儲過程實現
CREATE OR REPLACE PROCEDURE calc_arrival_date (
p_contract_no IN contract_item.contract_no%TYPE,
p_item_no IN contract_item.item_no%TYPE
) AS
v_product_id inventory.product_id%TYPE;
v_product_status inventory.product_status%TYPE;
v_std_shipping_qty inventory.std_shipping_qty%TYPE;
BEGIN
-- 1. 獲取產品信息(使用子查詢關聯合約項)
SELECT i.product_id, i.product_status, i.std_shipping_qty
INTO v_product_id, v_product_status, v_std_shipping_qty
FROM inventory i
WHERE i.product_id = (
SELECT product_id
FROM contract_item
WHERE contract_no = p_contract_no AND item_no = p_item_no
);
-- 2. 條件分支處理
IF v_product_status = 'SHIPPED' THEN
UPDATE contract_item
SET arrival_date = SYSDATE + 7
WHERE contract_no = p_contract_no AND item_no = p_item_no;
ELSIF v_product_status = 'ORDERED' THEN
UPDATE contract_item
SET arrival_date = ADD_MONTHS(SYSDATE, 1)
WHERE contract_no = p_contract_no AND item_no = p_item_no;
ELSE
UPDATE contract_item
SET arrival_date = ADD_MONTHS(SYSDATE, 2)
WHERE contract_no = p_contract_no AND item_no = p_item_no;
INSERT INTO orders (order_id, product_id, qty)
VALUES (seq_order.NEXTVAL, v_product_id, v_std_shipping_qty);
END IF;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001, '合約項不存在:' || p_contract_no || '-' || p_item_no);
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END calc_arrival_date;
進階條件控制技巧
CASE 表達式替代簡單 IF
-- SQL 中使用(可在 SELECT/WHERE/ORDER BY 使用)
arrival_date = CASE product_status
WHEN 'SHIPPED' THEN SYSDATE + 7
WHEN 'ORDERED' THEN ADD_MONTHS(SYSDATE, 1)
ELSE ADD_MONTHS(SYSDATE, 2)
END;
-- PL/SQL 中使用(CASE 語句)
CASE v_product_status
WHEN 'SHIPPED' THEN v_days := 7;
WHEN 'ORDERED' THEN v_days := 30;
ELSE v_days := 60;
END CASE;
空值安全比較
-- ❌ 危險:NULL = NULL 為 UNKNOWN,不進入任何分支
IF v_status = 'SHIPPED' THEN ...
-- ✅ 安全:顯式處理 NULL
IF v_status IS NULL THEN
-- 處理未知狀態
ELSIF v_status = 'SHIPPED' THEN
...
END IF;
-- 或使用 NVL/COALESCE
IF NVL(v_status, 'UNKNOWN') = 'SHIPPED' THEN ...
短路求值利用
-- 昂貴操作放在後,利用短路特性避免不必要執行
IF cheap_condition() AND expensive_function() THEN
-- 只有 cheap_condition 為 TRUE 才執行 expensive_function
END IF;
性能與可維護性建議
- 扁平化條件:避免超過 3 層巢狀 IF,拆分為獨立函數或使用 CASE
- 常量前置:
IF 'SHIPPED' = v_status 防止誤寫賦值 IF v_status = 'SHIPPED'(編譯器會報錯)
- 業務規則外部化:複雜條件存入配置表,邏輯通用化
- 單元測試覆蓋:每個分支至少一條測試用例,含邊界值、NULL、異常值
調試技巧
-- 1. 追蹤分支執行
IF v_status = 'SHIPPED' THEN
DBMS_OUTPUT.PUT_LINE('分支:SHIPPED, 合約=' || p_contract_no);
...
END IF;
-- 2. 條件值記錄
DBMS_OUTPUT.PUT_LINE('輸入狀態:' || NVL(v_product_status, 'NULL') ||
', 合約號:' || p_contract_no);