Оптимизация SQL-запроса, чтобы избежать полного сканирования таблицы
-
30-09-2019 - |
Вопрос
Рассмотрим следующий запрос:
SELECT * FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1;
Тем Печать столбец в Транзакции Таблица представляет собой TIMESTAMP, и в ней есть индекс.Как я могу изменить этот запрос, чтобы избежать полного сканирования таблицы?(то есть, используя Печать за пределами день() функция)
Спасибо!
Решение
Вот как я бы это сделал:
Добавьте несколько дополнительных полей: год, месяц, день или даже час, минута в зависимости от ожидаемого движения. Затем создайте триггер для заполнения дополнительных полей, возможно, вычитая 3 -часовой интервал заранее. Наконец -то постройте индекс на дополнительных полях.
Другие советы
Если цель состоит в том, чтобы избежать полного сканирования таблицы, и у вас есть первичный ключ (скажем, названный PK) для транзакций, рассмотрите возможность добавления индекса покрытия
ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp)
Затем
SELECT * FROM Transactions WHERE PK IN (SELECT PK FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1
)
Этот запрос не должен использовать полное сканирование таблицы (однако оптимизатор может принять решение об использовании полного сканирования, если количество строк в таблице мало или по какой -либо другой статистической причине :))
Лучшим способом может быть использование временной таблицы вместо подпрограммы.
Часто вы можете переписать функцию, чтобы получилось что-то вроде WHERE Stamp=XXXX
а XXXX — это какое-то выражение.Вы можете создать серию операторов BETWEEN для каждого месяца, WHERE Stamp BETWEEN timestamp('2010-01-01 00:00:00') AND timestamp ('2010-01-01 23:59:59') OR Stamp BETWEEN ...
, но я не уверен, что в этом случае будет использоваться индекс.Я бы создал столбец с указанием дня месяца, как предлагает @petr.
Рассчитайте желаемое значение марки отдельно, прежде чем запустить основной запрос, т.е.
Шаг 1 - Рассчитайте желаемое значение марки
Шаг 2 - Запустите запрос, где марка> (рассчитанное значение)
Поскольку на шаге 2 нет расчета, вы сможете использовать свой индекс.
Если я понимаю это правильно, вы в основном хотите вернуть все ряды, где штамп падает на первое в каждом месяце (вычитая 3 часа)? Если (и это большое, если), у вас есть фиксированное окно, скажем, последние 6 месяцев, вы можете просто перечислить 6 тестов диапазона. Но, тем не менее, я не уверен, что Indexted Access в любом случае будет быстрее.
select *
from transactions
where stamp between timestamp '2010-06-01 03:00:00' and timestamp '2010-06-02 02:59:59'
or stamp between timestamp '2010-07-01 03:00:00' and timestamp '2010-07-02 02:59:59'
or stamp between timestamp '2010-08-01 03:00:00' and timestamp '2010-08-02 02:59:59'
or stamp between timestamp '2010-09-01 03:00:00' and timestamp '2010-09-02 02:59:59'
or stamp between timestamp '2010-10-01 03:00:00' and timestamp '2010-10-02 02:59:59'
or stamp between timestamp '2010-11-01 03:00:00' and timestamp '2010-11-02 02:59:59'
or stamp between timestamp '2010-12-01 03:00:00' and timestamp '2010-12-02 02:59:59';
Черт! Я не уверен, как работает миллисекундная часть временной метки. Вам может потребоваться соответственно.
Переработав Петра немного, чтобы избежать пункта и сделать это для Myisam или Innodb.
Для Myisam
ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp)
Или, для Innodb, где PK неявно включена в каждый индекс,
ALTER TABLE Transactions ADD INDEX Stamp (Stamp)
Затем
SELECT *
FROM Transactions LEFT JOIN
(
SELECT PK
FROM Transactions
WHERE DAYOFMONTH(Stamp - interval 3 hour) = 1
) a ON Transactions.PK=a.PK
У подкового уровня будет выполнено только индекс, и внешний запрос будет только вытащить ряды из стола, где прошел A.PK.