从混合层级中查找多个子级的第一个共同父级

Posted

技术标签:

【中文标题】从混合层级中查找多个子级的第一个共同父级【英文标题】:Find first common parent for multiple children from mixed hierarchy levels 【发布时间】:2021-12-27 20:52:32 【问题描述】:

从许多不同的孩子中找到第一个共同的父母(如果有的话)。

例子:

    1       
   / \     
  2   3    
 /   / \
7   8   9  
   / \
 10   11

输入:[10, 9] 输出:3(此元素的第一个公共父级)

表格示例:

+------------------+-----------+------+
|EmployeePositionId|Subdivision|Parent|
+------------------+-----------+------+
|4718              |485        |42    |
|4719              |5064       |485   |
|4720              |5065       |5064  |
|4721              |5065       |5064  |
|4722              |3000       |null  |
+------------------+-----------+------+

如果我尝试搜索EmployeePositionId [4719, 4720, 4721], 我想获得Subdivision 5064,因为它是两个员工最接近的公共细分(5065 嵌套在 5064 中)。

如果我要查找 4719、4720、4721、4722,那么我想获取 null,因为这些元素没有共同的父元素。

或者答案将帮助我如何获取数据以便以后在 Python 中解决这个问题

【问题讨论】:

基于什么逻辑?请更具体并提供更多详细信息。 表中是否有层次等级的指标?树的完整性是否得到保证(无循环)?输入ID的最小/最大数量?表中是否存在所有输入 ID?您的 Postgres 版本? 我找到了这个,但它只查看两个 id,而不是变量号。 ***.com/questions/608076/… – Maciej Los,有必要按员工找到最近的综合部门,如果有的话 – Erwin Brandstetter,表中没有层次指示符,但您可以在非递归部分的查询中添加它:如果有帮助,0 作为“级别”。绝对没有周期。这棵树“起来”了。最小输入 - 0,最大 - 不超过 1500。所有输入 ID 都存在于表中。 Postgres 10.11 【参考方案1】:

这类问题对于 SQL 来说是困难。 使用您的特定表就更难了。它没有正确规范化。没有水平指示器。输入 ID 可以来自混合层级。

设置

您在稍后的评论中澄清说,即使问题中的示例数据表明并非如此,每个路径都以具有 "Parent" IS NULL(根)的行终止。这有点帮助。

我假设输入有效的“EmployeePositionId”。并且您的树中没有循环,否则 CTE 会进入无限循环。

如果表中没有层次结构级别,请添加它。这是一个简单的任务。如果无法添加,请创建一个 VIEW,或者最好创建一个 MATERIALIZED VIEW

CREATE MATERIALIZED VIEW mv_tbl AS
WITH RECURSIVE cte AS (
   SELECT *, 0 AS level
   FROM   tbl
   WHERE  "Parent" IS NULL
   
   UNION ALL
   SELECT t.*, c.level + 1
   FROM   cte c
   JOIN   tbl t ON t."Parent" = c."Subdivision"
   )
TABLE cte;

这些将是该任务的完美索引:

CREATE UNIQUE INDEX mv_tbl_id_uni ON mv_tbl ("EmployeePositionId") INCLUDE ("Subdivision", "Parent", level);  
CREATE INDEX mv_tbl_subdivision_idx ON mv_tbl ("Subdivision") INCLUDE ("Parent", level);

见:

Covering index for top read performance

查询

具有递归 CTE 的纯 SQL 解决方案,基于具有级别指示器(或上面的 MV)的表:

WITH RECURSIVE init AS (
   SELECT "Subdivision", "Parent", level
   FROM   mv_tbl
   WHERE  "EmployeePositionId" IN (4719, 4720, 4721)  -- input
   )
, cte AS (
   TABLE init
   UNION
   SELECT c."Parent", t."Parent", c.level - 1
   FROM   cte c
   JOIN   mv_tbl t ON t."Subdivision" = c."Parent"  -- recursion terminated at "Parent" IS NULL
   )
, agg AS (
   SELECT level, min("Subdivision") AS "Subdivision", count(*) AS ct
   FROM   cte
   GROUP  BY  level
   )
SELECT "Subdivision"
FROM   agg a
WHERE  ct = 1                                  -- no other live branch
AND    level <  (SELECT max(level) FROM cte WHERE "Parent" IS NULL) IS NOT TRUE  -- no earlier dead end
AND    level <= (SELECT min(level) FROM init)  -- include highest (least) level
ORDER  BY level DESC                           -- pick earliest (greatest) qualifying level
LIMIT  1;

db小提琴here

涵盖所有可能的输入,适用于任何现代版本的 Postgres。

我在代码中添加了基本解释。

相关:

How to aggregate a table with tree-structure to a single nested JSON object? How to turn a set of flat trees into a single tree with multiple leaves?

合法的、小写的、不带引号的标识符使您使用 Postgres 的生活更轻松。见:

Are PostgreSQL column names case-sensitive?

【讨论】:

对于 4719, 4720, 4721, 4722,您的查询返回 42 而不是 null。 @MaciejLos:考虑改进的查询。 很遗憾,表格中没有嵌套级别。而且我不能添加约束“父不为空”。 “Parent = null”表示单元在根。这就是一张巨大的桌子的布置和工作方式,我无法对其进行此类更改。但是可以解决这样的问题 - 请告诉我如何以我可以解决 Python 后处理问题的方式获取数据 @Masta 我没有建议约束“父母不为空”。考虑更新您添加的信息(应该在问题中)。祝你好运。

以上是关于从混合层级中查找多个子级的第一个共同父级的主要内容,如果未能解决你的问题,请参考以下文章

Java - 为啥另一个包中的子级无法通过父级引用访问父级的受保护方法?

PHP在父级中访问子级的私有属性

css父级没包住子级是怎么回事,我用firebug查看,确实在父级的div中,但是就是没有包括子级的内容

来自父级的 TextField 默认值未在子级上呈现

CSS - 将父级的高度设置为 0,但子级 div 仍然显示

为啥我不能使用“fieldset”标签作为 flex 父级的深层子级?