Оптимизация SQL-запроса, чтобы избежать полного сканирования таблицы

StackOverflow https://stackoverflow.com/questions/4334519

  •  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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top