首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle PL/SQL IF-ELSE 條件控制完全指南:語法陷阱、最佳實踐與實戰案例

     編輯:Oracle 資料庫相關知識

語法結構規範

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;

性能與可維護性建議

  1. 扁平化條件:避免超過 3 層巢狀 IF,拆分為獨立函數或使用 CASE
  2. 常量前置:IF 'SHIPPED' = v_status 防止誤寫賦值 IF v_status = 'SHIPPED'(編譯器會報錯)
  3. 業務規則外部化:複雜條件存入配置表,邏輯通用化
  4. 單元測試覆蓋:每個分支至少一條測試用例,含邊界值、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);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved