在几个均匀分布的时间间隔内分解时间聚合数据

Breaking down of time aggregated data on several evenly distributed intervals

数据库结构如下所示。 它只是代表员工的一行,正在处理的任务数量,开始的事实 activity 以及结束的事实和所用秒数的总摘要。

employee number_of_tasks start_act end_act total_seconds
AXF-6263 5 12:30 14:10 6000

我想以相等的间隔将其分解,每次 1 小时,另外总结总秒数,落入特定间隔。从而得到这样的结果。那么有什么 SQL 方法可以解决这个问题吗?

employee number_of_tasks start_act end_act total_seconds
AXF-6263 5 12:00 13:00 1800
AXF-6263 5 13:00 14:00 3600
AXF-6263 5 14:00 15:00 600

在此先感谢您的帮助!

使用 generate_series 并将其与您的 table 交叉连接。

-- Test case
create temporary table the_table (employee text, number_of_tasks integer, start_act time, end_act time, total_seconds integer);
insert into the_table values ('AXF-6263', 5, '12:30', '14:10', 6000);

-- Query
select employee, number_of_tasks, 
       h::time start_act, h::time  + interval '1 hour' end_act, 
       extract('epoch' from least(h::time + interval '1 hour', end_act) - greatest(h::time, start_act))::integer total_seconds
from the_table cross join lateral
     generate_series(date_trunc('hour', current_date + start_act), date_trunc('hour', current_date + end_act), interval '1 hour') h
order by h;
employee number_of_tasks start_act end_act total_seconds
AXF-6263 5 12:00:00 13:00:00 1800
AXF-6263 5 13:00:00 14:00:00 3600
AXF-6263 5 14:00:00 15:00:00 600

有趣的任务,我决定加入一个系列,然后找出他的重叠。

我的方法是使用灵活的时间单位,因此您可以更改为 days/weeks/etc

with employees AS (
  SELECT
    'AXF-6263' AS employee,
    5 AS number_of_tasks,
    '2022-03-25 12:30'::timestamp AS start_act,
    '2022-03-25 14:10'::timestamp AS end_act,
    6000 AS total_seconds
),

-- select a set of employees to work on
sample_set AS (
  SELECT
    employee,
    start_act,
    end_act
  FROM
    employees
  -- WHERE ...
  -- LIMIT ...
),

-- choose a unit for the interval
unit AS (
  SELECT
    'hour' AS unit,
    '1 hour'::interval AS interval
),

-- generate a full series of time intervals
time_spans AS (
  SELECT
    sample_set.employee,
    generate_series(
       min(date_trunc(unit.unit, start_act)),
       max(date_trunc(unit.unit, end_act)),
       unit.interval
     ) AS start_act
  FROM
    sample_set
  JOIN unit
    ON true
  GROUP BY
    employee,
    unit.interval
)

-- final results
SELECT
  employee,
  number_of_tasks,
  time_spans.start_act,
  time_spans.start_act + unit.interval AS end_act,
  CASE
    WHEN employees.start_act > time_spans.start_act THEN time_spans.start_act - employees.start_act + unit.interval
    WHEN employees.end_act < time_spans.start_act + unit.interval THEN employees.end_act - time_spans.start_act
    ELSE unit.interval
  END AS total_seconds
FROM
  employees
JOIN time_spans USING (employee)
JOIN unit ON
  true
ORDER BY
  employee,
  time_spans.start_act