Как рассчитать наименьший промежуток времени между последовательными событиями? - PullRequest
2 голосов
/ 23 мая 2009

Допустим, у меня есть таблица со столбцом метки времени, полным записей, и я хочу вычислить наименьшую разницу во времени между двумя последовательными записями, используя только один запрос.

Может быть, стол, как ...

CREATE TABLE `temperatures` (
  `temperature` double,
  `time` timestamp DEFAULT CURRENT_TIMESTAMP
);

Ответы [ 4 ]

4 голосов
/ 23 мая 2009

Вам нужны аналитические функции LAG и MIN.

Они отсутствуют в MySQL, но их легко эмулировать с помощью переменных сеанса.

Этот запрос возвращает все различия между последовательными записями:

SELECT  (temperature - @r) AS diff,
        @r := temperature
FROM    (
        SELECT  @r := 0
        ) vars,
        temperatures
ORDER BY
        time

Этот возвращает минимальную разницу во времени:

SELECT  (
        SELECT  id,
                @m := LEAST(@m, TIMEDIFF(time, @r)) AS mindiff,
                @r := time
        FROM    (
                SELECT  @m := INTERVAL 100 YEAR,
                        @r := NULL
                ) vars,
                temperatures
        ORDER BY
                time, id
        ) qo
WHERE   qo.id = 
        (
        SELECT  id
        FROM    temperatures
        ORDER BY
                time DESC, id DESC
        LIMIT 1
        )

См. Эту статью в моем блоге о том, как эмулировать аналитические функции в MySQL:

Если вы добавите PRIMARY KEY к себе в таблицу (что вы всегда должны делать, всегда делайте!), То вы можете использовать более SQL -ish решение:

SELECT  temperature -
        (
        SELECT temperature
        FROM   temperatures ti
        WHERE  (ti.timestamp, ti.id) < (to.timestamp, to.id)
        ORDER BY
               ti.timestamp DESC, ti.id DESC
        LIMIT 1
        )
FROM    temperatures to
ORDER BY
       to.timestamp, to.id

Это решение, однако, весьма неэффективно в MySQL из-за ошибки 20111 .

Подзапрос не будет использовать путь доступа range, хотя он будет использовать индекс для (timestamp, id) для упорядочения.

Это можно обойти, создав UDF, который возвращает предыдущую температуру, учитывая текущую запись id.

См. Эту статью в моем блоге для деталей:

Если вы не используете какие-либо условия фильтрации, то решение, использующее переменную сеанса, будет наиболее эффективным, хотя и MySQL специфичным.

Подобные решения для SQL Server будут выглядеть так:

SELECT  temperature -
        (
        SELECT TOP 1 temperature
        FROM   temperatures ti
        WHERE  ti.timestamp < to.timestamp
               OR (ti.timestamp = to.timestamp AND ti.id < to.id)
        ORDER BY
               ti.timestamp DESC, ti.id DESC
        )
FROM    temperatures to
ORDER BY
       to.timestamp, to.id

и

SELECT  MIN(mindiff)
FROM    (
        SELECT  timestamp -
                (
                SELECT TOP 1 timestamp
                FROM   temperatures ti
                WHERE  ti.timestamp < to.timestamp
                       OR (ti.timestamp = to.timestamp AND ti.id < to.id)
                ORDER BY
                       ti.timestamp DESC, ti.id DESC
                ) AS mindiff
        FROM    temperatures to
        ORDER BY
               to.timestamp, to.id
        ) q

В SQL Server это будет работать нормально, если у вас есть индекс на (timestamp, id) (или просто на (timestamp), если ваш PRIMARY KEY кластеризован)

3 голосов
/ 23 мая 2009

Предполагая, что на отметке времени имеется уникальное ограничение (чтобы не было двух записей одновременно):

SELECT MIN(timediff(t1.`time`, t2.`time`)) AS delta_t,
    FROM temperatures t1 JOIN temperatures t2 ON t1.`time` < t2.`time`

Это отвечает на вопросы довольно точно - и не передает другую полезную информацию (например, какие две метки времени или температуры).

2 голосов
/ 23 мая 2009

Попробуйте запрос, подобный этому:

select 
    cur.timestamp as CurrentTime,
    prev.timestamp as PreviousTime,
    timediff(cur.timestamp,prev.timestamp) as TimeDifference,
    cur.temperature - prev.temperature as TemperatureDifference
from temperatures cur
left join temperatures prev on prev.timestamp < cur.timestamp
left join temperatures inbetween
    on prev.timestamp < inbetween.timestamp
    and inbetween.timestamp < cur.timestamp
where inbetween.timestamp is null

Первое объединение ищет все предыдущие строки для текущей ("cur") строки. Второе соединение ищет ряды между первым и вторым рядом. Инструкция where говорит, что между первой и второй строкой не может быть никаких строк. Таким образом, вы получите список строк с их предыдущей строкой.

0 голосов
/ 23 мая 2009

Вы можете попробовать это:

SELECT
    T1.*,
    (SELECT MIN(T2.time)
     FROM temperatures T2
     WHERE T2.time > T1.time)-T1.time diff
FROM
    temperatures T1
ORDER BY
    T1.time
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...