People of Stack Overflow!
Thanks for taking the time to read this question. What I am trying to accomplish is to pivot some data all from just one table.
The original table has multiple datetime entries of specific events (e.g. when the customer was added add_time and when the customer was lost lost_time).
This is one part of two rows of the deals table:
| id | add_time | last_mail_time | lost_time | 
|---|---|---|---|
| 5 | 2020-03-24 09:29:24 | 2020-04-03 13:20:29 | NULL | 
| 310 | 2020-03-24 09:29:24 | NULL | 2020-04-03 13:20:29 | 
I want to create a view of this table. A view that has one row for each distinct date and counts the number of events at this specific time.
This is the goal (times do not match with the example!): 

I have working code, like this:
SELECT DISTINCT
    change_datetime,
    (SELECT COUNT(add_time) as add_time_count FROM deals WHERE add_time::date = change_datetime),
    (SELECT COUNT(lost_time) as lost_time_count FROM deals WHERE lost_time::date = change_datetime) 
FROM (
    SELECT
        add_time::date AS change_datetime
    FROM
        deals
    UNION ALL
    SELECT
        lost_time::date AS change_datetime
    FROM
        deals
    ) AS foo
WHERE change_datetime IS NOT NULL
ORDER BY
    change_datetime;
but this has some ugly O(n2) queries and takes a lot of time.
Is there a better, more performant way to achieve this?
Thanks!!
Advertisement
Answer
You can use a lateral join to unpivot and then aggregate:
select t::date,
       count(*) filter (where which = 'add'),
       count(*) filter (where which = 'mail'),
       count(*) filter (where which = 'lost')
from deals d cross join lateral
     (values (add_time, 'add'),
             (last_mail_time, 'mail'),
             (lost_time, 'lost')
     ) v(t, which)
group by t::date;