SQL Server 中 RAISERROR 是最可控的主动报错方式,需设严重级16、配 WITH SETERROR 才使 ERROR_NUMBER() 返回自定义值,消息限400字符;MySQL 无 RAISERROR,仅能通过 EXIT HANDLER + GET DIAGNOSTICS 提取错误;PostgreSQL 必须用 FUNCTION + RAISE EXCEPTION 才可返回带 ERRCODE 的结构化错误。

SQL Server 用 RAISERROR 主动抛出自定义错误
直接在存储过程里用 RAISERROR 是最可控的方式,它能立刻中断执行、触发客户端 CATCH 块,并把你的消息传出去。关键不是“捕获错误”,而是“提前判断+主动报错”。
-
RAISERROR第二个参数是严重级别:11~19 会进TRY...CATCH,0~10 只发消息不中断,≥20 直接断连接——日常用16最稳妥 - 必须配
WITH SETERROR才能让ERROR_NUMBER()返回你设的值(否则还是系统错误号) - 消息体别超 400 字符,
MESSAGE_TEXT在客户端(如 Python 的pymysql或 C# 的SqlException)里能直接读到 - 示例:
RAISERROR('订单状态非法:%s', 16, 1, @status) WITH SETERROR;
MySQL 存储过程中用 GET DIAGNOSTICS 捕获并包装错误
MySQL 不支持 RAISERROR 那类主动报错,只能靠 DECLARE EXIT HANDLER FOR SQLEXCEPTION 拦截已发生的错误,再用 GET DIAGNOSTICS 提取原始信息,最后通过 SELECT 或 OUT 参数返回。
-
GET DIAGNOSTICS CONDITION 1 @msg = MESSAGE_TEXT, @code = MYSQL_ERRNO;是唯一可靠提取方式,@@ERROR在 MySQL 里没用 - 别在
EXIT HANDLER里写复杂逻辑——它只保证执行一次,且不能 rollback(存储过程本身不支持事务回滚) - 如果要返回结构化错误,优先用
OUT参数:OUT p_error_code INT, OUT p_error_msg VARCHAR(255),调用方显式接收 - 避免用
SELECT返回错误信息——它会污染业务结果集,尤其当调用方 expect 单一结果时会出错
PostgreSQL 必须改写为函数才能返回自定义错误
PostgreSQL 的存储过程(PROCEDURE)从 12 版起才支持,但依然不提供标准错误返回机制;真正能稳定返回自定义错误的,只有 FUNCTION + RAISE EXCEPTION 组合。
-
RAISE EXCEPTION '用户不存在,ID: %', v_id USING ERRCODE = 'P0001';——ERRCODE可设 5 位自定义码(如P0001),客户端可通过SQLSTATE捕获 - 函数必须声明为
RETURNS TABLE或RETURNS VOID,不能用PROCEDURE替代——后者连RETURN都不支持 - 调用时不能用
CALL,得用SELECT * FROM func_name(...),否则错误不会透出到应用层 - 注意:函数里不能有 DML 语句(如
INSERT)除非加VOLATILE,但那样就失去函数语义了
跨数据库统一处理的坑:别依赖 RETURN 值传错误信息
RETURN 在 SQL Server 里是整数状态码,在 MySQL 里压根不存在,在 PostgreSQL 里只用于函数返回值——它从来不是错误载体,只是流程信号。
- SQL Server 的
RETURN值必须用EXEC @ret = proc显式接收,漏掉这步就等于没返回 - MySQL 的
RETURN只属于函数(FUNCTION),存储过程(PROCEDURE)里写RETURN会直接语法报错 - 所有数据库里,
RETURN都不能传字符串、不能传 NULL、不能带上下文——它只适合标示“成功/失败/重试”这类极简状态 - 真要传错误详情,老实用
OUT参数(MySQL)、RAISERROR+TRY/CATCH(SQL Server)、RAISE EXCEPTION(PostgreSQL)
实际落地时,最容易被忽略的是错误上下文丢失:比如 SQL Server 里只 RAISERROR 但没 WITH SETERROR,Python 客户端拿到的 error.number 还是系统默认值;MySQL 里用了 SELECT 报错但没关 sql_mode,导致客户端收到两个结果集而崩溃。这些细节不验证,上线后就是静默故障。


















