MySQL 8.0 函数仅支持返回单值,禁止返回结果集;须设 log_bin_trust_function_creators=1 并显式声明特性(如 DETERMINISTIC READS SQL DATA),函数内 SELECT 必须 INTO 变量,调用时易引发性能问题,复杂逻辑应改用 JOIN 或存储过程。

MySQL 8.0 中不能直接创建返回结果集的函数,只能返回单个标量值;想“优化”,首先要避开语法和权限两大雷区。
CREATE FUNCTION 语句必须带 characteristic 且 log_bin_trust_function_creators=1
MySQL 8.0 默认禁止创建含 SQL 查询的自定义函数,否则会报错 ERROR 1418。这不是语法错,是安全限制。
- 执行前必须先运行:
SET GLOBAL log_bin_trust_function_creators = 1(需 SUPER 权限) -
CREATE FUNCTION必须显式声明特性,比如READS SQL DATA或DETERMINISTIC,不能省略 - 常见组合:
DETERMINISTIC READS SQL DATA(确定性 + 读数据),或NOT DETERMINISTIC READS SQL DATA(非确定性 + 读数据) - 如果函数只做计算(如
add_float),可只写DETERMINISTIC;含SELECT的必须加READS SQL DATA
函数体内不能有 SELECT ... INTO 变量以外的查询输出
MySQL 函数不支持返回多行或多列——它不是存储过程。任何试图在函数里执行 SELECT col FROM t 而不接 INTO 的写法都会报错 ERROR 1415。
- 正确写法:用
SELECT COUNT(*) INTO @cnt FROM employees,再RETURN @cnt - 错误写法:
SELECT salary FROM employees WHERE last_name = ename(没INTO,也没RETURN包裹) - 若要查多个字段或多行,必须改用存储过程(
CREATE PROCEDURE),或把逻辑拆成多个单值函数 - 函数内声明变量用
DECLARE var_name TYPE DEFAULT ...,不能用@var用户变量(除非明确赋值并用于 RETURN)
调用方式与性能陷阱:函数被隐式执行多次
函数在 SELECT 中每行调用一次,如果没索引或逻辑复杂,极易拖慢整个查询——它不像视图或 CTE 那样能被优化器重写。
- 例如:
SELECT name, get_salary_by_name(name) FROM employees→ 对每一行都执行一次函数体内的SELECT - 避免在大表的
WHERE或ORDER BY中调用函数,尤其是含子查询的函数 - 如果函数只是封装简单计算(如日期差、字符串截取),影响小;但凡涉及表连接或聚合,优先考虑改写为 JOIN 或子查询
- 用
SHOW FUNCTION STATUS LIKE 'func_name'查看函数定义,确认是否真被缓存(DETERMINISTIC函数可能被优化器缓存,NOT DETERMINISTIC不会)
删除和修改函数必须用 DROP FUNCTION + 重新 CREATE
MySQL 不支持 ALTER FUNCTION,改逻辑只能删了重建,且要注意依赖关系。
- 安全删除:
DROP FUNCTION IF EXISTS get_count - 重建前务必确认函数没被视图、触发器或其它存储过程引用(否则会失效但不报错)
- 修改函数时,别忘了同步更新所有调用它的 SQL —— 比如参数名变更后,
SELECT get_count()会报ERROR 1370(权限不足)或参数不匹配 - 生产环境建议先在测试库验证函数行为,尤其注意 NULL 输入时的返回值(
SELECT salary FROM employees WHERE last_name = NULL返回空集,RETURN就是NULL)
真正卡住人的从来不是语法本身,而是函数被当成“万能胶”去封装复杂查询——它天生不适合替代 JOIN 或子查询。写之前先问一句:这个值,能不能用一条 SQL 直接算出来?


















