Somma intervallo di date all'interno della stessa colonna


10

Come è possibile sommare al meglio le differenze di un intervallo di date nella stessa colonna tra righe interlacciate? Ho una colonna Datetime e voglio calcolare la differenza tra le righe. Voglio la differenza in secondi. Questa domanda non riguarda come ottenere una differenza tra 2 timestamp, ma è più focalizzata su come calcolare in modo più efficiente tra le righe nella stessa tabella. Nel mio caso ogni riga ha un tipo di evento datetime che collega logicamente 2 righe insieme.

Dettagli relativi a come raggruppare i tipi di eventi di inizio e fine. (Andriy M's Question) Inizio e fine "dovrebbero" essere consecutivi. Se un inizio non ha una fine successiva, dovrebbe essere escluso dalla somma. Passa al prossimo inizio per vedere se ha una fine. Alla somma dei secondi totali devono essere aggiunte solo coppie consecutive Inizio - Fine.

Lavorare in postgresql 9.x ...

Dati di esempio in tabella;

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

Nota, tutte le date di inizio e fine saranno sequenziali.

Ecco il mio primo tentativo. Sembra funzionare.

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 

Il mio primo tentativo si basa su un ottimo esempio di StackOverflow Postgres 9.1 - Ottenere il valore successivo

Nota; Puoi commentare GROUP BY e SUM e annullare il commento degli inizi. * Per ottenere un record per ogni singola durata andando nella somma.

Risposte:


10

È possibile utilizzare la LEADfunzione analitica per ottenere la riga successiva eventtypee eventdateaccanto ai dati della riga corrente:

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'

Utilizzando la query sopra come tabella derivata, è possibile filtrare ulteriormente l'output eventtype = 'START' AND nexttype = 'END'e ottenere la differenza 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'
;

Come leggera variazione, è possibile implementare la sottoquery come 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'
;

Questa riscrittura può avere implicazioni per le prestazioni, perché a differenza di una tabella derivata, in PostgreSQL si materializza un CTE. I test dovrebbero rivelare se c'è una differenza e, in tal caso, quale opzione è migliore per te.


Andriy, grazie! Proverò la versione CTE e vedrò come aiuta.
C Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.