递归查询必须使用 WITH RECURSIVE,普通 CTE 不支持;各数据库语法差异大,MySQL 8.0 需显式设递归深度并用 COALESCE 防 NULL 截断路径,SQL Server 的 hierarchyid 高效但不通用。

递归查询必须用 WITH RECURSIVE,普通 CTE 不行
PostgreSQL、SQL Server、Oracle、SQLite 3.8.3+ 和 MySQL 8.0+ 都支持递归 CTE,但语法和限制差异大。核心是 WITH RECURSIVE 关键字——漏掉 RECURSIVE 会导致语法错误(如 PostgreSQL 报错 recursive query "t" does not have a recursive term)。MySQL 8.0 要求递归必须有终止条件(比如 level ),否则可能无限循环或被 cte_max_recursion_depth 截断。
路径拼接要用字符串连接 + COALESCE 处理根节点
组织架构中根节点的 parent_id 通常为 NULL 或 0,直接用 parent_path || '/' || name 会因 NULL 导致整条路径变 NULL。必须用 COALESCE 或 CONCAT(MySQL)兜底:
SELECT id, name, CAST(name AS VARCHAR(500)) AS path
FROM orgs WHERE parent_id IS NULL
UNION ALL
SELECT o.id, o.name,
COALESCE(t.path || ' / ', '') || o.name AS path
FROM orgs o
INNER JOIN t ON o.parent_id = t.id
注意:CAST 初始路径为足够长的字符串类型,避免后续拼接时被截断(尤其 SQL Server 对 VARCHAR 默认长度敏感)。
MySQL 8.0 需显式设递归深度,且不支持反向路径生成
MySQL 默认 cte_max_recursion_depth = 100,若组织层级超深(如 200 层),查不到完整路径。需在语句前加:
SET SESSION cte_max_recursion_depth = 500;
另外,MySQL 的 CONCAT 在遇到 NULL 时返回 NULL,不能像 PostgreSQL 那样靠 || 自动跳过;必须写成:
CONCAT(COALESCE(t.path, ''), ' / ', o.name)
如果要从叶子节点向上拼路径(比如“技术部 / 后端组 / Java 小组”),MySQL 不支持从下往上递归(无类似 CONNECT BY PRIOR 的反向引用),只能先查出所有祖先再聚合,或改用应用层处理。
SQL Server 的 hierarchyid 虽快但不通用
SQL Server 提供 hierarchyid 类型,能高效存储和查询树形结构,/1/2/3/ 这种路径可直接用 .ToString() 获取,性能远超递归 CTE。但它只存在于 SQL Server,迁移成本高;而且一旦用它,就不能再用标准 SQL 的 WITH RECURSIVE 写法——二者是互斥方案。如果团队未来可能换数据库,优先用标准递归 CTE,哪怕慢一点。
路径长度和层级深度容易被低估,尤其是测试数据只有 3 层,上线后发现某部门嵌套了 12 层,cte_max_recursion_depth 或字段长度不够就直接报错或截断。留余量比事后排查更省时间。










