从 Postgres 和 jOOQ 中的分层表递归生成 JSON 树

Posted

技术标签:

【中文标题】从 Postgres 和 jOOQ 中的分层表递归生成 JSON 树【英文标题】:Recursively generate JSON tree from hierarchical table in Postgres and jOOQ 【发布时间】:2021-06-13 18:47:13 【问题描述】:

我在 Postgres 数据库中有一个分层表,例如category。结构简单如下:

id parent_id name
1 null A
2 null B
3 1 A1
4 3 A1a
5 3 A1b
6 2 B1
7 2 B2

我需要从这张表中得到这样的递归深度树结构:

[
  
    "id": 1,
    "name": "A",
    "children": [
      
        "id": 3,
        "name": "A1",
        "children": [
          
            "id": 4,
            "name": "A1a",
            "children": []
          ,
          
            "id": 5,
            "name": "A1b",
            "children": []
          
        ]
      
    ]
  ,
  
    "id": 2,
    "name": "B",
    "children": [
      
        "id": 6,
        "name": "B1",
        "children": []
      ,
      
        "id": 7,
        "name": "B2",
        "children": []
      
    ]
  ,
]

是否有可能使用WITH RECURSIVEjson_build_array() 的组合或其他一些解决方案的未知深度?

【问题讨论】:

【参考方案1】:

我在this excellent blog post here 中找到了这个问题的答案,因为我想知道如何在 jOOQ 中概括这个问题。如果 jOOQ 可以以通用方式实现任意递归对象树,那将很有用:https://github.com/jOOQ/jOOQ/issues/12341

同时,使用受上述博客文章启发的 SQL 语句,并进行了一些修改。如果必须,请转换为 jOOQ,尽管您也可以将其存储为视图:

WITH RECURSIVE
  d1 (id, parent_id, name) as (
    values
      (1, null, 'A'),
      (2, null, 'B'),
      (3,    1, 'A1'),
      (4,    3, 'A1a'),
      (5,    3, 'A1b'),
      (6,    2, 'B1'),
      (7,    2, 'B2')
  ),
  d2 AS (
    SELECT d1.*, 0 AS level
    FROM d1
    WHERE parent_id IS NULL
    UNION ALL
    SELECT d1.*, d2.level + 1
    FROM d1
    JOIN d2 ON d2.id = d1.parent_id
  ),
  d3 AS (
    SELECT d2.*, jsonb_build_array() children
    FROM d2
    WHERE level = (SELECT max(level) FROM d2)
    UNION (
      SELECT (branch_parent).*, jsonb_agg(branch_child)
      FROM (
        SELECT 
          branch_parent, 
          to_jsonb(branch_child) - 'level' - 'parent_id' AS branch_child
        FROM d2 branch_parent
        JOIN d3 branch_child ON branch_child.parent_id = branch_parent.id
      ) branch
      GROUP BY branch.branch_parent
      UNION
      SELECT d2.*, jsonb_build_array()
      FROM d2
      WHERE d2.id NOT IN (
        SELECT parent_id FROM d2 WHERE parent_id IS NOT NULL
      )
    )
  )
SELECT jsonb_pretty(jsonb_agg(to_jsonb(d3) - 'level' - 'parent_id')) AS tree
FROM d3
WHERE level = 0;

dbfiddle。再次阅读linked blog post,了解其工作原理

【讨论】:

以上是关于从 Postgres 和 jOOQ 中的分层表递归生成 JSON 树的主要内容,如果未能解决你的问题,请参考以下文章

从jOOQ访问名为CHECK CONSTRAINTS的Postgres

JOOQ 强制类型将 BigInteger 转换为 BigDecimal 以用于 POSTGRES

将 JooQ 与 SQL Server 一起使用,getTables() 方法返回服务器上所有数据库中的所有表

如何在使用 jooq 生成的 dao 插入/更新后获取插入/更新的对象

Jooq Postgres JSON 查询

JOOQ 解析器问题(POSTGRES 到 H2 的翻译)