Сумма Интервал Даты в том же столбце

10

Как лучше всего суммировать различия в диапазоне дат в одном столбце между чередующимися строками? У меня есть столбец Datetime и я хочу рассчитать разницу между строками. Я хочу разницу в секундах. Этот вопрос не о том, как получить разницу между двумя временными метками, а о том, как наиболее эффективно рассчитать между строками в одной таблице. В моем случае каждая строка имеет тип события datetime, который логически связывает 2 строки.

Подробности, связанные с тем, как группировать типы событий начала и конца. (Вопрос Андрея М). Начало и конец «должны» быть последовательными. Если начало не имеет последующего конца, его следует исключить из суммы. Переход к следующему началу, чтобы увидеть, есть ли у него конец. Только последовательные пары начала и конца должны быть добавлены к сумме общих секунд.

Работа в postgresql 9.x ...

Пример данных в таблице;

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

Обратите внимание, что все даты начала и окончания будут последовательными.

Вот моя первая попытка. Кажется, работает.

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 

Моя первая попытка основана на отличном примере из stackoverflow Postgres 9.1 - Получение следующего значения

Заметка; Вы можете комментировать GROUP BY и SUM и снимать комментарии стартов. *, Чтобы получить запись для каждой отдельной продолжительности, входящей в сумму.

С Смит
источник

Ответы:

10

Вы можете использовать LEADаналитическую функцию для получения следующей строки eventtypeи данных eventdateтекущей строки:

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'

Используя приведенный выше запрос в качестве производной таблицы, вы можете дополнительно отфильтровать выходные данные eventtype = 'START' AND nexttype = 'END'и получить общую разницу:

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'
;

В качестве небольшого изменения вы можете реализовать подзапрос в виде 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'
;

Это переписывание может повлиять на производительность, поскольку в отличие от производной таблицы, CTE материализуется в PostgreSQL. Тестирование должно выявить разницу, и если да, то какой вариант лучше для вас.

Андрей М
источник
Андрей, спасибо! Я попробую версию CTE и посмотрю, как она помогает.
С Смит