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

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


八、工程级总结

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

相关推荐
Jason_zhao_MR3 小时前
米尔RK3506核心板SDK重磅升级,解锁三核A7实时控制新架构
linux·嵌入式硬件·物联网·架构·嵌入式·嵌入式实时数据库
叮咚侠3 小时前
Ubuntu 24.04.3 LTS 中 vdb 的 UUID 永久挂载没有显示的磁盘的操作步骤
linux·运维·ubuntu·挂载磁盘
代码or搬砖3 小时前
SQL核心语法总结:从基础操作到高级窗口函数
java·数据库·sql
3 小时前
TIDB——TIKV——读写与coprocessor
数据库·分布式·tidb·
.小墨迹3 小时前
C++学习之std::move 的用法与优缺点分析
linux·开发语言·c++·学习·算法·ubuntu
风华同学3 小时前
【Linux驱动篇】LED驱动开发实验
linux·驱动开发·ubuntu
李斯维3 小时前
安装 WSL 最好的方式
linux·windows
大猫和小黄4 小时前
若依微服务全面适配PostgreSQL-OpenGauss数据库
数据库·微服务·postgresql·若依
张小九994 小时前
fpocket安装和使用教程
linux·机器学习·github