mysql存储过程不强制参数非空,须手动校验:数值类用is null,字符串类用is null or length(trim(@param)) = 0;失败时用signal sqlstate '45000'主动报错;调用必须用命名参数,禁用位置传参。

MySQL存储过程本身不强制参数非空,传NULL或空字符串不会报错,但后续逻辑(如CONVERT、WHERE条件、拼接、算术运算)极易崩溃或静默出错。必须手动校验。
用 IF ... IS NULL 或 IS NULL OR = '' 显式判断
MySQL中= NULL永远返回FALSE,只能用IS NULL;字符串还需额外检查空值'',因为NULL和''语义不同。
常见错误现象:
- 传
NULL进INT参数,后续WHERE id = @id变成全表扫描 - 传
''进VARCHAR,IF @name IS NULL跳过,但业务上它就是“没填”
实操建议:
- 对数值/日期类参数:只判
IS NULL - 对字符串类参数:统一用
IF @param IS NULL OR @param = '' - 避免用
TRIM(@param) = ''——MySQL 5.7默认不支持TRIM单参数形式,8.0+才支持;稳妥写法是IF @param IS NULL OR LENGTH(TRIM(@param)) = 0
校验失败时用 SIGNAL SQLSTATE 主动报错
MySQL不支持THROW(那是SQL Server的),必须用SIGNAL抛出自定义错误,否则流程继续执行,错误被掩盖。
实操建议:
- 用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '参数 @user_id 不能为空' -
SQLSTATE '45000'是标准通用错误码,ORM(如MyBatis、Dapper)能识别并转成异常 - 不要用
SELECT '错误'或SET @err = 1代替——调用方无法感知失败 - 错误信息里带上参数名,方便前端或日志快速定位漏传点
多个参数校验别堆在开头写一长串 IF
5个参数挨个IF ... SIGNAL不仅啰嗦,还容易漏掉某个分支的END IF,导致语法错误。
实操建议:
- 把所有必填参数校验集中放在
BEGIN后第一段,用缩进对齐,一眼看出哪些不能为NULL - 按类型分组:先数值类,再字符串类,最后可选参数(如有)
- 示例片段:
IF @user_id IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '参数 @user_id 不能为空'; END IF; IF @name IS NULL OR LENGTH(TRIM(@name)) = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '参数 @name 不能为空或仅空白'; END IF;
注意:LENGTH(TRIM(@name)) = 0比@name = ''更安全,能覆盖' '、'\t'等空白字符。
调用时必须用命名参数,禁用位置传参
MySQL支持CALL proc(1, 'a', NULL)这种位置调用,但一旦过程改了参数顺序或加了新参数,调用方不动代码就传错值——比如@email收到@phone的值,且无任何类型提示。
实操建议:
- 一律使用命名调用:
CALL check_user(@user_id := 123, @name := '张三') - 即使只有一个参数,也写全:
CALL check_name(@name := '李四') - 测试时故意传一个不存在的参数名(如
@user_idd),确认报错是Unknown parameter而非静默忽略 - 如果调用方是Java + JDBC,确保
CallableStatement用setXXX("param_name", value)而非setXXX(1, value)
最易被忽略的一点:MySQL存储过程没有“默认值”机制(不像PostgreSQL或SQL Server),IN参数声明里的DEFAULT关键字在MySQL中无效。所谓“默认值”只能靠调用方保证,或在过程内用COALESCE(@param, 'default')兜底——但要注意COALESCE对''不生效,它只处理NULL。











