2013-06-10 2 views
3

У меня есть 3 процесса A, B и C, как определено в следующей серии таблиц:Выравнивание метки времени, когда не совсем синхронизированы

http://sqlfiddle.com/#!2/48f54

CREATE TABLE processA 
(date_time datetime, valueA int); 

INSERT INTO processA 
       (date_time, valueA) 
VALUES 
('2013-1-8 22:10:00', 100), 
('2013-1-8 22:15:00', 100), 
('2013-1-8 22:30:00', 100), 
('2013-1-8 22:35:00', 100), 
('2013-1-8 22:40:00', 100), 
('2013-1-8 22:45:00', 100), 
('2013-1-8 22:50:00', 100), 
('2013-1-8 23:05:00', 100), 
('2013-1-8 23:10:00', 100), 
('2013-1-8 23:20:00', 100), 
('2013-1-8 23:25:00', 100), 
('2013-1-8 23:35:00', 100), 
('2013-1-8 23:40:00', 100), 
('2013-1-9 00:05:00', 100), 
('2013-1-9 00:10:00', 100); 


CREATE TABLE processB 
(date_time datetime, valueB decimal(4,2)); 

INSERT INTO processB 
       (date_time, valueB) 
VALUES 
('2013-1-08 21:46:00', 3), 
    ('2013-1-08 22:11:00', 4), 
    ('2013-1-08 22:31:00', 5), 
    ('2013-1-08 22:36:00', 6), 
    ('2013-1-08 22:41:00', 7), 
    ('2013-1-08 23:06:00', 8), 
    ('2013-1-08 23:20:00', 2), 
    ('2013-1-08 23:46:00', 3), 
    ('2013-1-09 00:34:00', 9); 


CREATE TABLE processC 
(date_time datetime, status varchar(4)); 

INSERT INTO processC 

VALUES 
('2013-1-08 18:00:00', 'yes'), 
('2013-1-08 19:00:00', 'yes'), 
('2013-1-08 20:00:00', 'yes'), 
('2013-1-08 21:00:00', 'yes'), 
('2013-1-08 22:00:00', 'yes'), 
('2013-1-08 23:00:00', 'no'), 
('2013-1-08 00:00:00', 'no'), 
('2013-1-08 01:00:00', 'no'); 

Как вы можете видеть время, при котором показания происходят для каждый из процессов не является одним и тем же.

  1. ProcessA, ЕСЛИ это происходит, делает это через каждые 5 минут

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

  3. ProcessC всегда будет иметь почасовая стоимость (да или нет).

Во-первых, я хочу, чтобы преобразовать processB так что есть чтение в истории 5-минутного интервала, поэтому данные совпадет с processA, которые затем могут позволить мне сделать простое соединение обеих таблиц в 5-минутный интервал отметка. Для преобразования данные каждые 5 минут должны быть установлены на ближайший наблюдение процесса, доступное в окне минут [-30,30). Если значения равноудалены, тогда возьмите среднее значение. Если в 30-минутном окне нет ни одного, установите значение null.

После того, как я есть, что я могу сделать простое соединение на% Y% м% D% H с ProcessC используя что-то вроде следующего, чтобы получить финальный стол со всеми данными, выровненных в 5 минутный интервал метки:

date_format(date_time, '%Y%m%d%H') = date_format(date_time, '%Y%m%d%H') 

Если у кого есть указатели/рекомендации, я был бы признателен за какое-то направление. Я ценю это.

Пример вывод:

'2013-1-8 22:10:00', 100, 4, yes <--- closer to 22:11 than 21:46 
'2013-1-8 22:15:00', 100, 4, yes <--- closer to 22:11 than 21:31 
'2013-1-8 22:30:00', 100, 5, yes <--- closer to 22:31 than 22:11 
'2013-1-8 22:35:00', 100, 6, yes <--- closer to 22:36 than 22:31 
'2013-1-8 22:40:00', 100, 7, yes <--- closer to 22:41 than 22:36 
'2013-1-8 22:45:00', 100, 7, yes <--- closer to 22:41 than 23:06 
'2013-1-8 22:50:00', 100, 7, yes <--- closer to 22:41 than 23:06 
'2013-1-8 23:05:00', 100, 8, yes <--- closer to 23:06 than 23:06 
'2013-1-8 23:10:00', 100, 8, no  <--- closer to 23:06 than 23:20 
'2013-1-8 23:20:00', 100, 2, no  <--- closer to 23:20 than 23:10 
'2013-1-8 23:25:00', 100, 2, no <--- closer to 23:20 than 23:10 
'2013-1-8 23:35:00', 100, 3, no <--- closer to 23:46 than 23:20 
'2013-1-9 00:05:00', 100, 3, no <--- closer to 23:46 than 00:34 
'2013-1-9 00:10:00', 100, 6, no <--- takes the avg of 3 and 9 
+0

Можете ли вы более конкретно рассказать о критериях присоединения процесса B к процессу? Вы хотите, чтобы наблюдение за процессом в ближайшем времени в каждом процессе наблюдалось до тех пор, пока наблюдается наблюдение процесса B * плюс или минус один час * или меньше от процесса А? Кроме того, просьба указать образец желаемых строк вывода. –

+0

Спасибо за изменения. Это хорошая идея, чтобы ввести комментарий, когда вы отвечаете на вопрос, сделав редактирование. –

+0

Спасибо Олли за руководство. Я согласен и буду дальше, я обязательно сделаю так, чтобы добавить комментарий. :) – codingknob

ответ

4

Сложная часть этого является поиском соответствующей строки или строк из processB, которые соответствуют каждой строке processA как вы выяснили.

Давайте сделаем это шаг за шагом.

Во-первых, мы должны иметь возможность присоединиться к процессам A и processB для извлечения пар отметки времени кандидата. Давайте сделаем это так:

   SELECT a.date_time a, 
         TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff 
       FROM processA a 
       JOIN processB b 
        ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800 
        AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800 

Это заставляет нас а и б раз отвечающие [-30, 30) критерий. В этом результате много строк; но мы можем проверить его, чтобы убедиться, что мы правильно провели сравнение диапазона. http://sqlfiddle.com/#!2/48f54/47/0

Теперь нам нужно сгенерировать окно времени для поиска каждой записи для одной или нескольких соответствующих записей b. Вот так.

 SELECT a, 
       MIN(ABS(timediff)) windowsize 
      FROM (
       SELECT a.date_time a, 
         TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff 
       FROM processA a 
       JOIN processB b 
        ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800 
        AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800 
     ) d 
     GROUP BY a 

Это дает две колонок: первая метка время от а, а второй является временным диапазоном ближайшего б временной метки (или временных меток, если более чем один должны быть усреднено), которые находятся в диапазоне. У этого набора результатов нет строки для записей, у которых нет записей b, достаточно близко, чтобы их рассмотреть.http://sqlfiddle.com/#!2/48f54/46/0

Наконец, нам нужно получить и усреднить значения записи b для каждой записи. Вот это.

SELECT processA.date_time date_time, 
     processA.valueA valueA, 
     AVG(processB.valueB) valueB 
    FROM processA 
    LEFT JOIN (
     SELECT a, 
       MIN(ABS(timediff)) windowsize 
       FROM (
        SELECT a.date_time a, 
          TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff 
        FROM processA a 
        JOIN processB b 
         ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800 
         AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800 
      ) d 
      GROUP BY a 
    ) j ON processA.date_time = j.a 
    LEFT JOIN processB ON ( processB.date_time >= j.a - INTERVAL j.windowsize SECOND 
          AND processB.date_time <= j.a + INTERVAL j.windowsize SECOND 
          AND processB.date_time < j.a + INTERVAL 1800 SECOND) 
    GROUP BY processA.date_time, processA.valueA 

Уведомления Есть несколько открытых диапазонов здесь (< операторов вместо <= операторов). Здесь вы можете разместить свой диапазон открытия [-30, 30]. Вот запрос. http://sqlfiddle.com/#!2/48f54/45/0

Этот окончательный запрос объединяет три таблицы: processA, нашу виртуальную таблицу, показывающую диапазон поиска для каждой отметки времени, и process B. Последнее предложение ON выполняет фактический поиск диапазона. Открытый диапазон.

Посмотрите, как это происходит? Полезно построить запрос изнутри.

Не забудьте указать индекс на processB.date_time.

Я принимаю во внимание отказ от присоединения processC к этой виртуальной таблице.

+0

Олли, спасибо за ваше время, подробное пошаговое объяснение и продуманное решение. Я вернусь к этому решению на долгие годы, поскольку я узнаю больше sql. Единственный вопрос, который у меня был для вас, заключается в том, можно ли заменить '> =' и '<=' просто '=' и использовать 'OR' вместо' AND', а также исключить окончательный '<'. Причина, по которой я спрашиваю, - это то, что кандидаты timediff были выбраны на основе желаемых критериев, и желаемый набор данных в процессе B будет существовать для удовлетворения нашего требования. Я вот пример моего предложения. Тем не менее, я мог бы не заметить. – codingknob

+1

Конструкция 'time> = start AND time ", false) });