MySQL创建存储过程需先用DELIMITER声明分隔符,再用CREATE PROCEDURE定义,参数须标明IN/OUT/INOUT,过程体用BEGIN...END包裹;调用含OUT参数的过程前必须先声明用户变量,如SET @var=0,再CALL传入@var,最后SELECT @var获取结果。
MySQL 中 CREATE PROCEDURE 的基本写法
直接上手写存储过程,第一关是语法结构不能错。mysql 要求必须显式声明分隔符(delimiter),否则遇到第一个 ; 就会提前结束语句,报错 error 1064。
常见错误现象:ERROR 1064 (42000): You have an error in your SQL syntax,往往就卡在这儿。
- 先执行
DELIMITER $$(或任意非分号符号),再写CREATE PROCEDURE - 过程体必须用
BEGIN ... END包裹,哪怕只有一条语句 - 参数列表里每个参数必须带方向关键字:
IN、OUT或INOUT,不写默认是IN - 过程名不能和已有的函数/表同名,且不支持 schema 限定(如
mydb.proc_name)
DELIMITER $$ CREATE PROCEDURE get_user_count(IN dept_id INT, OUT total INT) BEGIN SELECT COUNT(*) INTO total FROM users WHERE department_id = dept_id; END$$ DELIMITER ;
IN 和 OUT 参数的实际行为差异
别被名字误导:IN 是传值进过程,OUT 是“只写”输出容器——调用前即使赋了值也会被忽略;而 INOUT 才是真正可读可写的引用式参数。
使用场景:批量处理时常用 OUT 返回统计结果,或用 INOUT 做中间状态累积(比如递归计算中的计数器)。
-
IN参数在过程内修改,不影响外部变量 -
OUT参数在过程内未赋值,调用后得到NULL(不是初始值) -
INOUT参数必须传变量名(不能传字面量),否则报错ERROR 1318 - 所有参数都是按值传递的语义,MySQL 没有真正的引用传递
调用带 OUT 参数的过程时容易漏掉的步骤
很多人写完过程就直接 CALL proc_name(123),忘了 OUT 参数需要先声明用户变量,再用 @var_name 传入。
常见错误现象:ERROR 1318 (42000): Incorrect parameter count to procedure,其实是参数类型不匹配(字面量 vs 变量)。
- 必须先用
SET @out_var = 0或类似方式初始化变量 -
CALL里传的是变量名,不是表达式(CALL p(@a)✅,CALL p(@a + 1)❌) - 调用后查结果得用
SELECT @out_var,不能在CALL后直接用SELECT total - 用户变量作用域是 session 级,跨连接不可见
SET @cnt = 0; CALL get_user_count(5, @cnt); SELECT @cnt AS result;
性能与维护上容易被忽略的细节
存储过程不是银弹。它在复杂事务中能减少网络往返,但调试困难、版本难管理、上线需 DBA 权限,这些现实约束比语法更影响落地。
- 过程体里尽量避免大结果集返回(
SELECT无INTO),否则客户端可能收不到OUT值 - ALTER PROCEDURE 不支持修改参数列表,改参数就得
DROP + CREATE - MySQL 8.0+ 支持
CREATE OR REPLACE PROCEDURE,但低版本仍得手动处理 - 过程内调用其他过程或函数时,权限是叠加检查的,容易因权限不足静默失败
最常被绕过的点:没在事务里包裹写操作,又没加 ROLLBACK 逻辑,导致部分成功、状态不一致。这不是语法问题,但每次重试都踩一遍。

















