Home > other >  PostgreSQL - Select splitted rows based on a column value
PostgreSQL - Select splitted rows based on a column value

Time:08-03

Could someone please suggest a query which splits items by working minutes per hour?

Source table

start_timestamp item_id total_working_minutes
2021-02-01 14:10 A 120
2021-02-01 14:30 B 20
2021-02-01 16:30 A 10

Expected result

timestamp_by_hour item_id working_minutes
2021-02-01 14:00 A 50
2021-02-01 14:00 B 20
2021-02-01 15:00 A 60
2021-02-01 16:00 A 20

Thanks in advance!

CodePudding user response:

You can accomplish this using a recursive query, which should work in both Redshift and PostgreSQL. The query uses a Common Table Expression to introduce a toggle, in order to generate two rows for each line in the original table:

  • The hour and amount of minutes worked the first hour
  • The following hour and remaining minutes (if any)

Then, the recursion splits each of the following hours the same way, until the remaining minutes are 60 or less.

Finally, the output is filtered to only include the first row of each split (pt = 0), and the results are aggregated by hour and ID.

with recursive 
  toggle(t) as (values(0), (1)),
  split_times(timestamp_by_hour, item_id, working_minutes, pt) as
(
  select case t 
    when 0 then date_trunc('hour', start_timestamp)
    else date_trunc('hour', start_timestamp   '1 hour')
  end,
  item_id,
  case t 
    when 0 
    then least(total_working_minutes, 60 - extract(minutes from start_timestamp))
    else total_working_minutes - 60   extract(minutes from start_timestamp)
  end,
  t
  from work_time cross join toggle
  where t = 0 or total_working_minutes > 60
union all
  select case t 
    when 0 then timestamp_by_hour
    else timestamp_by_hour   '1 hour'
  end,
  item_id,
  case t 
    when 0 
    then least(working_minutes, 60)
    else working_minutes - 60
  end,
  t
  from split_times cross join toggle
  where pt = 1 and (t = 0 or working_minutes > 60)
)
select timestamp_by_hour, item_id, sum(working_minutes) working_minutes
from split_times
where pt = 0
group by timestamp_by_hour, item_id
order by timestamp_by_hour, item_id;
  • Related