postgres中递归树的总和
Sum of recursive tree in postgres
有一个如下所示的树结构:
root
A B
A1 A2 B1 B2
A1.1 A1.2 A2.1 B1.1
table 看起来像这样:
id | name |value | parent_id
1 root null null
2 A null 1
3 B null 1
4 A1 null 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 null 2
8 A2.1 5 7
9 B1 null 3
10 B2 1 3
11 B1.1 10 9
.........................
所有非叶节点必须包含其子节点的总和,而不仅仅是根节点。例如,这是我想要的输出的样子:
id | name |value | parent_id
1 root 21 null
2 A 10 1
3 B 11 1
4 A1 5 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 5 2
8 A2.1 5 7
9 B1 10 3
10 B2 1 3
11 B1.1 10 9
如何使用快速 Postgres 查询实现此目的
使用递归 cte
:
with recursive cte(id, name, val, p) as (
select t.id, t.name, t.value, '.'||(select t1.id from tbl t1 where t1.parent_id is null)||'.'||t.id||'.' from tbl t where t.parent_id = (select t1.id from tbl t1 where t1.parent_id is null)
union all
select t.id, t.name, t.value, c.p||t.id||'.' from cte c join tbl t on c.id = t.parent_id
)
select t.id, sum(case when c.val is null then 0 else c.val end) from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id order by t.id
要用求和的子节点值更新原始 table,您可以在 update
:
中使用子查询
update tbl set value = t1.val from (
select t.id tid, sum(case when c.val is null then 0 else c.val end) val from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id) t1
where t1.tid = id
您需要在 UPDATE 语句中使用递归 CTE:
UPDATE tablename AS t
SET "value" = c.value
FROM (
WITH RECURSIVE cte AS(
SELECT t1.id, t1.name, t1.value, t1.parent_id
FROM tablename t1
WHERE NOT EXISTS (SELECT 1 FROM tablename t2 WHERE t2.parent_id = t1.id)
UNION ALL
SELECT t.id, t.name, c.value, t.parent_id
FROM tablename t INNER JOIN cte c
ON c.parent_id = t.id
)
SELECT id, SUM("value") "value"
FROM cte
GROUP BY id
) c
WHERE c.id = t.id;
参见demo。
有一个如下所示的树结构:
root
A B
A1 A2 B1 B2
A1.1 A1.2 A2.1 B1.1
table 看起来像这样:
id | name |value | parent_id
1 root null null
2 A null 1
3 B null 1
4 A1 null 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 null 2
8 A2.1 5 7
9 B1 null 3
10 B2 1 3
11 B1.1 10 9
.........................
所有非叶节点必须包含其子节点的总和,而不仅仅是根节点。例如,这是我想要的输出的样子:
id | name |value | parent_id
1 root 21 null
2 A 10 1
3 B 11 1
4 A1 5 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 5 2
8 A2.1 5 7
9 B1 10 3
10 B2 1 3
11 B1.1 10 9
如何使用快速 Postgres 查询实现此目的
使用递归 cte
:
with recursive cte(id, name, val, p) as (
select t.id, t.name, t.value, '.'||(select t1.id from tbl t1 where t1.parent_id is null)||'.'||t.id||'.' from tbl t where t.parent_id = (select t1.id from tbl t1 where t1.parent_id is null)
union all
select t.id, t.name, t.value, c.p||t.id||'.' from cte c join tbl t on c.id = t.parent_id
)
select t.id, sum(case when c.val is null then 0 else c.val end) from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id order by t.id
要用求和的子节点值更新原始 table,您可以在 update
:
update tbl set value = t1.val from (
select t.id tid, sum(case when c.val is null then 0 else c.val end) val from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id) t1
where t1.tid = id
您需要在 UPDATE 语句中使用递归 CTE:
UPDATE tablename AS t
SET "value" = c.value
FROM (
WITH RECURSIVE cte AS(
SELECT t1.id, t1.name, t1.value, t1.parent_id
FROM tablename t1
WHERE NOT EXISTS (SELECT 1 FROM tablename t2 WHERE t2.parent_id = t1.id)
UNION ALL
SELECT t.id, t.name, c.value, t.parent_id
FROM tablename t INNER JOIN cte c
ON c.parent_id = t.id
)
SELECT id, SUM("value") "value"
FROM cte
GROUP BY id
) c
WHERE c.id = t.id;
参见demo。