Найти и удалить записи, которые близки по времени - PullRequest
0 голосов
/ 08 июля 2019

Я регистрирую температуру в sqlite db, и датчик посылает пакет из ~ 6 показаний каждые ~ 40 секунд, чтобы убедиться, что он получен.Добавив свойство Unique в поле даты, я устранил большинство ошибок.Однако иногда часы тикают на 1 секунду во время серии, так что у меня есть записи с интервалом ~ 1 секунда.Я хотел бы сохранить только одно чтение для каждого 40-секундного пакета.Как я могу найти (и, следовательно, удалить) эти записи?

insDate             id  temp    humidity 
2019-07-08 11:34:07 176 41.36   70.0 
2019-07-08 11:34:46 176 41.36   70.0 
2019-07-08 11:34:47 176 41.36   70.0 
2019-07-08 11:35:26 176 41.36   70.0 
2019-07-08 11:36:05 176 41.36   70.0 
2019-07-08 11:36:06 176 41.36   70.0 
2019-07-08 11:36:45 176 41.36   70.0

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

Чтобы избежать проблем, обсуждаемых в комментариях, где математическое временное окно (с использованием оператора модуля) может произвольно отсекать / разбивать показания из одного и того же пакета, вместо этого мы можем проверять соседние записи, чтобы получить временную задержку между последовательными показаниями.Детали вопроса подразумевают, что показания в одном и том же пакете записей, вероятно, будут происходить в очень короткое время (в течение пары секунд) по сравнению с задержкой между пакетами (~ 40 секунд).Таким образом, чтобы получить одно чтение на пакет, выберите только те записи, которые имеют длительную задержку для следующей записи, где-то между 2 и 40 секундами ... что я несколько произвольно выбираю 10.

К счастью, sqlite поддерживает оконные функции , которые позволяют сравнивать соседние строки с использованием базовых запросов SQL - не нужно ничего писать в сценарии вне SQL.

WITH sensorExt AS (
       SELECT *, 
              strftime('%s', insDate) AS tsec --Convert to seconds since 1970-01-01 00:00:00
       FROM sensor )
SELECT *
FROM -- Must use subquery in order to apply WHERE conditions to window function results
  (SELECT sensorExt.*,
         (lead(tsec) OVER (ORDER BY insDate ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING))
          - tsec as postspan -- Calculate difference between following row seconds
   FROM sensorExt
   ORDER BY sensorExt.insDate) AS sensorExt2
WHERE
   -- Only keep rows which are at end of "burst" with large delay to next record 
   -- coalesce() is called to handle/include the null value of the last record.
   coalesce(postspan, 11) > 10

Примечание 1 : я предпочитаюиспользуйте общие табличные выражения (CTE; предложение WITH) вместо подзапросов, когда это возможно, но, похоже, есть ошибка с оконными функциями (т.е. предложение OVER) в CTE, поэтому япришлось вернуться к подзапросу.В противном случае я бы использовал серию таблиц CTE вместо того, чтобы смешивать конструкции.

Примечание 2 : Этого также можно достичь без оконных функций, используя стандартные агрегированные SQL-запросы, но я думаю, что этодля получения разницы между соседними строками потребуется как минимум набор из 3 вложенных подзапросов.

0 голосов
/ 08 июля 2019

Если предположить, что каждый соседний блок продолжительностью 40 секунд всегда будет иметь ровно 6 показаний, тогда необязательно начинать и заканчивать группу из 6 показаний.Мы можем попытаться агрегировать показания по блокам по 40 секунд, а затем просто взять значение MIN для каждого блока:

SELECT
    ts % 40 AS block,
    MIN(reading) AS reading
FROM yourTable
GROUP BY
    ts % 40
ORDER BY
    ts % 40;

. Причина, по которой это должно работать, состоит в том, что минимальное значение, которое мы выбрали, действительно принадлежит«предыдущий» блок, потому что мы угадали границу неправильно.Затем этот сдвиг также будет присутствовать в следующем блоке, а затем в последующем блоке, поэтому он не имеет значения в конце.

Этот ответ предполагает, что показание времени находится в столбце с именем ts,и он содержит что-то вроде секунд с эпохи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...