函数必须返回标量值且仅能嵌入select/where等表达式中,不可执行dml或事务;存储过程通过call调用,支持out参数、事务及复杂流程控制,适合封装多步业务逻辑。

函数适合嵌入查询做实时计算,过程适合封装带事务的多步操作;选错会导致语法报错、权限拒绝或主从不一致。
函数必须返回值且只能用在 SELECT / WHERE 里
MySQL 函数本质是“表达式求值单元”,调用时必须作为 SQL 表达式的一部分。比如 SELECT id, calc_tax(amount, rate) AS tax FROM orders 合法,但 CALL calc_tax(100, 0.1) 会直接报错 ERROR 1064。
- 函数声明必须含
RETURNS类型,且体内只能有RETURN一条返回语句 - 不能执行
INSERT/UPDATE/DELETE(除非开启log_bin_trust_function_creators=1并声明为DETERMINISTIC) - 不能含
COMMIT或ROLLBACK,否则复制环境可能丢事务、CDC 捕获失败 - 常见误用:把用户积分更新逻辑写成函数——这违反了函数“只读+纯计算”的语义,也绕过了 binlog 完整性保障
存储过程支持 OUT 参数和事务,但不能出现在 SQL 表达式中
存储过程是“动作执行单元”,设计目标就是干事情,比如转账、日志归档、批量同步。它用 CALL 独立执行,靠 OUT 或 INOUT 参数传出结果,甚至能用 SELECT 返回结果集。
- 支持完整控制流:
IF、WHILE、CURSOR,也能显式开事务、捕获SQLEXCEPTION - 参数可声明为
OUT user_id INT,调用后通过SELECT @user_id获取值 - 禁止在
SELECT列表或WHERE中调用,例如SELECT *, CALL sp_log_action(id)语法非法 - 注意:MySQL 9.6.0 起,外键与级联操作已上移至 SQL 层,存储过程中涉及外键变更会完整记录 binlog,但旧版本仍需警惕隐式提交风险
实际生产中容易踩的坑
最常被忽略的是 binlog 和权限链路问题。MySQL 9.6.0 强化了 CDC 一致性,但函数若未声明 READS SQL DATA 或误标 DETERMINISTIC,主库执行成功、从库可能因 binlog_format=STATEMENT 失败回滚;而存储过程若含非确定性操作(如 NOW() + INSERT),在 row 格式下虽安全,却会让审计日志无法还原原始意图。
- 函数被用于触发器中?危险!触发器内调用函数若含子查询,可能引发锁等待放大
- 过程里用
SELECT ... INTO赋值但没处理NOT FOUND,后续语句会静默跳过,查不到数据也不报错 - 跨库调用函数时,
DEFINER用户权限不足,函数执行报ERROR 1449,但错误日志里只显示“user not found” - 别指望函数能替代应用层缓存——每次调用都重新计算,不如在应用里查一次、复用多次
真正关键的不是语法能不能写出来,而是这个逻辑是否该由数据库承担。函数越轻量、越无副作用,越安全;过程越靠近业务边界、越需要原子性,越值得存在。一旦发现要给函数加 UPDATE、给过程加 RETURN,就该停下来重画边界了。











