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

Oracle 觸發器實現庫存出入帳:從業務邏輯到代碼落地的完整實踐

     編輯:Oracle 資料庫相關知識

業務背景與技術選型

某 ERP 系統需維護「入庫單(income)」、「出庫單(outlay)」、「庫存餘額表(balance)」。每當入/出庫單增刪改時,自動同步更新餘額表。對比「應用層代碼控制」與「資料庫觸發器」兩種方案:

維度應用層控制資料庫觸發器
資料庫訪問次數多次往返單次事務內完成
可移植性高(業務邏輯集中)低(綁定特定資料庫)
維護複雜度高(易漏改、事務邊界難控)低(集中在資料庫層)
資料一致性風險高(分散式事務、並發衝突)低(ACID 保證)

考慮到庫存業務對一致性極度敏感,且系統長期鎖定 Oracle,最終採用觸發器方案。

表結構設計

CREATE TABLE income (
    id          NUMBER PRIMARY KEY,
    pay_amount  NUMBER NOT NULL  -- 入庫金額
);

CREATE TABLE outlay (
    id             NUMBER PRIMARY KEY,
    outlay_amount  NUMBER NOT NULL  -- 出庫金額
);

CREATE TABLE balance (
    id      NUMBER PRIMARY KEY,
    balance NUMBER DEFAULT 0  -- 庫存餘額
);

入庫觸發器:TRI_ADD

CREATE OR REPLACE TRIGGER tri_add
AFTER INSERT OR DELETE OR UPDATE ON income
FOR EACH ROW
DECLARE
    v_delta NUMBER;
BEGIN
    IF DELETING THEN
        v_delta := -NVL(:OLD.pay_amount, 0);
    ELSIF UPDATING THEN
        v_delta := NVL(:NEW.pay_amount, 0) - NVL(:OLD.pay_amount, 0);
    ELSE  -- INSERTING
        v_delta := NVL(:NEW.pay_amount, 0);
    END IF;

    UPDATE balance
    SET balance = NVL(balance, 0) + v_delta
    WHERE id = :NEW.id;  -- 假設單一餘額記錄,實際可依業務擴展
END;
/

出庫觸發器:TRI_CUT

CREATE OR REPLACE TRIGGER tri_cut
AFTER INSERT OR DELETE OR UPDATE ON outlay
FOR EACH ROW
DECLARE
    v_delta NUMBER;
BEGIN
    IF DELETING THEN
        v_delta := NVL(:OLD.outlay_amount, 0);   -- 刪除出庫單 = 回庫存
    ELSIF UPDATING THEN
        v_delta := NVL(:OLD.outlay_amount, 0) - NVL(:NEW.outlay_amount, 0);
    ELSE  -- INSERTING
        v_delta := -NVL(:NEW.outlay_amount, 0);  -- 新增出庫單 = 扣庫存
    END IF;

    UPDATE balance
    SET balance = NVL(balance, 0) + v_delta
    WHERE id = :NEW.id;
END;
/

關鍵技術點深度解析

1. 觸發事件判斷:INSERTING / UPDATING / DELETING

Oracle 提供三個布林函數,互斥且僅一個為 TRUE,配合 IF-ELSIF-ELSE 精確分流。

2. :OLD / :NEW 偽記錄

  • INSERT::OLD 全 NULL,:NEW 為新值。
  • DELETE::OLD 為舊值,:NEW 全 NULL。
  • UPDATE:兩者皆有值,核心邏輯 = 新值 - 舊值(入庫)或 舊值 - 新值(出庫)。

3. NVL 防禦性編程

欄位允許 NULL 時,NVL(col, 0) 避免算術運算產生 NULL 導致餘額錯誤。

4. 行級觸發器(FOR EACH ROW)

保證每筆 DML 逐行觸發,支援批量操作(如 INSERT SELECT)正確累加。

進階優化與避坑指南

  1. 複合觸發器(11g+):若需在語句級執行額外邏輯(如寫審計日誌),可改用 COMPOUND TRIGGER 統一管理。
  2. 避免遞歸觸發:觸發器內更新同表會再次觸發,需用包變數標記 IF NOT g_recursive THEN ... END IF;。
  3. 大批量操作效能:百萬級導入時,考慮 DISABLE TRIGGER → 批量更新餘額 → ENABLE TRIGGER。
  4. 權限最小化:觸發器擁有者需有 balance 表的 UPDATE 權限,避免授予過高權限。

總結

觸發器將「庫存扣減/回補」這類強一致性邏輯下沉到資料庫核心層,消除了應用層分散式事務的複雜度。雖然犧牲了可移植性,但在 Oracle 為主力的企業級系統中,是極具性價比的架構決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved