Анализ записей таблицы и очистка от невалидных данных

286
24 июля 2017, 13:53

Доброго времени суток

Есть следующая таблица:

--
-- Структура таблицы `data`
--
CREATE TABLE IF NOT EXISTS `data` (
  `id` int(11) NOT NULL AUTO_INCREMENT, /* ID записи */
  `station_id` int(11) NOT NULL,        /* ID измерительной станции */
  `box_id` int(11) NOT NULL,            /* ID измерительного блока на станции */
  `param_id` int(11) NOT NULL,          /* Код измеренного параметра */
  `value` int(11) NOT NULL,             /* Значение измеренного параметра */
  `tm` bigint(20) NOT NULL,             /* Метка времени, когда был получен измеренный параметр */
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

Если кратко, то таблица хранит измерения неких параметров (целые числа value) для нескольких станций station_id измерения параметров. Внутри станции может быть несколько измерительных блоков box_id, и параметры они меряют тоже разные - param_id. Про получении данных записывается время в tm.

Записи могут быть примерно вот такими:

id     station_id box_id param_id value tm
906616 2          1      5        2100  1500270962
906616 2          2      2        2199  1500270960
906616 2          3      3        2850  1500270960

Ну и подобный числовой хаос, который без поллитры не понятен. Тем не менее каждая запись важна, и по ним строятся всякие графики, отчеты и прочий анализ.

Количество записей в таблице порядка 2-3 миллионов и возрастает. Записи годичной давности вынимаются из этой таблицы и перекладываются в архивные таблицы по ежемесячному cron.

К сути. Измерительное оборудование сгорело. Несколько станций записали в нескольких своих блоках вместо нормальных измерений нули и единицы (а возможно, и что-то еще нехорошее, но никак не похожее на нормальные измерения).

Задача: найти невалидные данные и уничтожить (удалить из таблицы), при этом не задев валидные данные.

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

SELECT `value` FROM `data` WHERE `station_id` = 1,`box_id` = 1,`param_id` = 1 ORDER BY `tm` DESC;

то value будет отсортированным массивом (если данные валидны). Но это не так по факту - есть вкрапления невалидных данных

Вот собственно и суть вопроса, по которому я обращаюсь. Нужно сочинить такой SQL запрос, который сумеет выбрать все записи с определенным значением индекса [station_id,box_id,param_id], у которых начался сбой - то есть значение A.value стало меньше чем B.value, при этом A.tm больше B.tm, то есть значение было получено позже и уменьшилось относительно предыдущего.

Умом я понимаю, что нужно сделать. Есть рабочее решение на PHP, которые выбирает блок измерений, который может прожевать, и в коде смотрит, все ли в порядке, удаляя невалидные данные. Недостаток этого решение в длительности обработки и в необходимости лопатить все данные, вместо только нужных участков. Второй недостаток - данные постоянно пополняются, и нет уверенности в определенный момент времени, что таблица очищена от сбоев, поскольку скрипт может обработать только небольшую часть данных. Нельзя сказать, что если скрипт ничего не сделал, то данные валидны и возрастают - потому что скрипт не может охватить все данные сразу, а только небольшое окно от сих до сих. На мой взгляд для этого как раз нужен SQL.

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

Я не силен в сочинении сложных SQL, обычно хватает маленьких. Сам язык знаю на базовом уровне - выборка из двух-трех таблиц, вставка, удаление. Но тут совсем необычная ситуация обработки данных.

Answer 1

Решение в лоб:

SELECT t1.id 
FROM data t1 JOIN data t2 
ON (t1.tm<t2.tm AND t1.value>t2.value) 
WHERE t1.station_id = 1 AND t1.box_id = 1 AND t1.param_id = 1 AND t2.station_id = 1 AND t2.box_id = 1 AND t2.param_id = 1;
READ ALSO
Создание триггера по типу cron-демона

Создание триггера по типу cron-демона

Существует ли возможность создания триггера для MariaDB или MySQL, который бы выполнялся с определённой периодичностьюНапример, удалял записи...

231
Ошибка Access denied for user &#39;maks12345&#39;@&#39;localhost&#39; to database &#39;databasewp&#39;

Ошибка Access denied for user 'maks12345'@'localhost' to database 'databasewp'

Хочу перенести сайт WordPress на другой доменЭкспортировал базу данных WordPress в phpMyAdmin, изменил домен и имя базы данных, хочу импортировать базу...

212
INSERD ODKU при вставке нескольких строк

INSERD ODKU при вставке нескольких строк

Добрый день! Есть запрос типа

213