首页/数据/数据库速查/存储过程与存储函数基础
SQL 语法52 条

存储过程与存储函数基础

把逻辑放在数据库侧:封装批量作业、减少网络往返;但调试、版本管理与移植性较差,业务逻辑仍建议放应用层。

数据库速查数据更新于 2026-10-13

存储过程与函数是什么

存储过程与存储函数把一段 SQL 逻辑存放在服务端,由客户端用 CALL 或表达式中调用。适合的场景是批量作业与数据迁移:一次调用执行多步语句,省掉多次网络往返,也便于把调度入口固定下来。代价同样明确:调试只能靠行号与自定义日志、版本管理与代码评审困难、在不同数据库之间几乎不可移植,所以核心业务逻辑仍建议留在应用层。

基本信息

创建过程CREATE PROCEDURE 名(IN/OUT/INOUT 参数 ...) BEGIN ... END
创建函数CREATE FUNCTION 名(参数) RETURNS 类型 ... 需声明 DETERMINISTIC 等特性
调用CALL 过程名(参数);函数直接在 SELECT 中使用
分隔符定义体前用 DELIMITER 临时换分隔符,避免分号提前截断
查看与删除SHOW CREATE PROCEDURE / SHOW PROCEDURE STATUS;DROP PROCEDURE

典型使用场景

  • 批量数据处理:按天数归档、批量状态流转这类多步操作用一次调用完成
  • 数据迁移辅助:把数据清洗与搬移逻辑固化在库里,运维直接调用而非手工执行长脚本
  • 权限收敛执行入口:只授予 EXECUTE 权限,避免开放底层表的直接写权限
  • 回归脚本固化:把数据准备步骤封装成过程,测试环境一键重建数据
  • 定时任务入口:事件调度或外部调度器只调用一个过程名,减少脚本维护成本

定义与调用

定义与调用
-- 必须临时改分隔符,否则过程体里的分号会提前结束定义
DELIMITER $$
CREATE PROCEDURE sp_close_orders(IN p_days INT)
BEGIN
  UPDATE orders SET status = 'closed'
  WHERE status = 'pending' AND created_at < NOW() - INTERVAL p_days DAY;
  SELECT ROW_COUNT() AS affected;   -- 返回受影响行数便于核对
END$$
DELIMITER ;
CALL sp_close_orders(30);
-- 查看与管理
SHOW CREATE PROCEDURE sp_close_orders\G
DROP PROCEDURE sp_close_orders;

过程与函数对比

维度存储过程存储函数
调用方式CALL 名(参数)在 SQL 表达式中调用
返回值通过 OUT 参数或多个结果集必须 RETURN 一个值
可用语句支持事务控制与多语句受限,不能随意提交事务
创建特性无额外要求需声明 DETERMINISTIC 等,否则复制环境可能报错
典型用途批量作业入口字段计算与转换
创建时忘记换分隔符是最常见的新手错误:过程体遇到第一个分号就被截断,报语法错误。定义前先执行 DELIMITER $$,定义结束再换回分号。
带 Binlog 的复制环境中创建存储函数需要正确声明特性(如 DETERMINISTIC、READS SQL DATA 等),否则在主库创建可能直接失败。涉及复制的环境要在主库先验证创建语句。

易错点

  • 定义过程体时不改 DELIMITER:分号提前截断定义,报语法错误且提示位置容易误导
  • 把核心业务逻辑塞进存储过程:调试只能靠行号与自定义日志,缺少单元测试与版本管理,后续维护成本远高于应用层实现
  • 在复制环境创建函数不声明确定性特性:主库报错或从库回放异常,创建语句必须在主库先验证
  • 忘记处理异常:过程内部某步失败后继续执行后续语句,产生半完成的数据状态,应在过程中加入错误处理与回滚逻辑
  • 授予执行者 EXECUTE 权限而忽略 SQL SECURITY 语义:过程默认按定义者权限运行,权限被间接放大,需要单独审计

数据来源与参考

  • MySQL 8.0 Reference Manual: Stored Programs and Views

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌