自愈型存储过程并非真正智能,而是将人工确认的校验与修复逻辑通过事务、日志和防御性编码固化;必须确保select与update条件严格一致、封装校验为视图或cte、执行前统计行数并设阈值中止、包裹在显式事务中并完备错误处理、规则外置到配置表以支持零发布变更。

自愈型存储过程这个说法容易误导——SQL Server 或 MySQL 的存储过程本身没有“感知损坏→判断原因→自主决策修复”的能力。所谓“自动修复”,实际是把人工确认过的校验规则和修正动作,用事务+日志+防御性编码固化下来。它不智能,但可审计、可回滚、可复现。
SELECT 必须先于 UPDATE,且条件严格一致
这是最常翻车的点。很多人写完校验查询,复制粘贴时漏改字段、多加括号、或 WHERE 条件用了不同别名,导致修复语句影响了不该动的行。
- 校验语句必须返回明确的业务矛盾,例如:
SELECT order_id FROM orders WHERE status = 'shipped' AND shipped_at IS NULL - 修复语句的
WHERE必须逐字符一致,不能写成WHERE orders.status = 'shipped' AND shipped_at IS NULL(表名前缀差异可能让执行计划走错索引) - 建议把校验逻辑封装为视图或 CTE,修复语句直接引用,避免手写两遍
- 执行前加
SELECT COUNT(*)统计待修复行数,超阈值(如 >100)就RAISERROR中止,不自动执行
修复操作必须包裹在显式事务中,并配错误处理器
跳过事务的“修复”等于埋雷。一旦中途失败(比如唯一键冲突、外键拒绝、字段长度超限),没回滚就会留下半修复状态——比原始问题更难诊断。
- SQL Server 示例:
BEGIN TRY BEGIN TRANSACTION; UPDATE orders SET shipped_at = GETDATE() WHERE status = 'shipped' AND shipped_at IS NULL; COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; INSERT INTO repair_log (table_name, condition, error_msg, executed_at) VALUES ('orders', 'status = ''shipped'' AND shipped_at IS NULL', ERROR_MESSAGE(), GETDATE()); END CATCH - 不要在
CATCH块里尝试“补救”,比如捕获主键冲突后改插一条新记录——这会掩盖原始数据质量缺陷 - MySQL 对应用
DECLARE EXIT HANDLER FOR SQLEXCEPTION,逻辑一致
把业务规则从代码里抽出来,存在配置表中
硬编码规则(如 WHERE amount )会导致每次策略调整都要改存储过程、申请上线、停服务验证。
- 建一张
repair_rules表,至少含:rule_id、table_name、check_sql(不含 SELECT)、fix_sql(不含 UPDATE)、enabled - 存储过程只做:读取启用的规则 → 拼接完整
SELECT … FROM … WHERE + check_sql→ 若有结果,再拼UPDATE … SET … WHERE + fix_sql - 规则变更只需
UPDATE repair_rules SET check_sql = 'amount ,零发布成本
真正难的不是写 SQL,而是定义清楚:“什么算错”“谁有权认定”“修错了找谁”。所有看似“自动”的过程,背后都依赖人对业务边界的反复确认。越想让它“自愈”,越要先把校验口径钉死、把影响范围锁住、把每一步操作记进日志表——否则修复动作本身,就是最大的数据风险源。











