简体   繁体   中英

Postgresql left join date_trunc with default values

I have 3 tables which I'm querying to get the data based on different conditions. I have from and to params and these are the ones I'm using to create a range of time in which I'm looking for the data in those tables.

For instance if I have from equals to '2020-07-01' and to equals to '2020-08-01' I'm expecting to receive the grouped row values of the tables by week, if in some case some of the weeks don't have records I want to return 0, if some tables have records for the same week, I'd like to sum them.

Currently I have this:

SELECT d.day, COALESCE(t.total, 0)
FROM  (
  SELECT day::date
  FROM generate_series(timestamp '2020-07-01',
                       timestamp '2020-08-01',
                       interval  '1 week') day
) d
LEFT JOIN (
  SELECT date AS day,
         SUM(total)
  FROM table1
  WHERE id = '1'
  AND date BETWEEN '2020-07-01' AND '2020-08-01'
  GROUP  BY day
) t USING (day)
ORDER BY d.day;

I'm generating a series of dates grouped by week, and on top of that I'm doing adding a left join. Now for some reason, it only works if the dates match completely, otherwise COALESCE(t.total, 0) returns 0 even if in that week the SUM(total) is not 0.

The same way I'm applying the LEFT JOIN, I'm using other left joins with other tables in the same query, so I'm falling with the same problem.

Please see if this works for you. Whenever you find yourself aggregating more than once, ask yourself whether it is necessary.

Rather than try to match on discrete days, use time ranges.

with limits as (
  select '2020-07-01'::timestamp as dt_start, 
         '2020-08-01'::timestamp as dt_end
), weeks as (
  SELECT x.day::date as day, least(x.day::date + 7, dt_end::date) as day_end
    FROM limits l
         CROSS JOIN LATERAL 
           generate_series(l.dt_start, l.dt_end, interval '1 week') as x(day)
   WHERE x.day::date != least(x.day::date + 7, dt_end::date)
), t1 as (
  select w.day, 
         sum(coalesce(t.total, 0)) as t1total
    from weeks w
         left join table1 t
                on t.id = 1
               and t.date >= w.day 
               and t.date < w.day_end
   group by w.day
), t2 as (
  select w.day, 
         sum(coalesce(t.sum_measure, 0)) as t2total
    from weeks w
         left join table2 t
                on t.something = 'whatever'
               and t.date >= w.day 
               and t.date < w.day_end
   group by w.day
)
select t1.day,
       t1.t1total,
       t2.t2total
  from t1
  join t2 on t2.day = t1.day;

You can keep adding tables like that with CTEs.

My earlier example with multiple left join was bad because it blows out the rows due to a lack of join conditions between the left-joined tables.

There is an interesting corner case for eg 2019-02-01 to 2019-03-01 which returns an empty interval as the last week. I have updated to filter that out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM