Reputation: 3311
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
Upvotes: 0
Views: 567
Reputation: 6130
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
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
Upvotes: 1
Reputation: 127086
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
Upvotes: 1