Home > Software design >  Postgresql sum with transpose
Postgresql sum with transpose

Time:01-20

My input table has count of values for events belonging to specific categories for every year.

events year category
16 2022 A
13 2022 B
3 2022 C
113 2022 D1
1 2022 D2
2 2022 D3
5 2023 A
8 2023 B

I need to show a sum of the events for each category for each year, in the specific case I'd also need to aggregate D1 D2 D3 together).

Expected Output:

year events-total category A category B category C category D
2022 148 16 13 3 116
2023 13 5 8 0 0

The main problem of my attempts comes from the fact that there are challenges from: 1 - years that dont have events in certain categories, 2 - the years are only 2 for now, but will grow as time goes by

I tried breaking the sql with several views but cannot preview in future years the cases where some categories don't have values.

I'd like to have a sql query that handles future cases.

Thanks for any help.

CodePudding user response:

This is the case of a pivoting task.

In your specific case the CASE expressions help you to gather specific count of events to be summed up, then you can aggregate on your year_ values. You can detect the D values with the LIKE operator.

SELECT year_, 
       SUM(events_)                                              AS events_total,
       SUM(CASE WHEN category    = 'A'  THEN events_ ELSE 0 END) AS categoryA,
       SUM(CASE WHEN category    = 'B'  THEN events_ ELSE 0 END) AS categoryB,
       SUM(CASE WHEN category    = 'C'  THEN events_ ELSE 0 END) AS categoryC,
       SUM(CASE WHEN category LIKE 'D%' THEN events_ ELSE 0 END) AS categoryD
FROM tab
GROUP BY year_

Check the demo here.


Using the FILTER operator, combined with the COALESCE function, would come out like this:

SELECT year_, 
       SUM(events_)                                               AS events_total,
       COALESCE(SUM(events_) FILTER(WHERE category    = 'A' ), 0) AS catA,
       COALESCE(SUM(events_) FILTER(WHERE category    = 'B' ), 0) AS catB,
       COALESCE(SUM(events_) FILTER(WHERE category    = 'C' ), 0) AS catC,
       COALESCE(SUM(events_) FILTER(WHERE category LIKE 'D%'), 0) AS catD
FROM tab
GROUP BY year_

Check the demo here.

  • Related