从 PostgreSQL 中的类别数组创建类别树 table

Creating a category tree table from an array of categories in PostgreSQL

如何从类别数组中生成 ID 和 parent_ids。子类别的数量或深度可以是 1-10 级之间的任何值。

示例 PostgreSQL 列。数据类型字符可变数组。

data_column
character varying[]             |               
----------------------------------
[root_1, child_1, childchild_1] |
[root_1, child_1, childchild_2] | 
[root_2, child_2]               | 

我想将数组的列转换为 table,如下所示,我假设它称为邻接列表模型。我知道还有嵌套树集模型和物化路径模型。

最终输出table

id | title        | parent_id
------------------------------
1  | root_1       | null
2  | root_2       | null  
3  | child_1      | 1
4  | child_2      | 2 
5  | childchild_1 | 3  
6  | childchild_2 | 3   

最终输出树层次结构

root_1
--child_1
----childchild_1
----childchild_2
root_2
--child_2

step-by-step demo: db<>fiddle

您可以使用 recursive CTE

WITH RECURSIVE cte AS 
(  SELECT data[1] as title, 2 as idx, null as parent, data FROM t   -- 1

   UNION

   SELECT data[idx], idx + 1, title, data                           -- 2
   FROM cte
   WHERE idx <= cardinality(data)
)
SELECT DISTINCT                                                     -- 3 
    title,
    parent
FROM cte
  1. 递归的起始查询:获取递归中需要的所有根元素和数据
  2. 递归部分:获取新索引的元素并增加索引
  3. 递归后:查询最终需要的列。 DISTINCT 删除绑定元素(例如相同的两倍 root_1)。

现在您已经创建了层次结构。现在您需要 ID。 您可以通过多种不同的方式生成它们,例如使用 row_number() window function:

WITH RECURSIVE cte AS (...)
SELECT 
    *,
    row_number() OVER ()
FROM (
    SELECT DISTINCT
        title,
        parent
    FROM cte
) s

现在,每一行都有自己的 ID。订单标准可能会稍微调整一下。在这里,如果没有任何进一步的信息,我们几乎没有机会改变它。但算法保持不变。

有了每列的id,我们可以创建一个自连接,通过使用parent title 列来连接父id。因为自连接是 select 查询的重复,所以将其封装到第二个 CTE 以避免代码复制是有意义的。最后的结果是:

WITH RECURSIVE cte AS 
(  SELECT data[1] as title, 2 as idx, null as parent, data FROM t

   UNION

   SELECT data[idx], idx + 1, title, data
   FROM cte
   WHERE idx <=  cardinality(data)
), numbered AS (
   SELECT 
       *,
       row_number() OVER ()
   FROM (
       SELECT DISTINCT
           title,
           parent
       FROM cte
   ) s
)
SELECT 
    n1.row_number as id,
    n1.title,
    n2.row_number as parent_id
FROM numbered n1
LEFT JOIN numbered n2 ON n1.parent = n2.title