业务规则必须抽离为独立函数,存储过程仅负责参数接收、调用封装逻辑、返回结果;函数需语义化命名、强类型输入输出、明确错误上下文,严禁混杂计算与适配逻辑。

把业务规则抽成独立函数,别塞进存储过程里
存储过程一旦混入大量计算、格式转换、状态判断逻辑,就变成“黑盒”,改一行可能影响五个下游调用。核心原则是:存储过程只做三件事——接收参数、调用封装好的逻辑单元、返回结果。
-
CREATE FUNCTION或CREATE PROCEDURE单独定义校验规则(如is_valid_order_status)、金额计算(如calc_discount_amount),再在主存储过程中SELECT或EXEC调用 - SQL Server 中避免在存储过程内写多层嵌套
CASE WHEN判断订单状态流转,改用get_next_approval_step(@current_step)函数封装 - PostgreSQL 的
plpgsql存储过程里,别直接拼接COALESCE(a, b, c, 'N/A')做默认值,而应抽象为coalesce_with_default(a, b, c)函数,便于统一处理空值策略 - 函数必须有明确输入输出类型,禁止返回
TABLE类型用于简单标量计算(比如日期偏移),否则调用方要写(SELECT offset_date FROM ...),可读性骤降
参数命名和类型必须和业务域对齐,不是数据库字段名
看到 @p1、@input_val 这种参数名就该警觉——它暴露的是实现细节,不是业务意图。维护者第一眼根本猜不出这个参数控制的是「是否跳过库存预占」还是「是否启用老用户补贴」。
- 用业务语义命名:
@skip_inventory_lock比@flag1清晰,@is_legacy_user_promo_enabled比@enable_flag可维护 - 避免用
VARCHAR(50)接收所有字符串参数;订单号就该是CHAR(16),状态码限定为CHAR(2)(如'SH','CN'),让类型本身成为契约 - SQL Server 中
BIT类型不能传NULL表示“未指定”,得用TINYINT或单独加@is_skip_flag_specified BIT参数,否则调用方传NULL会静默转成0 - MySQL 存储过程中若用
JSON类型参数接收动态条件,务必在开头用JSON_VALID()校验,否则后续JSON_EXTRACT报错时堆栈不指向参数来源
错误处理别只靠 RAISERROR 或 THROW 打日志
抛出 Msg 50000, Level 16, State 1 对 DBA 有用,但对业务开发毫无意义。真正卡住维护的是:不知道这个错误对应哪条业务规则失败、上游要不要重试、是否需要补偿。
- SQL Server 中用
THROW 50001, 'Order validation failed: stock insufficient for item %s', 1, @item_code,把关键业务标识(如单号、商品编码)带进错误消息体 - PostgreSQL 的
RAISE EXCEPTION后紧跟GET STACKED DIAGNOSTICS提取pg_context,但更关键是提前在存储过程开头写PERFORM log_entry('start', current_setting('app.request_id'), json_build_object(...))记录上下文 - 避免在事务中捕获所有错误后
RETURN静默退出;宁可让事务回滚并抛出带业务码的错误(如'ERR_STOCK_LOCK_FAILED'),由应用层统一识别处理 - Oracle 的
PRAGMA AUTONOMOUS_TRANSACTION日志写法很危险:如果主事务回滚,日志却已提交,查问题时会发现「日志说成功了,但数据没变」
别让存储过程承担接口适配职责
一个存储过程同时被报表系统、APP 后端、对账系统调用,最后演变成“兼容模式开关”满天飞:IF @compat_mode = 'v2' THEN ... ELSE ... END。这不是解耦,是把多个接口协议缝在同一个函数里。
- 对外暴露的存储过程只做一件事:接收标准输入、返回标准结构。适配逻辑下沉到视图或中间层函数,例如
vw_order_summary_for_app和vw_order_summary_for_report分别包装同一张基础表 - SQL Server 中用
SCHEMABINDING创建函数或视图,防止底层表结构变更时,上游存储过程还在引用已删字段而不报错 - MySQL 不支持函数内事务,如果存储过程里要调用多个写操作函数,别指望靠函数封装来“解耦”,反而该拆成多个小存储过程,由应用层编排调用顺序
- 所有跨系统调用的存储过程,必须在注释头明确写清:「此过程仅供
billing_servicev3.2+ 调用,字段final_amount含税,tax_amount为明细项」——版本、税费口径、字段含义,缺一不可
最常被忽略的其实是调用链路的可见性:没人会去查一个叫 sp_process_order 的过程到底调用了哪些函数,除非你在 SQL Server 的 sys.dm_exec_procedure_stats 里定期采样,或 PostgreSQL 的 pg_stat_statements 里按 queryid 聚合。可维护性不是写得漂亮,而是出问题时能快速定位到那一行业务规则。










