sql - 将元素的祖先从邻接列表写入 Postgres 表

标签 sql postgresql hierarchical-data recursive-query

我想编写一个 1 到 n 的层次结构,将其作为邻接列表存储到列出每个元素祖先的表中。我使用的是 Postgres 数据库(Postgres 10,但要部署代码的计算机运行 Postgres 9.x)。

示例输入表(邻接表):

INSERT INTO public.test (id, name, parent_id)
VALUES (1, 't', 1),
   (11, 't1', 1),
   (12, 't2', 1),
   (13, 't3', 1),
   (111, 't11', 11),
   (112, 't12', 11),
   (121, 't21', 12),
   (14, 't4', 1),
   (141, 't41', 14),
   (142, 't42', 14)

因此,我想要一个如下所示的表格(仅显示几行;此外,我试图解决的现实问题有七个层次结构级别,而不是只有两个层次结构级别):

+-----+-------+--------+--------+
| id  | level | level0 | level1 |
+-----+-------+--------+--------+
|   1 |     0 | NULL   | NULL   |
|  11 |     1 | 1      | NULL   |
|  12 |     1 | 1      | NULL   |
| 111 |     2 | 1      | 11     |
+-----+-------+--------+--------+

id 是元素的 id,level 是该元素在层次结构中所处的级别(0 是根级别),level0/1 是相应级别的元素的祖先。

我是 SQL 新手,所以我没有任何代码可以向您展示。谷歌搜索告诉我,我可能需要使用递归 CTE 来获得所需的结果并执行自连接,但我无法弄清楚如何做到这一点。感谢您的帮助。

编辑

这是我迄今为止尝试过的:

WITH RECURSIVE cte AS
(
SELECT m.id AS id,
    0 AS level,
    m.parent_id AS level0,
    m.parent_id AS level1,
    m.parent_id AS parent
    FROM public.test AS m
    WHERE m.parent_id IS NULL

UNION ALL

SELECT 
    m.id,
    cte.level + 1,
    cte.parent AS level0,
    cte.parent AS level1,
    m.parent_id AS parent
    FROM public.test AS m 
        INNER JOIN cte
            ON m.parent_id = cte.id 
)
SELECT *
FROM cte;

当然,将 level0level1 设置为元素的父级不会产生所需的结果,但我必须将其设置为某个值,但没有进一步比这个。

最佳答案

SQL 是一种严格类型化语言,不允许从 SELECT 返回的列数根据其所操作的数据而变化。参见例如Split comma separated column data into additional columns进行讨论。

但是,PostgreSQL 为您提供了 array type您可以使用它来将动态大小的值收集到单个列中。以下递归 CTE 将每一行的所有祖先收集到这样的数组中:

with recursive rec(id, level, parent_id, ancestors) as (
  select id, 0, parent_id, array[] :: int[]
  from test
  where parent_id = id
  union all
  select t.id, rec.level + 1, t.parent_id, rec.ancestors || array[t.parent_id]
  from test t
  join rec on t.parent_id = rec.id
  where t.parent_id <> t.id
)
select 
  rec.id,
  rec.level,
  rec.ancestors
from rec;

如果级别有已知限制,您可以从每列的数组中选择元素:

select
  rec.id,
  rec.level,
  rec.ancestors[1] level1,
  rec.ancestors[2] level2,
  ...

SQL Fiddle

关于sql - 将元素的祖先从邻接列表写入 Postgres 表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48662979/

相关文章:

sql - 使用 SQL 查找冲突的日期间隔

php - date_format 和 datetime 中的 sql 查询错误

postgresql - 你如何告诉 Heroku 忽略开发组中的 Sqlite gem

ruby-on-rails - 在数据库设计中管理多个类别的最佳方式

sql - PostgreSQL 如何查询字符串数组

mysql - 从具有特定根的 SQL 表中获取最新分支的最有效方法是什么?

reporting-services - SSRS 递归计数 - 如何仅为子级而不是父级获取聚合?

nested - 从 CouchDB 检索分层/嵌套数据

sql - SQL Server Management Studio中的脚本大小限制

sql - 关系代数中 "theta join"的清晰解释?