存储过程不能承载规则引擎逻辑,也不该驱动多级审批流状态跃迁;它仅适用于合法性校验、冗余字段补全、轻量聚合三类操作,规则解析与路由必须移至应用层或工作流服务。

存储过程不能承载规则引擎逻辑,也不该用来驱动多级审批流的状态跃迁。 它最多只能做校验、补字段、轻量聚合这三件事。真要实现“基于规则引擎的多级审批”,必须把规则解析、条件匹配、下一步路由这些动作移出数据库,放到应用层或独立工作流服务里。
为什么存储过程撑不起规则引擎
规则引擎的核心能力是动态加载、条件组合、上下文感知和可热更新——而存储过程在这些方面天然受限:
- 无法读取外部配置:MySQL/PostgreSQL 的存储过程不能直接拉取 JSON 规则文件或调用 HTTP 接口获取当前流程定义
- 不支持运行时编译逻辑:你没法在
CREATE PROCEDURE里写个EVAL('status == "approved" && role in ("mgr", "dir")')—— 没有等价语法 - 事务边界卡死扩展性:一旦规则触发通知、发邮件、调第三方 API,就超出事务范围;硬塞进存储过程会导致超时、锁表或失败静默
- 规则变更即 DBA 发版:改一条“法务部加签”规则,就得 ALTER PROCEDURE,还要走 SQL 上线流程,完全违背规则引擎“配置即代码”的初衷
存储过程能且仅能做的三类事
把它当数据库侧的“守门员”,不是“决策者”。以下操作可以且应该放在存储过程中:
-
状态跃迁合法性校验:比如在
approval_steps表的INSERT触发器里,查前序步骤是否已status = 'approved',否则SIGNAL SQLSTATE '45000' -
冗余字段自动补全:写入
updated_at、approver_role(别查users表!冗余进审批记录表)、flow_version -
轻量聚合回填主表:例如从明细表算出当前最高通过级,更新
AuditFlow.CurrentStage字段,但不触发下一步动作
示例(PostgreSQL):
CREATE OR REPLACE FUNCTION validate_next_step() RETURNS TRIGGER AS $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM approval_steps s
WHERE s.request_id = NEW.request_id
AND s.step = NEW.step - 1
AND s.status = 'approved'
) THEN
RAISE EXCEPTION 'Step % requires step % to be approved first', NEW.step, NEW.step - 1;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
真正可行的规则引擎集成方式
数据库只存事实,规则判断交给外面:
-
应用层解析规则:收到审批提交后,应用读取
rules_config表(含 JSON 字段),用 Jackson/Gson 解析条件,匹配当前用户角色、表单类型、金额区间等,再决定下一节点 - 异步工作流服务驱动:审批记录落库后,由 Kafka 消费者或定时扫描触发规则引擎(如 Drools、Easy Rules),执行路由、通知、超时提醒等,结果再回写数据库
-
数据库只暴露确定性接口:提供
get_approvers_for_step(request_id, step)这类纯查询函数,供外部调用,不包含 if-else 分支逻辑
容易被忽略的坑
哪怕只做校验,也常踩这些坑:
- 在触发器里查
users表判断角色?会锁用户表,改成冗余approver_role字段 + 联合索引 - 用子查询查“是否全部通过”?换成
COUNT(*) FILTER (WHERE status = 'approved')配合INDEX(approval_steps(request_id, status)) - 以为
EXECUTE AS能绕过权限限制?它只解决对象访问,不解决业务语义合法性,比如不能阻止主管批自己发起的申请
规则越复杂,越要远离存储过程。它不是审批流的引擎,只是数据落库前的最后一道滤网。











