PostgreSQL递归查询计算父值
Posted
技术标签:
【中文标题】PostgreSQL递归查询计算父值【英文标题】:PostgreSQL recursive query for calculation parent value 【发布时间】:2021-11-01 18:39:40 【问题描述】: 这是条目的层次结构。 _________Milky Way (30)________
/ | \
Alpha(10) Beta(20) Delta(null)
/ \ |
Mars(7) Jupiter(3) Delta-child(44)
父值是子值的总和。 例如。
Alpha = Mars + Jupiter = 7 + 3 = 10
Milky Way = Alpha + Beta + Delta = 10 + 20 + null = 30
任务:重新计算父级直到根,以防任何子级更新。让我们甚至简化 任务:选择所有条目,直到根,并重新计算值。 假设 Mars 已更新。现在 Mars 的值为 2。
_________Milky Way (?)________
/ | \
Alpha(?) Beta(20) Delta(null)
/ \ |
Mars(2) Jupiter(3) Delta-child(44)
这意味着所有的父母都应该更新:
Alpha = Mars + Jupiter = 2 + 3 = 5
Milky Way = Alpha + Beta + Delta = 5 + 20 + null = 25.
注意: Delta -> Delta-child 耦合已损坏,一切正常。它可能发生,让我们把它放在这里的范围之外。 '添加此示例只是为了确保在计算过程中不会对其进行计数,因为层次结构可能足够大,并且没有任务重新计算所有子叶子,只需要从父节点到根节点。
由于一些“从层次结构中选择..” 我想收到重新计算的父母的价值观。 前任。
id | name | value |
---|---|---|
1 | Milky Way | 25 |
2 | Alpha | 5 |
已更新 Mars 的代码示例(sqlfiddle 链接如下):Schema
CREATE TABLE hierarchy
(
id int4,
parent_id int4,
name varchar(255),
value int4
);
价值观
insert into hierarchy
values
(1, null, 'Milky Way', 30),
(2, 1, 'Alpha', 10),
(3, 1, 'Beta', 20),
(4, 1, 'Delta', null),
(5, 2, 'Mars', 2),
(6, 2, 'Jupiter', 3),
(7, 4, 'Delta-child', 44);
我的尝试:
我能够列出所有应该在计算中使用的叶子 sqlfiddle 1
WITH RECURSIVE cte AS (
SELECT h1.id, h1.parent_id, h1.name , h1.value from hierarchy h1
where h1.id = 5
UNION
SELECT h2.id, h2.parent_id, h2.name , h2.value from hierarchy h2
JOIN cte cte ON (cte.parent_id = h2.parent_id or cte.parent_id = h2.id )
where cte.id != h2.id
) select * from cte
order by id
当我尝试对值求和时,由于某种原因,查询进入无限循环sqlfiddle 2
WITH RECURSIVE cte AS (
SELECT h1.id, h1.parent_id, h1.name , h1.value from hierarchy h1
where h1.id = 5
UNION
SELECT h2.id, h2.parent_id, h2.name , (h2.value + cte.value) as value from hierarchy h2
JOIN cte cte ON (cte.parent_id = h2.parent_id or cte.parent_id = h2.id )
where cte.id != h2.id
) select * from cte
order by id
我尝试了另外一个查询,不幸的是它不包括父母的兄弟姐妹。sqlfiddle 3
WITH RECURSIVE cte AS (
SELECT h1.id, h1.parent_id, h1.name , h1.value from hierarchy h1
where h1.parent_id = (select parent_id from hierarchy where id = 5)
UNION
SELECT h2.id, h2.parent_id, h2.name , cte.value as value from hierarchy h2
JOIN cte cte ON (cte.parent_id = h2.parent_id or cte.parent_id = h2.id )
where cte.id != h2.id
) select id, parent_id, name, sum(value) from cte
group by id, parent_id, name
order by id
如有任何帮助,我将不胜感激。 :-)
【问题讨论】:
【参考方案1】:我花了一些时间,但你的试验做得很好。
我所做的是将问题分成几部分。
-
使用递归查询沿层次结构向下移动
with recursive base_qry as (
select id,
parent_id,
value,
ARRAY[id] as id_array
from hierarchy
union all
select h.id,
h.parent_id,
h.value,
id_array || h.id as id_array
from hierarchy h join base_qry b on h.parent_id = b.id and h.value is not null
)
-
了解受影响的节点过滤包含
id_array[array_length(id_array,1)] = 5
的所有节点的数组的最后一个 id
nodes_affected as (
select * from base_qry
where id_array[array_length(id_array,1)] = 5
order by array_length(id_array,1) desc
LIMIT 1)
-
查找所有未对更改节点造成影响的树分支(在此处检查节点
id=5
的过滤器)
all_combinations as (
select b.id, b.parent_id, b.value, b.id_array from
base_qry b join nodes_affected n
on ARRAY[n.id_array] && ARRAY[b.id_array]
where (b.id_array[array_length(b.id_array,1)] = 5
or b.id_array @> ARRAY[5] = false)
)
聚合
select id_array[1]::int id, sum(value)
from all_combinations where id not in (select parent_id from all_combinations where parent_id is not null)
group by id_array[1]::int
order by 1
整个查询
with recursive base_qry as (
select id,
parent_id,
value,
ARRAY[id] as id_array
from hierarchy
union all
select h.id,
h.parent_id,
h.value,
id_array || h.id as id_array
from hierarchy h join base_qry b on h.parent_id = b.id and h.value is not null
),
nodes_affected as (
select * from base_qry
where id_array[array_length(id_array,1)] = 5
order by array_length(id_array,1) desc
LIMIT 1),
all_combinations as (
select b.id, b.parent_id, b.value, b.id_array from
base_qry b join nodes_affected n
on ARRAY[n.id_array] && ARRAY[b.id_array]
where (b.id_array[array_length(b.id_array,1)] = 5
or b.id_array @> ARRAY[5] = false)
)
select id_array[1]::int id, sum(value)
from all_combinations where id not in (select parent_id from all_combinations where parent_id is not null)
group by id_array[1]::int
order by 1
;
【讨论】:
【参考方案2】:从叶子开始,遍历叶子贡献给层次结构的所有节点。然后将所有对节点的贡献相加。
WITH RECURSIVE cte AS (
SELECT id, parent_id, value
FROM hierarchy h1
WHERE not exists (select 1 from hierarchy h2 where h2.parent_id = h1.id)
UNION ALL
SELECT h.id, h.parent_id, case when h.value is null then 0 else cte.value end
FROM hierarchy h
JOIN cte ON (cte.parent_id = h.id)
)
select h.id, h.name, v.value
from (
select id, sum(value) as value
from cte
group by id
) v
join hierarchy h on h.id = v.id
order by h.id;
db<>fiddle
【讨论】:
【参考方案3】:您可以使用递归CTE来获取所有节点及其路径,然后对于hierarchy
中的每个非叶子节点,您可以在当前层次结构行id存在的情况下将CTE重新加入hierarchy
在路径中并对值求和:
with recursive cte(id, p, n, v, t) as (
select h.*, concat('[', h.id::text) from hierarchy h where h.parent_id is null
union all
select h.id, h.parent_id, h.name, h.value, concat(c.t, ', ', h.id)
from cte c join hierarchy h on c.id = h.parent_id
)
select h.id, sum(c.v)
from hierarchy h join cte c on c.id != h.id and exists (select 1 from jsonb_array_elements(concat(c.t, ']')::jsonb) v where v.value::text = h.id::text)
group by h.id order by h.id
输出:
id sum
1 79
2 5
4 44
【讨论】:
以上是关于PostgreSQL递归查询计算父值的主要内容,如果未能解决你的问题,请参考以下文章