Home > Enterprise >  Split postgresql table column into multiple columns
Split postgresql table column into multiple columns

Time:02-18

Ok so this is the partial query, working 100% as we speak. There's a comment as you can see where the query of the 4 columns is supposed to go.

SELECT DISTINCT
    p.id AS "Project ID",
    p.title AS "Project Title",
    p.summary AS "Project Summary",
    to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
    to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
    to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
    to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
    d.name AS "Center/Department",

/* THIS IS WHERE THE Query has to go.*/

 SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
        from grant_detail gd
                 JOIN grant_status gs on gd.status_id = gs.id
                 JOIN project_budget pb ON gd.id = pb.grant_id
        WHERE pb.project_id = p.id
        group by p.id),

        SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
        FROM organization o
                 JOIN organization_type ot ON ot.id = o.type_id
                 JOIN grant_detail gt ON gt.organization_id = o.id
                 JOIN project_budget pb ON pb.grant_id = gt.id
        WHERE pb.project_id = p.id
          AND ot.name = 'Funder'),

 SELECT string_agg(pc.name, ', ') AS "Categories"
        FROM project_project_categories ppc
                 JOIN project_category pc ON ppc.project_category_id = pc.id
        WHERE ppc.project_id = p.id),

    ( /*========= Student Researcher Full Name ( email)  =========*/
        SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
                                            s.last_name ||' ('||
                                            s.email ||')', ', ')
                            AS "Student Researcher"
        FROM project_stakeholder ps
                 JOIN stakeholder s ON s.id = ps.stakeholder_id
                 JOIN project_role pr ON ps.role_id = pr.id
        WHERE pr.name = 'Student Researcher'
          AND ps.project_id = p.id
        GROUP BY  p.id)

FROM
    /*CONTEXT*/
    project p

        LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
        LEFT JOIN department d ON p.department_id = d.id
        JOIN project_health ph ON p.health_id = ph.id
        JOIN project_status ps ON p.status_id = ps.id
-- WHERE
-- --         p.actual_start_date <= '__beforeActualStartDate'
-- --   AND p.actual_end_date >= '__afterActualEndDate'
-- --   AND p.expected_start_date <= '__beforeStartDate'
-- --   AND p.expected_end_date >= '__afterEndDate'
ORDER BY
    p.title


UPDATE: For Phelipe: You can see where I've commented my code in the place where the four columns need to be que

CodePudding user response:

Here is a fully dynamic solution that can work with a variable list of roles.

This solution relies on the creation of a composite type role_type which includes the list of roles and which can be called at the runtime :

CREATE OR REPLACE PROCEDURE role_type () LANGUAGE plpgsql AS
$$
DECLARE
  role_list text ;
BEGIN
  SELECT string_agg(r_name || ' text', ',' ORDER BY r_id)
    INTO role_list
    FROM role ;
  EXECUTE 'DROP TYPE IF EXISTS role_type' ;
  EXECUTE 'CREATE TYPE role_type AS (' || role_list || ')' ;
END ;
$$

After calling the procedure role_type(), the result is provided by a simple query using the jsonb_object_agg and jsonb_populate_record standard functions :

CALL role_type () ;

SELECT a.p_id AS "Project id"
     , a.p_title AS "Project Title"
     , (jsonb_populate_record(NULL :: role_type, jsonb_object_agg(lower(a.r_name), (a.usr_list)))).*
  FROM
     ( SELECT p.p_id
            , p.p_title
            , r.r_name
            , string_agg(u.fname || ' ' || u.lname, ', ') AS usr_list
         FROM project_usr AS pu
        INNER JOIN project AS p
           ON p.p_id = pu.project_id
        INNER JOIN usr AS u
           ON u.usr_id = pu.usr_id
        INNER JOIN role AS r
           ON r.r_id = pu.role_id
        GROUP BY p.p_id, p.p_title, r.r_name
     ) AS a
 GROUP BY a.p_id, a.p_title
 ORDER BY a.p_id

Finally, instead of calling the role_type() procedure each time before executing the query, it can be called by trigger when the role list is changing :

CREATE OR REPLACE FUNCTION role_after_insert_update ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
  CALL role_type () ;
  RETURN NEW ;
END ;
$$ ;

CREATE OR REPLACE TRIGGER role_after_insert_update AFTER INSERT OR UPDATE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_insert_update() ;

CREATE OR REPLACE FUNCTION role_after_delete ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
  CALL role_type () ;
  RETURN OLD ;
END ;
$$ ;

CREATE OR REPLACE TRIGGER role_after_delete AFTER DELETE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_delete() ;

see the full test result in dbfiddle

CodePudding user response:

you can do this but it supposes that role table is always the same with same id

select pu.project_id, 
case r.r_id when 1 then u.fname||' '||u.lname else '' end as Teacher,
case r.r_id when 2 then u.fname||' '||u.lname else '' end as Student,
case r.r_id when 3 then u.fname||' '||u.lname else '' end as Volunteer
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id

Result here

Crosstab

You could use crosstab too but the problem will be the same, you have to know the roles

select * from crosstab($$
select pu.project_id,r.r_name, u.fname||' '||u.lname
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
$$,'select r_name from role')
as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)

Result here

Maybe something like this (not tested, not enough informations)

with r as (
select * from crosstab($$
    select pu.project_id,r.r_name, u.fname||' '||u.lname
    from project_usr pu join role r on r.r_id = pu.role_id
    join usr u on pu.usr_id = u.usr_id
    $$,'select r_name from role')
    as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)
)
SELECT DISTINCT
    p.id AS "Project ID",
    p.title AS "Project Title",
    p.summary AS "Project Summary",
    to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
    to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
    to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
    to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
    d.name AS "Center/Department",

/* THIS IS WHERE THE Query has to go.*/
    r.teacher,
    r.student,
    r.volunteer,
    
    
 SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
        from grant_detail gd
                 JOIN grant_status gs on gd.status_id = gs.id
                 JOIN project_budget pb ON gd.id = pb.grant_id
        WHERE pb.project_id = p.id
        group by p.id),

        SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
        FROM organization o
                 JOIN organization_type ot ON ot.id = o.type_id
                 JOIN grant_detail gt ON gt.organization_id = o.id
                 JOIN project_budget pb ON pb.grant_id = gt.id
        WHERE pb.project_id = p.id
          AND ot.name = 'Funder'),

 SELECT string_agg(pc.name, ', ') AS "Categories"
        FROM project_project_categories ppc
                 JOIN project_category pc ON ppc.project_category_id = pc.id
        WHERE ppc.project_id = p.id),

    ( /*========= Student Researcher Full Name ( email)  =========*/
        SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
                                            s.last_name ||' ('||
                                            s.email ||')', ', ')
                            AS "Student Researcher"
        FROM project_stakeholder ps
                 JOIN stakeholder s ON s.id = ps.stakeholder_id
                 JOIN project_role pr ON ps.role_id = pr.id
        WHERE pr.name = 'Student Researcher'
          AND ps.project_id = p.id
        GROUP BY  p.id)

FROM
    /*CONTEXT*/
    project p

        LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
        LEFT JOIN department d ON p.department_id = d.id
        JOIN project_health ph ON p.health_id = ph.id
        JOIN project_status ps ON p.status_id = ps.id

/* don't know the key */ join r on ...

ORDER BY
    p.title
  • Related