简体   繁体   中英

postgres sql GROUP BY DATE with LEFT JOIN does not list ALL records from the right table

I am attempting the group by date of an attendance timestamp and at the same time list all employees of the grouped date. And regardless if there is entry in the attendance, I'd like to list all available employee.

Here is my attempt to produce the query, but it lacks listing all the employees for every generated date.

Please refer to my attempted sql code: https://dbfiddle.uk/?rdbms=postgres_13&fiddle=a971a7ac01fb2f4bc7458983c40a24bf

SELECT 
    a.created_at::date as created_date,
    a.employee_id,
    e.name,
    e.division_id,
    MIN(a.created_at::time) FILTER (WHERE a.activity_type = 1) as min_time_in,
    MAX(a.created_at::time) FILTER (WHERE a.activity_type = 2) as max_time_out
FROM
   attendance a
LEFT JOIN employee e on e.id = a.employee_id   
GROUP BY created_date, a.employee_id, e.name, e.division_id
ORDER BY created_date, a.employee_id

the result shows like this:

created_date    employee_id name    division_id min_time_in max_time_out
2020-11-18  1   John                    1       07:10:25    20:01:05
2020-11-18  2   Amber                   2       07:30:25    19:10:25
2020-11-18  3   Mike                    1       07:50:25    19:22:38
2020-11-19  1   John                    1       07:11:23    23:21:53

However, my goal is to list all employees and group the employee_id records by date. regardless if there is attendance by the employee, it should show a row of the date. How can I generate a query that produce like below:

created_date    employee_id name    division_id min_time_in max_time_out
2020-11-18  1   John                    1       07:10:25    20:01:05
2020-11-18  2   Amber                   2       07:30:25    19:10:25
2020-11-18  3   Mike                    1       07:50:25    19:22:38
2020-11-18  4   Jimmy                   1       null         null
2020-11-18  5   Kathy                   2       null         null
2020-11-19  1   John                    1       07:11:23    23:21:53
2020-11-19  2   Amber                   2       null         null
2020-11-19  3   Mike                    1       null         null
2020-11-19  4   Jimmy                   1       null         null
2020-11-19  5   Kathy                   2       null         null

This should work:

SELECT 
     d::date AS created_date
  ,  e.id
  ,  e.name
  ,  e.division_id
  ,  MIN(a.created_at::time) FILTER (WHERE a.activity_type = 1) as min_time_in
  ,  MAX(a.created_at::time) FILTER (WHERE a.activity_type = 2) as max_time_out
FROM    (SELECT MIN(created_at), MAX(created_at) FROM attendance) AS r(startdate,enddate)
  , generate_series(
        startdate::timestamp, 
        enddate::timestamp, 
        interval '1 day') g(d)
    CROSS JOIN  employee e
    LEFT JOIN   attendance a ON a.created_at::date = d::date AND e.id = a.employee_id
GROUP BY 
    created_date
  , e.id
  , e.name
  , e.division_id
ORDER BY 
    created_date
  , e.id;

edit: id from employee selected, instead of employee_id

Just cross join the distinct dates from your attendance table with employee table. You can try like below:

select
a1. date,
e.id,
e.name,
e.division_id,
MIN(a.created_at::time) FILTER (WHERE a.activity_type = 1  ) as min_time_in,
MAX(a.created_at::time) FILTER (WHERE a.activity_type = 2) as max_time_out
from 
employee e left join (select distinct date(created_at) "date" from attendance ) a1 on true
left JOIN attendance a on e.id=a.employee_id and date(a.created_at)=a1.date
group by 1,2,3,4
order by 1,2

Note: left join on true is equivalent to cross join

DEMO

UPDATE:

If you want the output for all dates between min date and max date.

with cte as (select min(created_at)::date "mindate", max(created_at)::date "maxdate" from attendance )

select
a1. date,
e.id,
e.name,
e.division_id,
MIN(a.created_at::time) FILTER (WHERE a.activity_type = 1  ) as min_time_in,
MAX(a.created_at::time) FILTER (WHERE a.activity_type = 2) as max_time_out
from 
employee e 
left join (select g.date from cte, generate_series(cte.mindate,cte.maxdate,interval '1 day') g(date) ) a1 on true
left JOIN attendance a on e.id=a.employee_id and date(a.created_at)=a1.date
group by 1,2,3,4
order by 1,2

DEMO

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