Hmm... I didn't find a good way to model this requirement graphically.
But there's no need to write SQLScript; it's not too difficult in plain SQL.
start your table of time-"punches":
create column table punches (emp_id int, work_day date, time_in time, time_out time); insert into punches values (1, date'2015-07-01', time'07:30', time'11:30'); insert into punches values (1, date'2015-07-01', time'12:30', time'14:30'); insert into punches values (1, date'2015-07-01', time'14:45', time'16:30'); insert into punches values (2, date'2015-07-01', time'08:30', time'11:30'); insert into punches values (2, date'2015-07-01', time'12:30', time'15:30');
select * from punches;
EMP_ID WORK_DAY TIME_IN TIME_OUT
1 2015-07-01 07:30:00 11:30:00
1 2015-07-01 12:30:00 14:30:00
1 2015-07-01 14:45:00 16:30:00
2 2015-07-01 08:30:00 11:30:00
2 2015-07-01 12:30:00 15:30:00
Now we can find how many seconds have been worked in every streak:
select p.emp_id, p.work_day, p.time_in, p.time_out, seconds_between(p.time_in, p.time_out) work_secs from punches p order by p.emp_id, p.work_day;
EMP_ID WORK_DAY TIME_IN TIME_OUT WORK_SECS
1 2015-07-01 07:30:00 11:30:00 14400
1 2015-07-01 12:30:00 14:30:00 7200
1 2015-07-01 14:45:00 16:30:00 6300
2 2015-07-01 08:30:00 11:30:00 10800
2 2015-07-01 12:30:00 15:30:00 10800
Aggregating this on a employee and workday level now and calculating the work hours is trivial.
However, you asked for a hourly breakdown of worked minutes.
A key point for that is to know about all the possible working minutes.
That can be nicely done with an auxiliary table that contains all possible minutes of a day.
create column table allhours (minutes time);
To fill this table we need to use a little "generator" query:
insert into allhours (select top 1440 add_seconds (time'00:00', row_number() over () * 60) from objects);
As there are 1440 minutes in every day we here just generate an increasing range of numbers with the row_number() window function and use it to add the appropriate number of seconds to the midnight timestamp.
Using "objects" or "objects cross join objects" works, as this table is available on all SAP HANA databases and usually filled with at least a few thousand entries.
Checking the entries we find that all minutes seem to be there:
(select top 5 "MINUTES" from allhours order by "MINUTES" asc) UNION ALL (select top 5 "MINUTES" from allhours order by "MINUTES" desc);
MINUTES
00:01:00
00:02:00
00:03:00
00:04:00
00:05:00
00:00:00
23:59:00
23:58:00
23:57:00
23:56:00
Now it's pretty straight forward to find the amount of minutes for every hour per day and employee:
select p.emp_id, p.work_day, hour(h."MINUTES"), sum (1) as hours_sum from punches p inner join allhours h on h."MINUTES" >= p.time_in and h."MINUTES" < p.time_out group by p.emp_id, p.work_day, hour(h."MINUTES") order by p.emp_id, p.work_day, hour(h."MINUTES");
The sum(1) is used to count every worked minute once - even when we aggregate on the hour-level
Important here is to get the join condition right.
We want to count every worked minute - but not the time after a worker checked out.
Therefore we include every minute equal and larger to the time_in and smaller (but not equal) to the time_out.
EMP_ID WORK_DAY HOUR(MINUTES) HOURS_SUM
1 2015-07-01 7 30
1 2015-07-01 8 60
1 2015-07-01 9 60
1 2015-07-01 10 60
1 2015-07-01 11 30 //---> break at 11:30
1 2015-07-01 12 30 //---> start again at 12:30
1 2015-07-01 13 60
1 2015-07-01 14 45 //--> break at 14:30 until 14:45
1 2015-07-01 15 60
1 2015-07-01 16 30
2 2015-07-01 8 30
2 2015-07-01 9 60
2 2015-07-01 10 60
2 2015-07-01 11 30 //---> break at 11:30
2 2015-07-01 12 30 //---> start again at 12:30
2 2015-07-01 13 60
2 2015-07-01 14 60
2 2015-07-01 15 30
Compare this with the data we inserted in the beginning!
Maybe someone else can come up with some graphical modelling here ![]()
Cheers,
Lars