MySQL存储过程是一组预编译并保存在服务端的SQL语句集合,通过调用名字加参数来执行。它适合封装报表统计、批量更新、数据清洗等固定逻辑,避免把复杂查询散落在应用代码里。和函数不同,存储过程可以返回多个结果集,也能执行事务控制,但不支持在SELECT中直接当作表达式调用。

一、创建存储过程的基本语法与实战示例
在MySQL客户端中写存储过程,最先要处理的是语句结束符冲突。默认分号会被客户端当作单条SQL结束,而过程体内部本身就有很多分号,所以必须用DELIMITER临时把结束符换成其他字符,比如//或$$。过程定义完后再把结束符还原,否则后续命令都会报错。
创建时使用CREATE PROCEDURE语句,后面跟过程名和参数列表。参数需要声明输入输出方向:IN是传入值,OUT是返回给调用方的值,INOUT既可传入也能改完传回。过程体放在BEGIN和END之间,里面可以写任意合法SQL,包括变量声明、条件判断和游标循环。
下面示例创建一个按部门统计人数的过程,传入部门编号,传出人数和平均薪资:
DELIMITER //
CREATE PROCEDURE count_dept(
IN dept_id INT,
OUT emp_count INT,
OUT avg_sal DECIMAL(10,2)
)
BEGIN
SELECT COUNT(*), AVG(salary)
INTO emp_count, avg_sal
FROM employee
WHERE department_id = dept_id;
END //
DELIMITER ;
调用时用CALL指令,并且要用用户变量接收OUT参数:CALL count_dept(10, @c, @a);随后SELECT @c, @a;就能看到结果。需要注意,过程名在数据库内不唯一区分大小写,但Linux下文件系统敏感时可能引发奇怪问题,建议统一小写命名。
二、查看已存在存储过程的定义与状态
当团队接手旧库或排查慢查询时,经常要翻看某个过程到底写了什么。MySQL把存储过程元信息放在information_schema.ROUTINES表里,而具体定义文本在information_schema.ROUTINES的ROUTINE_DEFINITION字段中,不过普通账号可能看不到完整正文,此时用SHOW CREATE PROCEDURE更可靠。
如果只是想列出当前库有哪些过程,可以用SHOW PROCEDURE STATUS,它能按数据库名、名字模糊匹配。加上WHERE Db='test'就能只看某个库。该指令还会显示创建时间、修改时间和字符集,对审计很有用。
以下代码展示三种常见查看方式:
-- 查看定义原文 SHOW CREATE PROCEDURE count_dept; -- 查看当前库所有过程 SHOW PROCEDURE STATUS WHERE Db = DATABASE(); -- 通过系统表查询备注和参数 SELECT ROUTINE_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' AND ROUTINE_SCHEMA = 'test';
需要提醒的是,ROUTINE_DEFINITION里看到的是去格式化后的文本,注释可能丢失。若过程是用SQL SECURITY INVOKER定义的,调用者权限不足时会看不到内容,这是安全设计而非故障。
三、修改与删除存储过程的正确姿势
MySQL没有提供类似ALTER PROCEDURE改过程体的语法,官方只允许用ALTER PROCEDURE修改少量特性,比如COMMENT、SQL SECURITY或语言设置,不能改BEGIN...END里的逻辑。因此真正改业务逻辑时,标准做法是先DROP再CREATE。
删除用DROP PROCEDURE,建议加上IF EXISTS避免过程不存在时直接报错中断脚本。删除后依赖它的应用调用会失败,所以在生产环境应先用SHOW CREATE PROCEDURE备份定义,再挑低峰期操作,并通知调用方。
下面示例演示安全替换一个过程:先删后建,并在建之前改结束符:
DROP PROCEDURE IF EXISTS count_dept;
DELIMITER //
CREATE PROCEDURE count_dept(
IN dept_id INT,
OUT emp_count INT
)
BEGIN
SELECT COUNT(*)
INTO emp_count
FROM employee
WHERE department_id = dept_id;
END //
DELIMITER ;
如果只想补个注释或改安全上下文,可以用ALTER PROCEDURE count_dept COMMENT '统计部门人数' SQL SECURITY DEFINER;。这种轻量修改不会锁表,也不会影响正在跑的调用。对比来看,删建方式更彻底但风险高,alter方式安全却能力有限,应按场景选择。
四、权限管理与常见错误排查
存储过程的执行权限和定义权限是分开的。执行需要EXECUTE权限,创建和删除则需要CREATE ROUTINE和ALTER ROUTINE。用GRANT EXECUTE ON PROCEDURE test.count_dept TO 'app'@'%';即可放行调用。若过程内部访问了调用者无权查看的表,且过程定义为DEFINER属主有权,则仍能跑通,这是常见权限绕行点。
新手常犯的错误包括:忘记改DELIMITER导致语法报错;OUT参数在调用时没传用户变量而传了常量;在Navicat等工具里直接右键修改却没勾选重建导致旧逻辑残留。还有把存储过程写成超大事务,造成锁等待雪崩。
遇到ERROR 1305 PROCEDURE does not exist时,先确认当前DATABASE()是否选对,再查SHOW PROCEDURE STATUS确认名字拼写。若是主从复制环境,过程默认不会同步到从库,除非开启binlog且用CREATE PROCEDURE语句级记录,否则从库缺失过程会引发调用异常。
MySQL存储过程Stored_Procedure修改时间:2026-08-15 20:10:32