mysql 5.7不支持with recursive,需用自定义函数+find_in_set实现递归查询,但必须解决group_concat截断(默认1024字符)、参数类型不匹配(需显式cast转字符串)、权限限制(error 1418需设log_bin_trust_function_creators=1)三大问题。

MySQL 5.7 及更早版本不支持 WITH RECURSIVE,遇到多级菜单、组织架构、分类目录这类树状结构时,最直接可用的方案就是自定义函数 + FIND_IN_SET 组合。但这个方案不是“写完就能用”,必须处理好截断、空值、类型匹配和权限三类硬伤。
为什么 GROUP_CONCAT 返回值会被截断?
函数内部依赖 GROUP_CONCAT 拼接所有子节点 ID,而 MySQL 默认的 group_concat_max_len 是 1024 字符。一旦子节点超过几十个,ID 列表就会被无声截断,查询结果漏数据——且无任何报错提示。
- 执行
SET SESSION group_concat_max_len = 50000;仅对当前会话生效,函数内不继承该设置 - 必须在创建函数前全局设置:
SET GLOBAL group_concat_max_len = 50000; - 若无 SUPER 权限,会报错
ERROR 1238 (HY000): Variable 'group_concat_max_len' is a read only variable - Linux 下可永久生效:在
my.cnf的[mysqld]段添加group_concat_max_len = 50000,重启 mysqld
get_child_menus 函数里 pid 和 id 类型不一致会怎样?
常见错误是表中 id 为 BIGINT,但函数参数声明为 VARCHAR(64),导致 FIND_IN_SET(pid, tempids) 中类型隐式转换失败——FIND_IN_SET 要求两个参数都是字符串,且 tempids 必须是逗号分隔的纯数字字符串(如 '1,2,3'),不能带空格或引号。
- 若
pid字段是INT或BIGINT,函数参数必须用CAST(pid AS CHAR)显式转字符串,否则部分匹配失效 - 初始化
tempids时别写成SET tempids = CONCAT('', in_pid);,直接SET tempids = in_pid;更安全 - 测试时用
SELECT get_child_menus(1);看返回值是否为完整逗号串,而不是NULL或空字符串
创建函数失败:提示 ERROR 1418 怎么办?
这是 MySQL 安全限制触发的典型报错:This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration。本质是函数可能修改数据或依赖外部状态,服务端拒绝创建。
- 最简解法:执行
SET GLOBAL log_bin_trust_function_creators = 1;(需 SUPER 权限) - Windows 修改
my.ini,Linux 修改my.cnf,在[mysqld]下加一行log-bin-trust-function-creators=1,然后重启 MySQL - 函数定义里显式加
READS SQL DATA(推荐):CREATE FUNCTION ... RETURNS ... READS SQL DATA BEGIN ... END - 注意:开启该选项后,函数仍不可用于复制环境中的主库,除非你确认函数是幂等且确定性的
真正麻烦的不是写函数,而是验证它在真实数据量下是否稳定——比如 5 层深、每层平均 20 个子节点,最终拼出的字符串长度可能超 10KB;而 FIND_IN_SET 在长字符串里逐个匹配效率线性下降。如果层级更深或并发高,建议优先升级到 MySQL 8.0 使用 WITH RECURSIVE,而非硬扛函数方案。











