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

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


八、工程级总结

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

相关推荐
数智工坊9 小时前
Zotero自建Ubuntu WebDAV附件同步方案(完美解决存储空间不足)
linux·运维·ubuntu
ltl10 小时前
HTTP/3 实战:从 QUIC 到 H3 的完整请求链路
linux
IvorySQL10 小时前
PostgreSQL 日报 | JOIN 与外键优化(8 月 9 日)
数据库·postgresql
ltl10 小时前
POSIX 文件锁:flock、fcntl 与 NFS 上的工程陷阱
linux
ltl11 小时前
CUDA 生态:cuBLAS、cuDNN、NCCL、Triton、CUTLASS
linux
饺子大魔王的男人11 小时前
CentOS部署Cockpit:图形化查看系统、日志与服务状态
linux·运维·centos
ltl11 小时前
自治数据库十年回顾:Peloton、NoisePage、OtterTune 到云原生 auto-tuning
数据库
不会就选b11 小时前
Linux之信号(二)
linux·运维·服务器
我是小灰灰吖12 小时前
QT在线安装器使用指南:从下载到配置完整教程
linux·qt·ubuntu
GlueNa2SiO312 小时前
第十八章 Linux故障排查与恢复
linux·服务器·笔记·学习