Предположим, у меня есть следующий 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 или мы должны рассматривать другие столбцы в этом случае?
Поскольку вам нужен только один столбец, я думаю, что самосоединение не нужно. Я бы начал писать это как коррелированный подзапрос:
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)
.
LIMIT 1
после asc
чтобы удалить multiple results on subquery
ошибке multiple results on subquery
, и теперь он, кажется, дает то, что я хотел, и выполняется примерно через 2 минуты. Fetch берет навсегда, но это, вероятно, я через VPN :)
Я бы изменил ваши данные на 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';
Я знаю, что могут быть обоснованные соображения для хранения этих данных, как есть, просто предоставляя другую точку зрения.
event_type
чтобы вы сохранили правильные типы данных и могли эффективно фильтровать необходимые строки. Кроме того, я говорю больше об OLAP, отличном от среды OLTP, здесь ... какой у вас есть?
hh
дважды. Очевидно, что вы должны попытаться избавиться от суб-выбора.