:
create table tree(id int primary key, parent int, caption text, node_value int);
insert into tree values
(1, 0, 'A', null),
(2, 1, 'B', 6),
(3, 1, 'C', null),
(4, 3, 'D', null),
(5, 4, 'E', 10),
(6, 4, 'F', 2),
(7, 1, 'H', 18),
(8, 7, 'I', 102),
(9, 7, 'J', 301);
- .
create or replace function get_node_value(node_id int)
returns int language plpgsql as $$
declare
val int;
begin
select node_value
from tree
where id = node_id
into val;
if val isnull then
select avg(get_node_value(id))
from tree
where parent = node_id
into val;
end if;
return val;
end;
$$;
select get_node_value(1);
get_node_value
10
(1 row)
.
sql- . , , plpgsql.
create or replace function get_node_value_sql(node_id int)
returns int language sql as $$
select coalesce(
node_value,
(
select avg(get_node_value_sql(id))::int
from tree
where parent = node_id
)
)
from tree
where id = node_id;
$$;
cte . , .
with recursive bottom_up(id, parent, caption, node_value, level, calculated) as (
select
*,
0,
node_value calculated
from tree t
where not exists (
select id
from tree
where parent = t.id)
union all
select
t.*,
b.level+ 1,
case when t.node_value is null then b.calculated else t.node_value end
from tree t
join bottom_up b on t.id = b.parent
)
select id, parent, caption, avg(calculated)::int calculated
from (
select id, parent, caption, level, avg(calculated)::int calculated
from bottom_up
group by 1, 2, 3, 4
) s
group by 1, 2, 3
order by 1;
.