MySQL自定义函数必须三选一声明DETERMINISTIC、NO SQL或READS SQL DATA,根本原因是开启binlog后需保障主从复制一致性与查询优化安全;漏声明则报错1418,误标会导致缓存错误、复制不一致及函数索引失败。

MySQL自定义函数不是“必须声明 DETERMINISTIC”,而是必须三选一:显式声明 DETERMINISTIC、NO SQL 或 READS SQL DATA。漏掉任何一个,就会报错 This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration(错误 1418)。
为什么 MySQL 强制要求这三类声明?
根本原因不是语法洁癖,而是 binlog 开启后,MySQL 必须能判断这个函数在主从复制中是否安全可重放。如果函数行为不可知,主库执行返回 A,从库重放时可能返回 B——一致性就崩了。
这三类声明本质是「数据访问契约」,告诉优化器和复制模块:
• 函数会不会读表?
• 会不会改数据?
• 输出是不是只取决于输入?
-
DETERMINISTIC:相同参数 → 永远相同结果,且不依赖外部状态(如表数据、NOW()、RAND()、用户变量) -
NO SQL:连SELECT都不执行,纯计算(比如RETURN a * b + 10) -
READS SQL DATA:只SELECT,不INSERT/UPDATE/DELETE,但结果可能随表数据变化而变
把非确定函数错标为 DETERMINISTIC 的真实后果
MySQL 不校验你写的 DETERMINISTIC 是否属实——它全信你。这就导致静默故障:
- 优化器可能缓存第一次调用结果,后续调用直接返回旧值(比如查最新订单时间却始终返回创建函数那天的值)
- 主库执行
get_user_status(123)返回'active',从库因快照隔离或延迟看到已更新的行,返回'inactive',复制不一致 - 基于该函数建函数索引会失败,报错
Function is not deterministic,即使你写了声明
典型误标场景:SELECT status FROM users WHERE id = uid 类查询,哪怕加了 LIMIT 1,只要表数据可能被并发修改,就不能标 DETERMINISTIC。
声明位置和语法细节容易踩坑
声明必须紧接在 RETURNS 类型之后、BEGIN 之前,且只能写一个:
CREATE FUNCTION calc_tax(price DECIMAL(10,2)) RETURNS DECIMAL(5,2) DETERMINISTIC -- ✅ 正确位置 BEGIN RETURN price * 0.08; END;
常见错误:
- 漏写声明(最常见)
- 写成
DETERMINISTIC READS SQL DATA(两个都写 → 报错) - 把声明放在
BEGIN后面或RETURNS前面(语法错误) - 函数里调用了另一个没声明特性的自定义函数(依赖链断裂,整个函数视为非确定)
什么时候该用 NO SQL 或 READS SQL DATA?
选哪个,只看函数体实际干了什么:
- 完全不碰 SQL:比如
RETURN FLOOR(SQRT(a * a + b * b))→ 用NO SQL - 只查表、无写操作:比如
SELECT COUNT(*) FROM logs WHERE date = CURDATE()→ 用READS SQL DATA - 含
INSERT/UPDATE/DELETE?函数不允许,这种逻辑请改用存储过程
复杂点在于:哪怕只读一行,只要那行可能被其他事务修改,就不能标 DETERMINISTIC;而 READS SQL DATA 虽然允许读,但无法规避 MVCC 快照差异——这点常被忽略。


















