通過 BEFORE INSERT 行級觸發器,結合配置表取前綴、當前月份、本月最大流水號,自動生成格式為 GXCB_20241100001 的單據號,含完整代碼與邏輯解析。
CREATE OR REPLACE TRIGGER trg_kc_sprk_i
BEFORE INSERT ON kc_sprk
FOR EACH ROW
DECLARE
v_scbh VARCHAR2(40); -- 前綴
i_count NUMBER; -- 本月流水號
i_for NUMBER; -- 循環變量
BEGIN
-- 1. 從單據編碼規則表獲取前綴 (規則 ID=8)
SELECT t.bhqz INTO v_scbh FROM xt_djbhgz t WHERE t.gzid = 8;
-- 若無配置表,可直接硬編碼: v_scbh := 'GXCB_';
-- 2. 計算本月已有單據數 + 1
SELECT COUNT(1) + 1 INTO i_count
FROM kc_sprk j
WHERE j.xzsj >= TRUNC(SYSDATE, 'MM'); -- 本月第一天 00:00:00
-- 3. 組裝前綴 + 年月 (yyyymm)
v_scbh := v_scbh || TO_CHAR(SYSDATE, 'YYYYMM'); -- 'GXCB_202411'
-- 4. 補齊流水號至固定 5 位 (不足前補 0)
FOR i_for IN 1 .. 5 - LENGTH(i_count) LOOP
v_scbh := v_scbh || '0';
END LOOP;
-- 5. 最終單據號賦值給新行
:NEW.rk_djh := v_scbh || i_count;
END trg_kc_sprk_i;
-- 假設前綴 GXCB_,11 月第 1,2,3 筆 GXCB_20241100001 GXCB_20241100002 GXCB_20241100003
| 步驟 | 邏輯 | 潛在問題 | 優化建議 |
|---|---|---|---|
| 取前綴 | 查配置表 XT_DJBH_GZ | 查表開銷、配置變更需重編譯 | 改用 SYS_CONTEXT / 全局變量 / Result Cache 函數緩存前綴 |
| 計算流水 | COUNT(*) + 1 掃描本月數據 | 大表全掃、併發下重複風險 | 改用 序列 + 獨立表存儲月份狀態 或 DBMS_SQLTUNE 緩存 |
| 補零循環 | PL/SQL 循環拼接 | 效率低 | LPAD(i_count, 5, '0') 或 TO_CHAR(i_count, 'FM00000') |
| 併發安全 | 無鎖機制 | 高併發下極大概率產生重複單據號 | 加 SELECT ... FOR UPDATE 鎖定統計行,或用序列保證唯一性 |
-- 1. 創建序列 (不循環, 不緩存)
CREATE SEQUENCE seq_kc_sprk NOCACHE NOCYCLE;
-- 2. 月份重置記錄表
CREATE TABLE kc_sprk_seq_ctrl (
yyyymm VARCHAR2(6) PRIMARY KEY,
base_val NUMBER DEFAULT 0 -- 該月序列起始基數
);
-- 3. 觸發器邏輯 (簡化)
:NEW.rk_djh := v_prefix || TO_CHAR(SYSDATE,'YYYYMM') ||
LPAD(seq_kc_sprk.NEXTVAL - base_val, 5, '0');
-- 每月首筆插入 kc_sprk_seq_ctrl 記錄 base_val = seq.CURRVAL - 1
觸發器生成單據號邏輯直觀,但行級觸發器 + COUNT(*) 掃描 + 無鎖 在高併發下極易重複且性能差。生產系統建議採用 序列 + 月份基數表 或 Redis/應用層號段模式,觸發器僅作為兼容舊系統的過渡方案。