DETERMINISTIC必须显式声明,否则MySQL默认视为NOT DETERMINISTIC,导致函数无法用于索引列、分区表达式等场景;函数体内禁止SELECT基表,否则引发N次独立查询,应改用JOIN预加载。

DETERMINISTIC必须显式声明,否则MySQL默认视为NOT DETERMINISTIC
不加 DETERMINISTIC 声明的函数,MySQL 会禁止它出现在索引列、分区表达式、函数索引等关键位置。比如你写了个时间格式化函数想用在 WHERE date_str = format_date(create_time),结果发现根本走不了索引——八成是漏了这个声明。
常见错误现象:ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration
- 只要函数逻辑不依赖
NOW()、USER()、系统变量、或任何外部状态,就该加上DETERMINISTIC - 哪怕只调用一次
RAND()或UUID(),也必须改用NOT DETERMINISTIC,且不能用于索引场景 - 声明位置必须紧接在
RETURNS后、函数体前,顺序不能错:RETURNS VARCHAR(255) DETERMINISTIC
函数体内禁止SELECT基表,否则每行触发一次查询
这是最隐蔽也最致命的性能陷阱。例如写一个根据 user_id 查昵称的函数,如果函数里直接 SELECT nickname FROM users WHERE id = input_id,那在 SELECT *, get_user_nickname(user_id) FROM orders 中,每条订单都会触发一次独立查询,N 条订单就是 N 次全表扫描(或索引查找)。
- 正确做法:把关联逻辑上提到 SQL 层,用
JOIN预加载;函数只做纯计算或查极小维度表(如状态码映射表) - 查缓存表可以接受,但必须确保该表行数
- 若真需封装查询,优先考虑视图或 CTE,它们能被优化器整体分析并下推条件
参数类型与字段类型必须严格一致,否则隐式转换废掉索引
假设业务表中有个字段是 status_code TINYINT UNSIGNED,你却定义函数参数为 INT 或 VARCHAR(2),MySQL 就会在调用时自动做类型转换,导致该列上的索引完全失效。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 查表字段类型用
DESCRIBE table_name或SHOW COLUMNS FROM table_name - 函数参数类型必须一字不差匹配:包括是否
UNSIGNED、长度(如VARCHAR(32)不等于VARCHAR(64))、是否允许NULL - 字符串类参数慎用
TEXT,它无法参与索引比较;优先选VARCHAR(n)并设合理上限
真正适合用DETERMINISTIC函数的只有四类轻量场景
别被“函数能复用逻辑”带偏了——绝大多数所谓“提升效率”的函数,实际让查询更慢。只有以下四类才值得封装:
- 格式化输出:如
FROM_UNIXTIME(ts, '%Y-%m-%d')封装成fmt_date_ms,避免 SQL 里重复写格式串 - 状态码映射:如输入
TINYINT状态值,返回固定长度中文描述(CASE WHEN写在函数里比写在每个 SELECT 中更可控) - 简单规则判断:如
is_eligible_for_free_shipping(amount DECIMAL(10,2))返回TINYINT,逻辑只是amount >= 99.00 - 权限辅助:如查
user_role_permissions这种百行以内的配置表,判断当前用户对某资源 ID 是否有读权限
复杂计算、多表 JOIN、子查询、动态拼接、返回集合——这些统统不该放进函数。它们破坏执行计划可见性,让优化器无法下推、合并或并行,最终拖垮整个查询。










