mysql存储过程是业务逻辑下沉的关键,需严守参数规则与异常处理:in参数修改不影响调用方;out/inout必须用@变量接收且inout需预初始化;if/case须配declare exit handler,推荐resignal或signal抛错。

MySQL 存储过程不是“可有可无”的语法糖,而是业务逻辑下沉到数据库层的关键手段——但必须写对参数类型、流程分支和错误处理,否则反而埋下隐性故障。
IN/OUT/INOUT 参数到底怎么用才不踩坑
很多人以为 IN 就是“传进去”,OUT 就是“拿出来”,但实际调用时容易漏掉关键细节:
-
IN参数在存储过程中被修改,不会影响调用方的原始变量(比如传入@x := 10,过程里改SET p_in := 20,调用后@x还是 10); -
OUT参数必须用用户变量接收,且调用前无需赋值(CALL proc8(1001, @name); SELECT @name;),否则返回NULL; -
INOUT是两者的混合,调用前要初始化,过程内可读可写,调用后能拿到新值; - 所有
OUT和INOUT参数,都必须在CALL时传入**用户变量**(以@开头),不能传字面量或字段名。
IF / CASE 流程控制必须配异常处理器
只写 IF ... THEN ... ELSE 而不加异常捕获,等于裸奔——尤其涉及事务时,出错就卡在中间状态:
- MySQL 的
DECLARE EXIT HANDLER FOR SQLEXCEPTION不是可选项,是强需求; - 别用
ROLLBACK后直接SELECT 'error',这会让应用层收不到标准错误码;推荐用RESIGNAL(保留原错误信息)或SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'xxx'; -
CASE适合多分支等值判断(如状态码映射),IF更适合布尔逻辑(如IF v_balance ); - 注意
IF必须成对出现:IF ... THEN ... END IF;,漏掉END IF会报语法错误,且错误提示常指向末尾而非真实缺位处。
局部变量声明位置和作用域极易混淆
MySQL 要求所有 DECLARE 必须放在 BEGIN 后的最前面,任何 SQL 语句之前——顺序错就报错:
- 局部变量(
DECLARE v_count INT DEFAULT 0;)只在当前BEGIN...END块内有效,嵌套块中需重新声明; - 别和用户变量(
@v_count)混用:后者跨语句存活,但不可用于INTO子句的目标(SELECT x INTO v_local合法,SELECT x INTO @v_user报错); - 想把查询结果存进局部变量,必须用
SELECT ... INTO v_var,不能用SET v_var = (SELECT ...)(子查询返回多行会报错,而INTO会明确报Subquery returns more than 1 row)。
真正难的不是写出一个能跑的存储过程,而是让它在并发转账、批量导入、定时对账等真实场景下不丢数据、不错账、不锁死。参数类型选错、异常没兜底、变量作用域误判——这些地方出问题,日志里往往只有一行 ERROR 1305 (42000): PROCEDURE xxx does not exist,其实根本不是不存在,是上次创建因语法错误静默失败了。










