row_number()不能用于connect by排序,因其全局排序会破坏树结构;应使用order siblings by控制同级顺序;需层级编号时用level和sys_connect_by_path构造路径;真需行号须在外层查询套用row_number()并保留order siblings by。

Oracle中ROW_NUMBER()不能直接用于CONNECT BY结果集排序
直接在CONNECT BY查询里套ROW_NUMBER() OVER (ORDER BY ...)会报错或逻辑错乱,因为ROW_NUMBER()是窗口函数,它按最终结果集整体排序,而树形遍历需要的是「同级节点内有序」+「跨层级保持父子顺序」。强行用ORDER BY全局排序(比如ORDER BY level, id)会打乱树结构——子节点可能被排到其他分支后面。
ORDER SIBLINGS BY才是控制同级顺序的正确方式
ORDER SIBLINGS BY专为树形结构设计,只影响同一父节点下的直接子节点顺序,不影响层级关系本身。它必须紧跟在CONNECT BY之后、WHERE之前,且不能和全局ORDER BY混用。
-
START WITH pid = '0'定义根节点 -
CONNECT BY PRIOR id = pid表示自上而下找子节点 -
ORDER SIBLINGS BY sort_no, id让每个父节点下的子节点按sort_no升序,id兜底
示例:
SELECT id, name, pid, LEVEL FROM t_menu START WITH pid = '0' CONNECT BY PRIOR id = pid ORDER SIBLINGS BY sort_no, id;
需要全局唯一编号时,用LEVEL + SYS_CONNECT_BY_PATH拼路径再计算
如果真要给每个节点分配一个类似“1.2.3”这样的层级编号(不是单纯行号),ROW_NUMBER()依然不适用。得靠LEVEL伪列和SYS_CONNECT_BY_PATH构造可读路径:
-
SYS_CONNECT_BY_PATH(id, '.') AS path生成如.1.3.7这样的字符串路径 - 用
LPAD配合LEVEL做缩进标识:LPAD(' ', (LEVEL-1)*2) || name - 若需数字型层级码(如10102),需额外用
REGEXP_SUBSTR提取并拼接,但性能较差,慎用于大数据量
简单示意路径编号:
SELECT
LEVEL,
SYS_CONNECT_BY_PATH(id, '.') AS full_path,
LPAD(' ', (LEVEL-1)*2) || name AS indented_name
FROM t_menu
START WITH pid = '0'
CONNECT BY PRIOR id = pid
ORDER SIBLINGS BY sort_no;
真正需要行号场景:先树形查出结果,再外层套ROW_NUMBER()
只有当业务明确要求「把整棵树扁平化后从1开始连续编号」(比如导出Excel第1行=根,第2行=第一个子节点……),才考虑在外层查询加ROW_NUMBER()。但要注意:
- 必须保留
ORDER SIBLINGS BY在内层,否则编号顺序无意义 - 不能在
CONNECT BY子句里写WHERE剪枝后再套ROW_NUMBER(),否则编号会跳空 - 大数据量时性能下降明显,因需两轮扫描
示例(仅限小数据量或导出需求):
SELECT
ROW_NUMBER() OVER (ORDER BY sort_order) AS rn,
id, name, pid, lvl
FROM (
SELECT
id, name, pid, LEVEL AS lvl,
SYS_CONNECT_BY_PATH(sort_no, '.') AS sort_order
FROM t_menu
START WITH pid = '0'
CONNECT BY PRIOR id = pid
ORDER SIBLINGS BY sort_no, id
);
层级编号本质是树遍历顺序问题,不是窗口函数能解决的。最易踩的坑就是把ROW_NUMBER()当成ORDER SIBLINGS BY的替代品——它们解决的是完全不同的排序维度。











