Оптимизация неявного запроса на самостоятельное соединение

0

Предположим, у меня есть следующий log таблицы. Он отображает серию временных событий, связанных с серийным номером. Существуют разные столбцы типа события, здесь показаны только 2 столбца - session_started и voltage_changed. В каждой строке будет только один тип события, который не является нулевым. Все строки имеют time_stamp serial и time_stamp поля. Первая строка имеет оба типа событий, равные NULL, что означает, что один из других столбцов типа события (не показан) содержит значение (он помогает с образцом выборки).

Я хочу найти для каждого произошедшего session_started событие, самое следующее значение voltage_changed, которое регистрируется (по метке времени). Вот данные:

serial ||     time_stamp        || session_started || voltage_changed

BBBB    |  2017-12-15 03:05:55  |        NULL      |        NULL    |
AAAA    |  2017-12-15 04:05:55  |        1         |        NULL    |
AAAA    |  2017-12-15 04:30:55  |        NULL      |        127     |
AAAA    |  2017-12-15 05:15:55  |        NULL      |        75      |
BBBB    |  2017-12-15 05:20:55  |        1         |        NULL    |
BBBB    |  2017-12-15 06:00:55  |        NULL      |        10      |

И желаемый результат:

serial ||     time_stamp        ||    voltage

AAAA    |  2017-12-15 04:05:55  |       127        |
BBBB    |  2017-12-15 05:20:55  |       10         |

Вот запрос, который я попробовал. Он работает и дает правильный результат в этой примерной таблице, но занимает очень много времени для работы в полной таблице (я устал от ожидания завершения запроса...) Полная таблица содержит 190 000 строк и имеет индекс ontime_stamp.

SELECT 
   h.serial, 
   h.time_stamp,
   hh.voltage_changed AS voltage 
FROM 
   log h, 
   log hh 
WHERE 
   h.serial = hh.serial 
   AND hh.time_stamp = (SELECT MIN(hh.time_stamp) 
                        FROM  log hh 
                        WHERE (hh.time_stamp >= h.time_stamp) 
                        AND hh.voltage_changed IS NOT NULL 
                        AND (h.session_started = 1));

Есть ли способ оптимизировать этот запрос для более эффективной работы на большой таблице? Достаточно ли иметь индекс на time_stamp или мы должны рассматривать другие столбцы в этом случае?

  • 1
    Ваш запрос сбивает с толку, он использует псевдоним таблицы hh дважды. Очевидно, что вы должны попытаться избавиться от суб-выбора.
Теги:

2 ответа

2
Лучший ответ

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

select l.*,
       (select l2.voltage_changed
        from log l2
        where l2.serial = l.serial and
              l2.time_stamp >= l.time_stamp and
              l2.voltage_changed is not null
        order by l2.time_stamp asc
        limit 1
       ) as voltage_changed
from log l
where l.session_started = 1;

Для этого вам нужны два индекса. Более важным является log(serial, voltage_changed, time_stamp). Второй - log(session_started, serial).

  • 0
    Я добавил LIMIT 1 после asc чтобы удалить multiple results on subquery ошибке multiple results on subquery , и теперь он, кажется, дает то, что я хотел, и выполняется примерно через 2 минуты. Fetch берет навсегда, но это, вероятно, я через VPN :)
  • 0
    @EkcenierK. , , Спасибо.
1

Я бы изменил ваши данные на serial | time_stamp | event_type | event_value serial | time_stamp | event_type | event_value serial | time_stamp | event_type | event_value, так что у вас есть session_started, voltage_changed или другие значения в столбце event_type, и любое целочисленное значение связано с событием в столбце event_value. И индекс event_type и serial с serial - это кластеризованный индекс.

В этом случае необходимые строки будут эффективно фильтроваться без необходимости сканирования всей таблицы для непустых значений столбца voltage_changed для дальнейшего самосоединения.

Тогда ваш запрос будет похож (работает с коррелированным подзапросом):

select
t1.serial,
t1.time_stamp,
t2.event_value as voltage
from (
    select
    e1.serial,
    e2.time_stamp,
    min(e3.timestamp) AS voltage_ts
    from log e1
    left join log e2
    on e1.serial=e2.serial
    and e1.time_stamp<=e2.time_stamp
    and e2.event_type='voltage_changed'
    where e1.event_type='session_started'
    and e1.event_value=1
    group by 1,2
) t1
join log t2
on t1.serial=t2.serial
and t1.voltage_ts=t2.time_stamp
and t2.event_type='voltage_changed';

Я знаю, что могут быть обоснованные соображения для хранения этих данных, как есть, просто предоставляя другую точку зрения.

  • 0
    Спасибо за вашу помощь. К сожалению, значения событий не являются целыми числами, они бывают разных типов. Было бы намного удобнее, хотя :)
  • 1
    @EkcenierK, вы можете сохранить его как varchar и привести к целочисленному типу, это относительно дешевая операция по сравнению со сканированием всей таблицы для получения соответствующих значений. Компромисс может заключаться в том, чтобы продолжать хранить значения в разных столбцах, но добавить индексированный event_type чтобы вы сохранили правильные типы данных и могли эффективно фильтровать необходимые строки. Кроме того, я говорю больше об OLAP, отличном от среды OLTP, здесь ... какой у вас есть?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню