声明 reads sql data 必须紧接 returns 后、begin 前,不可换行或加注释;函数体内仅允许确定性 select,禁用 now()、rand()、临时表、未声明的 udf 等;若 log_bin_trust_function_creators=off,还需确保用户具备 create routine 和 execute 权限。

声明 READS SQL DATA 依然报错,大概率不是声明写错了,而是没写对位置、没写全、或者函数体里偷偷干了它不允许的事。
READS SQL DATA 必须紧贴在 RETURNS 后面
MySQL 对语法顺序极其敏感:READS SQL DATA(以及 DETERMINISTIC、NO SQL)必须直接跟在 RETURNS 子句之后、BEGIN 之前,中间不能换行、不能加注释、不能漏掉分号(如果用了分隔符)。
错误写法示例:
CREATE FUNCTION get_user_name(uid INT) RETURNS VARCHAR(50) -- 这里加了注释或空行就可能触发报错 READS SQL DATA BEGIN DECLARE name VARCHAR(50); SELECT username INTO name FROM users WHERE id = uid; RETURN name; END;
正确写法(紧凑无空行):
CREATE FUNCTION get_user_name(uid INT) RETURNS VARCHAR(50) READS SQL DATA BEGIN DECLARE name VARCHAR(50); SELECT username INTO name FROM users WHERE id = uid; RETURN name; END;
- 如果用了
DELIMITER,确保READS SQL DATA在RETURNS同一行或下一行紧邻,不被注释/空行隔开 - MySQL 8.0+ 对换行容忍度更低,老版本可能“凑合过”,新版本直接拒绝
函数体里调用了非 READS SQL DATA 允许的操作
READS SQL DATA 的契约很窄:只允许 SELECT,禁止任何写操作、禁止调用会修改状态的函数(如 NOW()、RAND()、UUID())、禁止访问用户变量或临时表——哪怕只是读,也可能越界。
常见踩坑点:
- 用了
SELECT ... INTO但目标表是临时表或未显式声明为只读 - 函数里嵌套调用了另一个没声明或声明为
MODIFIES SQL DATA的函数 - 写了
SELECT NOW() AS ts——NOW()是非确定性函数,违反READS SQL DATA的隐含前提(结果应可复现) - 查询中用了子查询,而子查询里包含
ORDER BY ... LIMIT 1且没加ORDER BY确保稳定性,MySQL 可能判定行为不可控
log_bin_trust_function_creators=OFF 时,声明本身会被忽略
这个变量是“开关”:当它为 OFF(默认值),MySQL 根本不信任你写的声明,而是强制要求你必须同时满足两个条件:
- 显式声明三者之一(
DETERMINISTIC/NO SQL/READS SQL DATA) - 且当前用户拥有
CREATE ROUTINE权限 +EXECUTE权限
如果你声明了 READS SQL DATA,但权限不足,或者 log_bin_trust_function_creators 是 OFF 且你没用高权限账号创建,MySQL 仍会报错,且错误信息不会提示权限问题,只重复 “none of DETERMINISTIC…”。
验证方式:
SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
若返回 OFF,临时修复命令是:
SET GLOBAL log_bin_trust_function_creators = 1;
但注意:这绕过了安全校验,仅适合开发环境;生产环境应优先补全声明+检查权限,而不是开这个开关。
函数里用了未声明的外部依赖
MySQL 不会静态分析函数体是否真“只读”,但它会检查是否引用了不允许的东西。例如:
- 查询了视图,而该视图底层用了
UNION ALL+ 非确定性函数 - SELECT 中用了自定义函数(UDF),而那个 UDF 没声明或声明为
MODIFIES SQL DATA - 用了
INFORMATION_SCHEMA表——某些版本认为这是“元数据读取”,不完全等同于READS SQL DATA所指的用户数据
最稳妥的做法:把函数拆成最小单元,逐个确认每个 SELECT 是否真的只查固定表、无函数调用、无变量、无动态 SQL(PREPARE 不被允许)。
真正容易被忽略的是:MySQL 把“确定性”和“只读”绑得太死——哪怕你只读一张表,只要用了 RAND() 或 SYSDATE(),它就拒绝承认你是 READS SQL DATA;而一旦你改成 DETERMINISTIC,又得保证每次输入相同参数必得相同结果,否则主从复制就出鬼。











