常见 table 表达式内部的反向聚合
Reverse aggregation inside of common table expression
我本以为下面的查询 returns 所有人都有各自的 children。
WITH RECURSIVE nested_people (id, name, children) AS (
SELECT id, name, NULL::JSON AS children
FROM people
WHERE parent_id IS NULL
UNION ALL
SELECT people.id, people.name, ROW_TO_JSON(nested_people.*) AS children
FROM people
JOIN nested_people ON people.parent_id = nested_people.id
)
SELECT * FROM nested_people;
但实际上恰恰相反。我想不出一种无需额外的 CTE 即可进行正确嵌套的方法。有办法吗?
示例数据
+----+-------+-----------+
| id | name | parent_id |
+----+-------+-----------+
| 1 | Adam | null |
| 2 | Abel | 1 |
| 3 | Cain | 1 |
| 4 | Enoch | 3 |
+----+-------+-----------+
结果
+----+-------+--------------------------------------------------------------------------+
| id | name | children |
+----+-------+--------------------------------------------------------------------------+
| 1 | Adam | null |
| 2 | Abel | {"id":1,"name":"Adam","children":null} |
| 3 | Cain | {"id":1,"name":"Adam","children":null} |
| 4 | Enoch | {"id":3,"name":"Cain","children":{"id":1,"name":"Adam","children":null}} |
+----+-------+--------------------------------------------------------------------------+
预期结果
+----+-------+----------------------------------------------------------------------------------------------------------------------+
| id | name | children |
+----+-------+----------------------------------------------------------------------------------------------------------------------+
| 1 | Adam | [{"id":2, "name":"Abel", "children":null},{"id":3,"name":"Cain","children":[{"id":4,"name":"Enoch","children":null}] |
| 2 | Abel | null |
| 3 | Cain | [{"id":4,"name":"Enoch","children":null}] |
| 4 | Enoch | null |
+----+-------+----------------------------------------------------------------------------------------------------------------------+
这个rCTE从另一边遍历树:
WITH RECURSIVE cte AS (
SELECT id, parent_id, name, NULL::JSON AS children
FROM people p
WHERE NOT EXISTS ( -- only leaf nodes; see link below
SELECT 1 FROM people
WHERE parent_id = p.id
)
UNION ALL
SELECT p.id, p.parent_id, p.name, row_to_json(c) AS children
FROM cte c
JOIN people p ON p.id = c.parent_id
)
SELECT id, name, json_agg(children) AS children
FROM cte
GROUP BY 1, 2;
使用json_agg()
聚合外部SELECT
中每个节点的多个分支。
与您想要的结果存在细微差别:
- 这包括
children
列中的 parent_id
。
- 单个节点未包装到数组中。
两者都可以进行调整,但我希望结果对您来说没问题。
如何识别叶节点:
- Select rows which are not present in other table
我本以为下面的查询 returns 所有人都有各自的 children。
WITH RECURSIVE nested_people (id, name, children) AS (
SELECT id, name, NULL::JSON AS children
FROM people
WHERE parent_id IS NULL
UNION ALL
SELECT people.id, people.name, ROW_TO_JSON(nested_people.*) AS children
FROM people
JOIN nested_people ON people.parent_id = nested_people.id
)
SELECT * FROM nested_people;
但实际上恰恰相反。我想不出一种无需额外的 CTE 即可进行正确嵌套的方法。有办法吗?
示例数据
+----+-------+-----------+
| id | name | parent_id |
+----+-------+-----------+
| 1 | Adam | null |
| 2 | Abel | 1 |
| 3 | Cain | 1 |
| 4 | Enoch | 3 |
+----+-------+-----------+
结果
+----+-------+--------------------------------------------------------------------------+
| id | name | children |
+----+-------+--------------------------------------------------------------------------+
| 1 | Adam | null |
| 2 | Abel | {"id":1,"name":"Adam","children":null} |
| 3 | Cain | {"id":1,"name":"Adam","children":null} |
| 4 | Enoch | {"id":3,"name":"Cain","children":{"id":1,"name":"Adam","children":null}} |
+----+-------+--------------------------------------------------------------------------+
预期结果
+----+-------+----------------------------------------------------------------------------------------------------------------------+
| id | name | children |
+----+-------+----------------------------------------------------------------------------------------------------------------------+
| 1 | Adam | [{"id":2, "name":"Abel", "children":null},{"id":3,"name":"Cain","children":[{"id":4,"name":"Enoch","children":null}] |
| 2 | Abel | null |
| 3 | Cain | [{"id":4,"name":"Enoch","children":null}] |
| 4 | Enoch | null |
+----+-------+----------------------------------------------------------------------------------------------------------------------+
这个rCTE从另一边遍历树:
WITH RECURSIVE cte AS (
SELECT id, parent_id, name, NULL::JSON AS children
FROM people p
WHERE NOT EXISTS ( -- only leaf nodes; see link below
SELECT 1 FROM people
WHERE parent_id = p.id
)
UNION ALL
SELECT p.id, p.parent_id, p.name, row_to_json(c) AS children
FROM cte c
JOIN people p ON p.id = c.parent_id
)
SELECT id, name, json_agg(children) AS children
FROM cte
GROUP BY 1, 2;
使用json_agg()
聚合外部SELECT
中每个节点的多个分支。
与您想要的结果存在细微差别:
- 这包括
children
列中的parent_id
。 - 单个节点未包装到数组中。
两者都可以进行调整,但我希望结果对您来说没问题。
如何识别叶节点:
- Select rows which are not present in other table