mysql数据库中存储过程的创建与调试

来源:这里教程网 时间:2026-02-28 20:45:40 作者:

创建存储过程前必须设置 delimiter

MySQL 默认用分号

;
作为语句结束符,而存储过程中会包含多个内部语句(比如
SELECT
IF
SET
),如果不改分隔符,MySQL 会在第一个
;
就尝试执行,直接报错
ERROR 1064 (42000)

正确做法是用

DELIMITER $$
(或任意非分号符号)临时修改分隔符,定义完再改回去:

DELIMITER $$
CREATE PROCEDURE get_user_by_id(IN uid INT)
BEGIN
    SELECT * FROM users WHERE id = uid;
END$$
DELIMITER ;

常见踩坑点:

忘记恢复
DELIMITER ;
,后续所有语句都会报语法错误
在客户端工具(如 MySQL Workbench)里复制整段时,如果工具自动补全了分号,可能让
$$
后多出一个
;
,导致语法失败
存储过程名不能和已存在的函数/表同名,否则报
ERROR 1304 (42000): PROCEDURE xxx already exists

调试存储过程没有 print 或 console.log,得靠 SELECT 和 SHOW WARNINGS

MySQL 存储过程中没有原生日志输出函数,调试主要靠两种方式:

在关键位置插入
SELECT 'debug: step 1';
—— 这样调用时会返回额外的结果集,能直观看到执行到了哪一步
SHOW WARNINGS;
查看最近一次执行是否触发了警告(比如类型隐式转换、NULL 值参与计算)
对变量赋值后立即
SELECT @var_name;
(需先用
SET @var_name := ...
定义用户变量)

注意:

SELECT
调试法在应用程序中调用时可能破坏结果集结构(比如应用只期待一行用户数据,却收到两行:一行 debug 提示 + 一行真实数据),建议仅在命令行或测试环境使用。

参数类型 IN / OUT / INOUT 容易混淆,特别是 OUT 的赋值时机

IN
是传入值(默认),
OUT
是传出值(调用后才可读),
INOUT
是两者兼有。关键区别在于:

OUT
参数在进入过程时为
NULL
,即使调用时传了值也会被忽略
必须在过程内显式赋值,例如
SET out_result = 'done';
,否则调用后仍为
NULL
调用时
OUT
INOUT
参数必须传用户变量(如
@result
),不能传字面量(如
'abc'

示例:

DELIMITER $$
CREATE PROCEDURE calc_total(IN price DECIMAL(10,2), OUT total DECIMAL(10,2))
BEGIN
    SET total = price * 1.08; -- 必须赋值,否则 total 返回 NULL
END$$
DELIMITER ;
<p>CALL calc_total(100.00, @t);
SELECT @t; -- 返回 108.00

调试时别忘了检查 SQL_MODE 和权限

存储过程执行失败,有时不是逻辑问题,而是环境限制:

如果开启了严格模式(
STRICT_TRANS_TABLES
),插入超长字符串、除零等操作会直接报错中断,而默认模式下可能只发警告继续执行
创建存储过程需要
CREATE ROUTINE
权限;执行需要
EXECUTE
权限;修改需要
ALTER ROUTINE
权限——普通应用账号通常不带这些,开发时容易卡在
ERROR 1418 (HY000)
查看当前 SQL_MODE:
SELECT @@sql_mode;
;临时关闭严格模式:
SET sql_mode = '';
(仅会话级,不推荐生产)

真正麻烦的是跨环境迁移:开发库开了

log_bin_trust_function_creators=1
,但生产库没开,会导致创建失败且报错信息非常模糊。

相关推荐