当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL SIGNAL 怎样在存储程序中定义自定义异常

MySQL SIGNAL 怎样在存储程序中定义自定义异常

来源:17golang原创 2026-10-20 22:55:48 0浏览 收藏

存储程序里遇到参数不合法、业务状态不允许或数据边界不满足时,最容易出现的临时方案是故意访问一张不存在的表,让 MySQL “替你报错”。更稳妥的做法是使用 SIGNAL 主动返回异常,把 SQLSTATE、错误码和消息一次写进调用方能识别的错误契约。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/signal.html

要点速览
  • SIGNAL 的核心是先选择一个合法的 SQLSTATE,再用 SET 填写异常信息。
  • 通用用户自定义异常通常使用 '45000';不要使用以 '00' 开头的成功类 SQLSTATE。
  • DECLARE CONDITION 可以给 SQLSTATE 起名字,但命名条件供 SIGNAL 使用时应基于 SQLSTATE,而不是只绑定 MySQL 错误号。
  • SIGNAL 主动创建异常,handler 负责捕获,RESIGNAL 负责在 handler 中转发或修改已有异常。

先用 SQLSTATE 划定异常类别

SIGNAL 的第一步不是填写中文提示,而是确定异常类别。MySQL 8.4 文档规定,condition_value 可以直接写五字符的 SQLSTATE,也可以引用一个此前声明的命名条件。SQLSTATE 前两位表达类别,'45000' 是常用的“未处理用户自定义异常”。

CREATE PROCEDURE create_order(IN p_amount DECIMAL(10, 2))
BEGIN
    -- 金额不满足业务约束时,主动返回用户自定义异常
    IF p_amount 

如果需要表达更具体的条件,可以选用合适的 SQLSTATE,而不是把所有异常都塞进一个字符串。需要特别注意的是,SQLSTATE 不能以 '00' 开头,因为这一类表示成功;写错时 MySQL 会报告 Bad SQLSTATE。

MySQL SIGNAL 的 SQLSTATE、命名条件、存储程序作用域和异常出口之间的静态关系说明图
图1:SQLSTATE 与命名条件的关系图,展示存储程序作用域中的异常定义边界;这是静态说明图,不是截图或运行证据。

用 SIGNAL SET 填写错误契约

只返回一个 SQLSTATE 往往不够。应用端通常还需要稳定的错误号和可读消息,这时使用 SET 一次填写 MESSAGE_TEXT 与 MYSQL_ERRNO。同一个信息项在一条 SIGNAL 中只能出现一次,值可以来自存储程序参数、局部变量、系统变量或字面量。

CREATE PROCEDURE create_order(IN p_amount DECIMAL(10, 2))
BEGIN
    DECLARE v_message VARCHAR(128);

    -- 先把可复用的提示组装好,避免异常消息和判断条件脱节
    SET v_message = CONCAT('订单金额无效: ', p_amount);

    IF p_amount 

这里的职责分工很清楚:SQLSTATE 适合做异常类别判断,MYSQL_ERRNO 适合映射内部错误码,MESSAGE_TEXT 适合给日志或界面提供上下文。不要把关键业务分支只建立在可变的中文消息上。

MySQL SIGNAL 的 SQLSTATE、MYSQL_ERRNO、MESSAGE_TEXT 与客户端错误契约之间的静态关系说明图
图2:SIGNAL 信息项关系图,展示异常字段如何组成面向客户端的稳定错误契约;这是静态说明图,不是截图或运行证据。

用 DECLARE CONDITION 管理重复条件

当同一种异常会在多个分支出现时,可以在适用的复合语句块中先声明命名条件。命名条件让正文不必重复散落的 SQLSTATE 字符串,也能把“条件叫什么”和“条件是什么”分开维护。

CREATE PROCEDURE reserve_stock(IN p_stock INT)
BEGIN
    -- 命名条件绑定 SQLSTATE,便于在多个分支复用
    DECLARE stock_empty CONDITION FOR SQLSTATE '45000';

    IF p_stock 

声明作用域是迁移时的重点:内层同名条件会遮蔽外层同名条件;如果命名条件不在 SIGNAL 所处作用域内,会出现未定义条件错误。另一个常见误区是用 MySQL 错误号声明命名条件后再直接 SIGNAL,这种条件并不满足 SIGNAL 对命名条件的要求。

区分 SIGNAL、handler 与 RESIGNAL

这三个语句经常被混在同一段存储程序里,但它们的职责不同:SIGNAL 是主动创建条件,DECLARE ... HANDLER 是声明发生条件后的处理动作,RESIGNAL 则只能在条件处理器的上下文里转发已有条件,并可修改部分信息。

CREATE PROCEDURE import_one(IN p_code VARCHAR(32))
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        -- handler 负责捕获;RESIGNAL 负责保留异常并补充统一消息
        RESIGNAL SET MESSAGE_TEXT = '导入订单失败';
    END;

    IF p_code IS NULL OR p_code = '' THEN
        -- 这是主动创建异常,不是对已有异常做转发
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = '订单编码不能为空',
                MYSQL_ERRNO = 30003;
    END IF;
END;
-- 不在 handler 中调用 RESIGNAL,会得到 handler not active 类错误

如果需求是“发现业务条件不满足,直接给客户端一个新异常”,用 SIGNAL。如果需求是“捕获底层异常后保留它,再补充统一上下文”,才考虑 RESIGNAL。这条边界能避免为了统一消息而意外丢失原始 SQLSTATE。

按迁移清单收紧异常边界

将旧的“制造一个数据库错误”改成 SIGNAL 时,可以把检查拆成四个可执行项目。它们不是额外的运行时验证,而是写存储程序时应固定下来的接口约定。

检查项推荐做法常见迁移风险
SQLSTATE选择合法的五字符值,用户异常优先考虑 45000使用 00 开头的成功类值
错误信息用 MYSQL_ERRNO 和 MESSAGE_TEXT 分别承载机器码与可读提示让客户端解析中文消息判断分支
命名条件在当前作用域用 SQLSTATE 定义并复用把 MySQL 错误号当成可直接 SIGNAL 的命名条件
异常流转主动抛出用 SIGNAL,handler 内转发用 RESIGNAL在 handler 外使用 RESIGNAL,或覆盖掉原始错误

最终可以把异常契约写进存储程序的接口文档:调用方先按 SQLSTATE 判断大类,再按错误号分支,最后把消息写入日志或展示给用户。这样数据库从“偶尔报错的黑盒”变成了可以随版本迁移、可被客户端稳定消费的错误边界。

常见问题

SIGNAL 只能写在存储过程中吗?

不只能。MySQL 文档说明它也可以出现在触发器、事件或客户端直接执行的语句中;本文聚焦存储程序的自定义异常契约。

MESSAGE_TEXT 和 MYSQL_ERRNO 哪个更重要?

两者用途不同。错误号适合稳定分支判断,消息适合上下文展示;不要用消息文本替代机器可读的错误码。

什么时候应该使用 RESIGNAL?

当当前代码已经处于条件处理器中,并且希望把已有异常继续向外传递或补充信息时使用;主动创建新条件仍然使用 SIGNAL。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL optimizer hint 怎样限定连接顺序MySQL optimizer hint 怎样限定连接顺序
上一篇
MySQL optimizer hint 怎样限定连接顺序
MySQL 锁等待超时怎样从 performance_schema 找阻塞者
下一篇
MySQL 锁等待超时怎样从 performance_schema 找阻塞者
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    570次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    614次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    641次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    601次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    419次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码