Somme de l'intervalle des dates dans la même colonne

10

Comment additionnez-vous au mieux les différences d'une plage de dates dans la même colonne entre des lignes entrelacées? J'ai une colonne Datetime et je veux calculer la différence entre les lignes. Je veux la différence en secondes. Cette question n'est pas sur la façon d'obtenir une différence entre 2 horodatages, mais est plus axée sur la façon de calculer le plus efficacement entre les lignes sur la même table. Dans mon cas, chaque ligne a un type d'événement datetime qui relie logiquement 2 lignes ensemble.

Détails Liés à la façon de regrouper les types d'événements de début et de fin. (Question d'Andriy M) Les départs et les fins "devraient" être consécutifs. Si un début n'a pas de fin ultérieure, il doit être exclu de la somme. Passer au début suivant pour voir s'il a une fin. Seules les paires Début - Fin consécutives doivent être ajoutées à la somme des secondes totales.

Travailler dans postgresql 9.x ...

Exemple de données dans le tableau;

eventtype, eventdate
START, 2015-01-01 14:00
END, 2015-01-01 14:25
START, 2015-01-01 14:30
END, 2015-01-01 14:43
START, 2015-01-01 14:45
END, 2015-01-01 14:49
START, 2015-01-01 14:52
END, 2015-01-01 14:55

Remarque: toutes les dates de début et de fin seront séquentielles.

Voici ma première tentative. Semble fonctionner.

SELECT 
-- starts.*
SUM(EXTRACT(EPOCH FROM (eventdate_next - eventdate))) AS duration_seconds
FROM
( 
    WITH x AS (
        SELECT *, dense_rank() OVER (ORDER BY eventdate) AS rnk
        FROM   table
        AND eventdate > '2015-01-01 00:00:00.00'
        AND eventdate < '2016-01-01 23:59:59.59' 
        )
    SELECT x.eventdate, x.eventtype, y.eventdate AS eventdate_next,  y.eventtype AS eventtype_next
    FROM   x
    LEFT   JOIN (SELECT DISTINCT eventdate, eventtype, rnk FROM x) y ON y.rnk = (x.rnk + 1)
    ORDER  BY x.eventdate
) starts
WHERE
eventtype = 'START'   
GROUP BY eventtype 

Ma première tentative est basée sur un excellent exemple de stackoverflow Postgres 9.1 - Obtenir la valeur suivante

Remarque; Vous pouvez commenter le GROUP BY et le SUM et annuler le commentaire des départs. * Pour obtenir un enregistrement pour chaque durée individuelle entrant dans la somme.

C Smith
la source

Réponses:

10

Vous pouvez utiliser la LEADfonction analytique pour obtenir la ligne suivante eventtypeet à eventdatecôté des données de la ligne actuelle:

SELECT
  eventtype,
  eventdate,
  LEAD(eventtype) OVER (ORDER BY eventdate) AS nexttype,
  LEAD(eventdate) OVER (ORDER BY eventdate) AS nextdate
FROM
  atable
WHERE
      eventdate >= '2015-01-01 00:00:00.00'
  AND eventdate <  '2016-01-01 23:59:59.59'

En utilisant la requête ci-dessus comme table dérivée, vous pouvez filtrer la sortie plus loin eventtype = 'START' AND nexttype = 'END'et obtenir la différence totale:

SELECT
  SUM(EXTRACT(EPOCH FROM (nextdate - eventdate))) AS duration_seconds
FROM
  (
    SELECT
      eventtype,
      eventdate,
      LEAD(eventtype) OVER (ORDER BY eventdate) AS nexttype,
      LEAD(eventdate) OVER (ORDER BY eventdate) AS nextdate
    FROM
      atable
    WHERE
          eventdate >= '2015-01-01 00:00:00.00'
      AND eventdate <  '2016-01-01 23:59:59.59'
  ) AS s
WHERE
      eventtype = 'START'
  AND nexttype  = 'END'
;

En légère variation, vous pouvez implémenter la sous-requête en tant que CTE:

WITH cte AS
  (
    SELECT
      eventtype,
      eventdate,
      LEAD(eventtype) OVER (ORDER BY eventdate) AS nexttype,
      LEAD(eventdate) OVER (ORDER BY eventdate) AS nextdate
    FROM
      atable
    WHERE
          eventdate >= '2015-01-01 00:00:00.00'
      AND eventdate <  '2016-01-01 23:59:59.59'
  )
SELECT
  SUM(EXTRACT(EPOCH FROM (nextdate - eventdate))) AS duration_seconds
FROM
  cte
WHERE
      eventtype = 'START'
  AND nexttype  = 'END'
;

Cette réécriture peut avoir des implications sur les performances, car contrairement à une table dérivée, un CTE est matérialisé dans PostgreSQL. Les tests devraient révéler s'il y a une différence et, dans l'affirmative, quelle option vous convient le mieux.

Andriy M
la source
Andriy, merci! Je vais essayer la version CTE et voir comment ça aide.
C Smith