SQL Server 存储过程设计规范(事务与异常处理)

一、背景与目标

在中大型业务系统中,存储过程往往被多处调用、层层嵌套,一旦事务和异常处理设计不规范,极易导致以下问题:

  • 子存储过程误提交或回滚父事务

  • 事务悬挂(未提交 / 未回滚)

  • 异常被吞掉,调用方无法感知失败

  • 生产环境问题难以定位

因此,必须建立一套统一、强约束、可组合的存储过程设计规范。

本文给出一套行业级 SQL Server 存储过程事务规范,并解释其设计原理。


二、核心设计原则

原则 1:存储过程不擅自管理外部事务

  • 存储过程只在没有事务时才开启事务

  • 不擅自 COMMIT / ROLLBACK 调用方事务

  • 支持多层存储过程安全嵌套

存储过程必须是"事务友好型组件",而不是事务的拥有者。


原则 2:事务的开启、提交、回滚必须成对、可控

统一通过一个本地变量标识事务是否由当前存储过程开启:

复制代码
DECLARE @TranStarted BIT = 0; 

原则 3:TRY...CATCH 是强制要求

  • 禁止裸写 BEGIN TRAN / COMMIT

  • 禁止不捕获异常

  • 禁止在 CATCH 中继续执行业务 SQL


原则 4:异常必须可追踪、可传播

  • 记录错误日志(表或统一过程)

  • 对外部调用者:

    • 接口型存储过程 → 返回 code / msg

    • 业务型存储过程 → THROW 抛出异常


三、统一事务控制模板(强制)

标准事务控制骨架

sql 复制代码
SET NOCOUNT ON;
SET IMPLICIT_TRANSACTIONS OFF;

DECLARE @TranStarted BIT = 0;

BEGIN TRY
    IF @@TRANCOUNT = 0
    BEGIN
        BEGIN TRAN;
        SET @TranStarted = 1;
    END

    -- ======================
    -- 业务逻辑
    -- ======================

    IF @TranStarted = 1
    BEGIN
        COMMIT;
    END
END TRY
BEGIN CATCH
    IF @TranStarted = 1 AND XACT_STATE() <> 0
    BEGIN
        ROLLBACK;
    END

    -- 统一错误处理
    EXEC dbo.proc_error_log;

    -- 异常传播策略由类型决定
END CATCH

四、两类存储过程的规范区分

1️⃣ 接口型存储过程(返回 code / msg)

适用场景

  • 被 Java / Go / .NET 直接调用

  • 作为接口的"最终边界"

  • 前端或服务端需要明确状态码

规范示例(你给出的 nb_tran_demo1
sql 复制代码
CREATE PROCEDURE dbo.nb_tran_demo1
AS
BEGIN
    SET NOCOUNT ON;
    SET IMPLICIT_TRANSACTIONS OFF;

    DECLARE @TranStarted BIT = 0;

    BEGIN TRY
        IF @@TRANCOUNT = 0
        BEGIN
            BEGIN TRAN;
            SET @TranStarted = 1;
        END

        -- ======================
        -- 业务逻辑
        -- ======================

        IF @TranStarted = 1
        BEGIN
            COMMIT;
        END

        SELECT 200 AS code, '成功' AS msg;
    END TRY
    BEGIN CATCH
        IF @TranStarted = 1 AND XACT_STATE() <> 0
        BEGIN
            ROLLBACK;
        END

        EXEC dbo.proc_error_log;

        SELECT 501 AS code, '系统处理失败' AS msg;
    END CATCH
END
设计要点
  • ❌ 不使用 THROW

  • ✅ 异常被转换为业务可识别结果

  • ✅ 保证接口幂等、稳定


2️⃣ 业务型存储过程(必须 THROW)

适用场景

  • 被其他存储过程调用

  • 业务逻辑拆分、复用

  • 不直接面对前端或接口层

规范示例(你给出的 nb_tran_demo
sql 复制代码
CREATE PROCEDURE dbo.nb_tran_demo
AS
BEGIN
    SET NOCOUNT ON;
    SET IMPLICIT_TRANSACTIONS OFF;

    DECLARE @TranStarted BIT = 0;

    BEGIN TRY
        IF @@TRANCOUNT = 0
        BEGIN
            BEGIN TRAN;
            SET @TranStarted = 1;
        END

        -- ======================
        -- 业务逻辑
        -- ======================

        IF @TranStarted = 1
        BEGIN
            COMMIT;
        END
    END TRY
    BEGIN CATCH
        IF @TranStarted = 1 AND XACT_STATE() <> 0
        BEGIN
            ROLLBACK;
        END

        EXEC dbo.proc_error_log;

        THROW;
    END CATCH
END
设计要点
  • 必须使用 THROW

  • ❌ 禁止返回 SELECT code

  • ✅ 由上层决定事务命运


五、XACT_STATE 的强制使用规范

XACT_STATE() 含义 处理方式
1 事务可提交 可 COMMIT / ROLLBACK
-1 事务已损坏 只能 ROLLBACK
0 无事务 禁止操作

规范写法

sql 复制代码
IF @TranStarted = 1 AND XACT_STATE() <> 0 ROLLBACK; 

六、错误日志处理规范

统一错误记录过程(示意)

sql 复制代码
CREATE PROCEDURE dbo.proc_error_log
AS
BEGIN
    INSERT INTO sys_error_log (
        error_number,
        error_message,
        error_procedure,
        error_line,
        create_time
    )
    VALUES (
        ERROR_NUMBER(),
        ERROR_MESSAGE(),
        ERROR_PROCEDURE(),
        ERROR_LINE(),
        GETDATE()
    );
END

设计原则

  • ❌ 禁止在业务过程里散写 ERROR_*()

  • ✅ 所有异常集中入口

  • ✅ 支持后续告警、审计、分析


七、明确禁止的写法(红线)

❌ 在子存储过程中无条件 COMMIT

❌ CATCH 中继续执行业务 SQL

❌ 使用 @@ERROR

❌ 混用 RAISERRORTHROW

❌ 不区分接口型 / 业务型存储过程


八、工程级总结

一条铁律:谁开启事务,谁才有资格提交或回滚事务。

相关推荐
qianshang23314 小时前
SQL注入学习总结
网络·数据库·渗透
what丶k14 小时前
深入解析Redis数据持久化:RBD机制原理、实操与生产最佳实践
数据库·redis·缓存
瀚高PG实验室15 小时前
通过数据库日志获取数据库中的慢SQL
数据库·sql·瀚高数据库
Hgfdsaqwr15 小时前
Python在2024年的主要趋势与发展方向
jvm·数据库·python
JiMoKuangXiangQu15 小时前
ARM64 进程虚拟地址空间布局
linux·arm64 虚拟地址布局
invicinble15 小时前
对于Mysql深入理解
数据库·mysql
阳光九叶草LXGZXJ16 小时前
达梦数据库-学习-47-DmDrs控制台命令(LSN、启停、装载)
linux·运维·数据库·sql·学习
春日见16 小时前
如何避免代码冲突,拉取分支
linux·人工智能·算法·机器学习·自动驾驶
Hgfdsaqwr16 小时前
掌握Python魔法方法(Magic Methods)
jvm·数据库·python
s1hiyu16 小时前
使用Scrapy框架构建分布式爬虫
jvm·数据库·python