方法一:使用 CONNECT BY(传统方式)
-- 假设表名为 my_table,字段为 id 和 parent_id
SELECT id
FROM my_table
START WITH id = '根节点ID' -- 从根节点开始
CONNECT BY PRIOR id = parent_id -- 递归条件:父节点的 id = 子节点的 parent_id
MINUS -- 去重(可选)
-- 或者使用 NOT EXISTS 来筛选叶子节点
SELECT id
FROM my_table t
WHERE NOT EXISTS (
SELECT 1
FROM my_table t2
WHERE t2.parent_id = t.id
)
START WITH id = '根节点ID'
CONNECT BY PRIOR id = parent_id;
方法二:使用 WITH 递归(推荐,更标准)
WITH RECURSIVE cte AS (
-- 基础查询:从根节点开始
SELECT id, parent_id, 1 as level
FROM my_table
WHERE id = '根节点ID' -- 替换为实际的根节点ID
UNION ALL
-- 递归查询:查找所有子节点
SELECT t.id, t.parent_id, c.level + 1
FROM my_table t
INNER JOIN cte c ON t.parent_id = c.id
)
-- 筛选叶子节点(没有子节点的节点)
SELECT c.id
FROM cte c
WHERE NOT EXISTS (
SELECT 1
FROM my_table t
WHERE t.parent_id = c.id
);
方法三:一步到位(最简洁)
WITH cte AS (
SELECT id, parent_id
FROM my_table
START WITH id = '根节点ID'
CONNECT BY PRIOR id = parent_id
)
SELECT id
FROM cte c
WHERE NOT EXISTS (
SELECT 1 FROM my_table WHERE parent_id = c.id
);
示例
假设数据如下:
id | parent_id
----|----------
A | NULL (根)
B | A
C | A
D | B
E | B
查询根节点 A 的所有叶子节点:
SELECT id
FROM my_table
START WITH id = 'A'
CONNECT BY PRIOR id = parent_id
MINUS
SELECT id
FROM my_table
WHERE parent_id IS NOT NULL;
结果:C, D, E
性能优化建议
- 创建索引:
CREATE INDEX idx_parent_id ON my_table(parent_id);
CREATE INDEX idx_id ON my_table(id);
- 如果数据量大,考虑使用 NO_CYCLE:
START WITH id = '根节点ID'
CONNECT BY NOCYCLE PRIOR id = parent_id