【问题标题】:Traversing a closure table遍历闭包表
【发布时间】:2022-10-14 21:20:54
【问题描述】:

看看这个闭包表:

ancestor descendant path_length
1 1 0
2 2 0
3 3 0
4 4 0
2 4 1
5 5 0
2 5 1
6 6 0
4 6 1
2 6 2
7 7 0
4 7 1
2 7 2
8 8 0
6 8 1
4 8 2
2 8 3

现在我想要这个:

1
2
4
6
8
7
5
3

是否可以使用 SQL 查询?

【问题讨论】:

  • 是表格格式问题还是您的表格有 9 个字段,即 3 个相同字段重复 3 次(在架构级别)?
  • 它只是一张三列十七行的表。
  • 检查更新表的正确性,并考虑添加有关您当前使用的 DBMS 和尝试查询(如果有)的信息。

标签: sql postgresql tree


【解决方案1】:

步骤1: 找到你的树根

给定您的输入表,您可以通过选择

  • 所有具有“路径长度 = 0"(只选择一次)
  • 在后代中找不到“路径长度 > 0"(至少从级别 = 1 中找到的那些节点)。
SELECT ancestor AS root FROM tab WHERE path_length = 0
EXCEPT
SELECT descendant FROM tab WHERE path_length > 0
root
1
2
3

第2步: 为你的二叉树实现深度优先搜索。

这可以通过

  • 扫描“祖先" 值只属于根表(避免重复"后裔" 值)通过加入上一个表
  • 应用深度优先排序,首先对祖先进行排序,然后需要以递归方式始终获得第一个儿子。这可以通过ROW_NUMBER 窗口函数有效地完成,负责将值 1 分配给每个父母的每个第一个儿子。相同排名值但不同父级之间的排序可以使用“后裔“价值本身。
WITH roots AS (
    SELECT ancestor AS root FROM tab WHERE path_length = 0
    EXCEPT
    SELECT descendant FROM tab WHERE path_length > 0
)
SELECT descendant
FROM       tab
INNER JOIN roots
        ON tab.ancestor = roots.root
ORDER BY ancestor, 
         ROW_NUMBER() OVER(PARTITION BY ancestor, path_length ORDER BY descendant), 
         descendant DESC

检查演示here


上一个是一个通用的解决方案,但如果您假设事先知道根的值(1、2 和 3),您可以将查询简化如下:

SELECT descendant
FROM tab
WHERE ancestor < 3
ORDER BY ancestor, 
         ROW_NUMBER() OVER(PARTITION BY ancestor, path_length ORDER BY descendant), 
         descendant DESC

【讨论】:

    猜你喜欢
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-12-30
    • 2012-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-10
    相关资源
    最近更新 更多