以24小时为间隔计算事件和组的持续时间

时间:2017-08-21 13:31:31

标签: sql oracle report intervals

我有事件数据的表包含两个日期: 1)活动开始日期(例如2017-03-01 05:30:00) 2)活动结束日期(例如2017-03-01 06:10:00)

我需要创建包含结构的报告,其中数据是在24小时时间间隔内分组给定年 - 日和小时的所有事件的总和,如下所示:

两行:

Id      | Start date       | End date 
Event 1 | 2017-03-01 07:45 | 2017-03-01 08:15  
Event 2 | 2017-03-01 08:25 | 2017-03-01 08:40

查询结果:

Year-Month-Day | Hours | (Activity time in seconds or minutes - here minutes) 
2017-03-01     |    00 | 0 
2017-03-01     |    01 | 0 
2017-03-01     |    02 | 0
2017-03-01     |    03 | 0 
2017-03-01     |    04 | 0 
2017-03-01     |    05 | 0
2017-03-01     |    06 | 0 
2017-03-01     |    07 | 15
2017-03-01     |    08 | 30 
2017-03-01     |    09 | 00 
...

在Oracle SQL中执行此操作是否优雅?我在第一时间写了一些psudocode,(我在开始和结束时间之间计算差异,检查受影响的小时数,并分配到正确的时间间隔),但是可能存在更多本机和更好的性能解决方案 - 比如聚合函数等?< / p>

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

这是一个可以满足您需求的查询。 但请注意,这仅适用于在100万分钟内完成的事件,即大约2年。

WITH event
     AS (SELECT 'Event 1' AS id,
                '2017-03-01 07:45' AS start_date,
                '2017-03-01 08:15' AS end_date
         FROM   DUAL
         UNION ALL
         SELECT 'Event 2' AS id,
                '2017-03-01 08:25' AS start_date,
                '2017-03-01 08:40' AS end_date
         FROM   DUAL),
     add_mins
     AS (SELECT LEVEL
                - 1
                   AS add_min
         FROM   DUAL
         CONNECT BY LEVEL <= 1000000),
     hrs_in_day
     AS (SELECT LEVEL
                - 1
                   AS hr
         FROM   DUAL
         CONNECT BY LEVEL <= 24),
     all_days_hrs
     AS (SELECT *
         FROM   (SELECT TO_CHAR (
                           (first_start_day
                            + LEVEL
                            - 1),
                           'YYYY-MM-DD'
                        )
                           AS curr_day
                 FROM   (SELECT MIN (curr_day) AS first_start_day,
                                MAX (curr_day) AS last_end_day
                         FROM   (SELECT TO_TIMESTAMP (start_date, 'YYYY-MM-DD HH24:MI')
                                           AS curr_day
                                 FROM   event
                                 UNION
                                 SELECT TO_TIMESTAMP (end_date, 'YYYY-MM-DD HH24:MI')
                                           AS curr_day
                                 FROM   event))
                 CONNECT BY (first_start_day
                             + LEVEL
                             - 1) < last_end_day),
                hrs_in_day)
SELECT hl.curr_day AS year_month_day,
       LPAD (hl.hr, 2, '0') AS hours,
       COUNT (h.curr_hr) AS activity_duration_in_min
FROM   all_days_hrs hl
       LEFT JOIN
       (SELECT id,
               start_time,
               end_time,
               curr_time,
               TO_CHAR (curr_time, 'YYYY-MM-DD') AS year_month_day,
               EXTRACT (HOUR FROM curr_time) AS curr_hr
        FROM   (SELECT id,
                       start_time,
                       end_time,
                       b.add_min,
                       start_time
                       + NUMTODSINTERVAL (b.add_min, 'minute')
                          AS curr_time
                FROM   (SELECT id,
                               start_time,
                               end_time,
                               EXTRACT (DAY FROM dur_interval) * 1440
                               + EXTRACT (HOUR FROM dur_interval) * 60
                               + EXTRACT (MINUTE FROM dur_interval)
                                  AS duration_in_min
                        FROM   (SELECT id,
                                       start_time,
                                       end_time,
                                       (end_time
                                        - start_time)
                                          AS dur_interval
                                FROM   (SELECT id,
                                               TO_TIMESTAMP (
                                                  start_date,
                                                  'YYYY-MM-DD HH24:MI'
                                               )
                                                  AS start_time,
                                               TO_TIMESTAMP (
                                                  end_date,
                                                  'YYYY-MM-DD HH24:MI'
                                               )
                                                  AS end_time
                                        FROM   event))) a,
                       add_mins b
                WHERE  b.add_min < a.duration_in_min)) h
          ON (hl.curr_day = h.year_month_day
              AND hl.hr = h.curr_hr)
GROUP BY hl.curr_day,
         hl.hr
ORDER BY year_month_day NULLS FIRST,
         hl.hr;

虽然有点慢。我没有花时间考虑性能。 但它的确有效。这是输出。

Year-Month-day  | Hours | Activity_Duration_in_min
    2017-03-01  | 00    | 0
    2017-03-01  | 01    | 0
    2017-03-01  | 02    | 0
    2017-03-01  | 03    | 0
    2017-03-01  | 04    | 0
    2017-03-01  | 05    | 0
    2017-03-01  | 06    | 0
    2017-03-01  | 07    | 15
    2017-03-01  | 08    | 30
    2017-03-01  | 09    | 0
    2017-03-01  | 10    | 0
    2017-03-01  | 11    | 0
    2017-03-01  | 12    | 0
    2017-03-01  | 13    | 0
    2017-03-01  | 14    | 0
    2017-03-01  | 15    | 0
    2017-03-01  | 16    | 0
    2017-03-01  | 17    | 0
    2017-03-01  | 18    | 0
    2017-03-01  | 19    | 0
    2017-03-01  | 20    | 0
    2017-03-01  | 21    | 0
    2017-03-01  | 22    | 0
    2017-03-01  | 23    | 0

如果我们更改“事件2”的结束日期&#39;到2017-03-02(即活动持续了一天,15分钟和第8小时40分钟)我们可以看到输出变化以反映48小时的持续时间。

Year-Month-day  | Hours | Activity_Duration_in_min
    2017-03-01  | 00    | 0
    2017-03-01  | 01    | 0
    2017-03-01  | 02    | 0
    2017-03-01  | 03    | 0
    2017-03-01  | 04    | 0
    2017-03-01  | 05    | 0
    2017-03-01  | 06    | 0
    2017-03-01  | 07    | 15
    2017-03-01  | 08    | 50
    2017-03-01  | 09    | 60
    2017-03-01  | 10    | 60
    2017-03-01  | 11    | 60
    2017-03-01  | 12    | 60
    2017-03-01  | 13    | 60
    2017-03-01  | 14    | 60
    2017-03-01  | 15    | 60
    2017-03-01  | 16    | 60
    2017-03-01  | 17    | 60
    2017-03-01  | 18    | 60
    2017-03-01  | 19    | 60
    2017-03-01  | 20    | 60
    2017-03-01  | 21    | 60
    2017-03-01  | 22    | 60
    2017-03-01  | 23    | 60
    2017-03-02  | 00    | 60
    2017-03-02  | 01    | 60
    2017-03-02  | 02    | 60
    2017-03-02  | 03    | 60
    2017-03-02  | 04    | 60
    2017-03-02  | 05    | 60
    2017-03-02  | 06    | 60
    2017-03-02  | 07    | 60
    2017-03-02  | 08    | 40
    2017-03-02  | 09    | 0
    2017-03-02  | 10    | 0
    2017-03-02  | 11    | 0
    2017-03-02  | 12    | 0
    2017-03-02  | 13    | 0
    2017-03-02  | 14    | 0
    2017-03-02  | 15    | 0
    2017-03-02  | 16    | 0
    2017-03-02  | 17    | 0
    2017-03-02  | 18    | 0
    2017-03-02  | 19    | 0
    2017-03-02  | 20    | 0
    2017-03-02  | 21    | 0
    2017-03-02  | 22    | 0
    2017-03-02  | 23    | 0