2017-08-21 1 views
0

J'ai table avec des données d'événements contient deux dates: 1) Date de début de l'événement (par exemple 2017-03-01 05:30:00) 2) Fin de l'événement Date (. par exemple 2017-03-01 06:10:00)Compter la durée des événements et le groupe par intervalles de 24 heures SQL

Je dois créer un rapport avec la structure, où les données sont somme tous les événements de l'année jour et une heure donnée groupés en intervalle de temps de 24 heures, comme ça:

Deux lignes:

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 

Et résultat Question:

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 
... 

Est-ce une manière élégante de faire cela dans Oracle SQL? J'ai écrit quelques psudocode en premier moment, (où je compte diff entre le début et la fin, vérifier combien d'heures ont été affectées, et assigner au bon intervalle), mais peut-être plus natif et une meilleure performance?

Merci pour votre aide.

+0

Avez-vous essayé la fonction de recherche ici? il y a beaucoup de réponses à ce sujet –

+0

Bien sûr, mais toutes les réponses fondées sont axées sur le comptage des événements dans l'intervalle, ne compte pas la durée de l'événement dans la table d'intervalle de 24h en langage SQL pur. – wukkie

+0

@TimBiegeleisen Pourquoi 55 minutes? 07-08 => 15 minutes (07:45 - 08:00 [E1]) 08-09 => 30 minutes (08:00 - 08:15 [E1] + 08:25: - 08:40 [E2 ]) – wukkie

Répondre

2

Voici une requête qui répond à vos besoins. Notez toutefois que cela ne fonctionne que pour les événements qui se terminent en 1 million de minutes, c'est-à-dire environ 2 ans.

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; 

C'est un peu lent cependant. Je n'ai pas passé de temps à penser à la performance. Mais ça marche. Voici la sortie.

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 

Et si nous changeons la date de fin de « événement 2 » 02/03/2017 (ie l'événement a duré un jour et 15 min et 40 min dans la 8ème heure), nous pouvons voir que les changements de sortie pour refléter la durée de 48 heures.

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 
+0

Je testais pour une possibilité de plus où un événement dure plus de deux jours ici il ne montrera pas les jours qui sont entre. c'est-à-dire si nous changeons la date de fin de 'l'événement 2' en 2017-03-03 alors les entrées pour le jour 2017-03-02 ne seront pas présentes dans la sortie. –

+0

@pratikgarg bonne prise! mis à jour le code. Était une solution simple. –