Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive query with sum in Postgres

I must store a lot of projects in my DB. Each project can have child projects. The structure looks like a tree:

                                     Project
                            /           |             \
                    ProjectChild1    ProjectChild2    [...]    ProjectChild[n]
                    /       |
 ProjectChildOfChild1    ProjectChildOfChild2

The level of the tree is unknow. I'm thinking to create a table like this:

Table Projects:

project_ID id_unique PRIMARY_KEY
project_NAME text
project_VALUE numeric
project_PARENT id_unique

In this case, the column project_PARENT will store the id of the parent project, if exists.

For my application I need to retrieve the total value of a project, for this I need to sum the values of every project child and the root project.

I know that I need to use recursivity, but I don't know how to do this in Postgres.

like image 784
Guilherme Batista Avatar asked Dec 20 '22 09:12

Guilherme Batista


2 Answers

This is a simplified version of @a_horse's correct answer (after discussion with OP in comments).
Works with any (reasonably finite) number of levels in the recursion.

Total price for given project_id

WITH RECURSIVE cte AS (
   SELECT project_id AS project_parent, project_value
   FROM   projects
   WHERE  project_id = 1 -- enter id of the base project here !

   UNION  ALL 
   SELECT p.project_id, p.project_value
   FROM   cte
   JOIN   projects p USING (project_parent)
)
SELECT sum(project_value) AS total_value
FROM   cte;

Total price for all projects

WITH RECURSIVE cte AS (
   SELECT project_id, project_id AS project_parent, project_value
   FROM   projects
   WHERE  project_parent IS NULL  -- all base projects

   UNION  ALL 
   SELECT c.project_id, p.project_id, p.project_value
   FROM   cte c
   JOIN   projects p USING (project_parent)
   )
SELECT project_id, sum(project_value) AS total_value
FROM   cte
GROUP  BY 1
ORDER  BY 1;

db<>fiddle here (with correct test case)
Old sqlfiddle

like image 178
Erwin Brandstetter Avatar answered Jan 09 '23 04:01

Erwin Brandstetter


Something like this:

with recursive project_tree as (
   select project_id, 
          project_name,
          project_value,
          project_parent
   from projects
   where project_id = 42 -- << the id of the "base" project
   union all 
   select p.project_id,
          p.project_name,
          p.project_value,
          p.project_parent
   from projects p
     join project_tree t on t.project_id = p.project_parent
)
select sum(project_value)
from project_tree;

The first part of the union needs to select the project that you want to evaluate (the "child" project). The recursive join will the walk up the tree and retrieve all parent projects.

like image 43
a_horse_with_no_name Avatar answered Jan 09 '23 03:01

a_horse_with_no_name