查找孩子的***父母,多个级别
Posted
技术标签:
【中文标题】查找孩子的***父母,多个级别【英文标题】:Find top parent of child, multiple levels 【发布时间】:2021-12-01 14:55:22 【问题描述】:ENTRY TABLE
__________________
| ID | PARENT_ID |
| 1 | null |
| 2 | 1 |
| 3 | 2 |
| 4 | null |
| 5 | 4 |
| 6 | 5 |
...
在某些情况下,我会复制这些条目,它们是通过父 ID 连接起来的。
每个条目可以有一个副本:
THIS WONT HAPPEN
__________________
| ID | PARENT_ID |
| 1 | null |
| 2 | 1 |
| 3 | 1 |
...
有时我需要复制并查询它的***父级。我需要找到我搜索的所有条目的***父条目。
例如,如果我查询 ID 6 和 3 的父母,我会得到 ID 4 和 1。 如果我查询 ID 5 和 2 的父母,我会得到 ID 4 和 1。 但如果我查询 ID 5 和 1,它应该返回 ID 4 和 1,因为条目 ID 1 已经是***父项本身。
我不知道从哪里开始,因为在这种情况下我不知道如何递归查询。
谁能指出我正确的方向?
我知道下面的查询只会返回子元素(ID 6 和 3),但老实说,我不知道从哪里开始。
顺便说一句,我正在使用 OracleSQL。
SELECT * FROM entry WHERE id IN (6, 3);
【问题讨论】:
见this。 您正在处理一个层次结构,特别是在您的情况下是一组非常特殊的树,其中每个节点最多有一个孩子。给定任意“节点”,您正在寻找树的“根”;这意味着您需要从下到上“遍历”,而不是从上到下。如果您对所有这些术语都不熟悉,您应该先阅读一些关于“有向树”的内容,以了解祖先和后代、根和叶等。然后使用CONNECT BY
分层子句实际上会非常明显。但不能跳过基础知识。
调查一下,谢谢
【参考方案1】:
您可以使用分层查询和CONNECT_BY_ROOT
。
要么从层次结构的根开始向下工作:
SELECT id,
CONNECT_BY_ROOT(id) AS root_id
FROM entry
WHERE id IN (6, 3)
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id;
或者,从入口到根目录:
SELECT CONNECT_BY_ROOT(id) AS id,
id AS root_id
FROM entry
WHERE parent_id IS NULL
START WITH id IN (6, 3)
CONNECT BY PRIOR parent_id = id;
其中,对于样本数据:
CREATE TABLE entry( id, parent_id ) AS
SELECT 1, NULL FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL UNION ALL
SELECT 5, 4 FROM DUAL UNION ALL
SELECT 6, 5 FROM DUAL UNION ALL
SELECT 7, 6 FROM DUAL
两个输出:
ID ROOT_ID 3 1 6 4
db小提琴here
【讨论】:
从根开始没有多大意义 - 为什么要检查不包含所需节点的树(在森林中)?唯一合乎逻辑的方法是从节点本身开始,“向上”工作以找到根(向上遍历时实际上是“叶子”)。 @mathguy 对于没有分支的 OP 数据,他们每棵树选择一个项目,那么它没有太大区别;但是,在一般情况下,如果是一片分支树的森林,那么我会同意你的观点并从项目开始并回到树的根部(我只是碰巧先写了另一个版本并编辑了另一个版本在后来我认为更合适的时候)。【参考方案2】:您可以使用递归 CTE 来遍历图形并找到初始父级。例如:
with
n (starting_id, current_id, parent_id, v) as (
select id, id, parent_id, 0 from entry where id in (6, 3)
union all
select n.starting_id, e.id, e.parent_id, n.v - 1
from n
join entry e on e.id = n.parent_id
)
select starting_id, current_id as initial_id
from (
select n.*, row_number() over(partition by starting_id order by v) as rn
from n
) x
where rn = 1
结果:
STARTING_ID INITIAL_ID
------------ ----------
3 1
6 4
请参阅db<>fiddle 的运行示例。
【讨论】:
你不需要使用ROW_NUMBER
,只需过滤WHERE parent_id IS NULL
db<>fiddle
@MT0 叹息。我想我需要一杯咖啡。
另外,如果速度很重要,递归 CTE 应该只在无法进行分层查询或非常复杂的情况下使用(以及与 Oracle 不同,没有分层查询的 SQL 方言;@ 987654327@ 不在 SQL 标准中,它是 Oracle 专有的)。以上是关于查找孩子的***父母,多个级别的主要内容,如果未能解决你的问题,请参考以下文章