Запретить MySQL использовать полное сканирование таблицы для запроса - PullRequest
3 голосов
/ 24 октября 2009

Можно ли как-то запретить MySQL выполнять полное сканирование таблицы, если результат не был найден с помощью индексов?

Например, этот запрос:

SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1;

Эффективно только в том случае, если X удовлетворяет условию, и возвращается хотя бы 1 строка, но если условие не может быть удовлетворено какими-либо данными в таблице, будет выполнено полное сканирование, которое может быть очень дорогостоящим.

Я не хочу оптимизировать этот конкретный запрос, это всего лишь пример.

ПОЯСНИТЕ на этот запрос с X внутри или за пределами диапазона:

id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a range long_ip  long_ip 8 \N 116183 100.00 Using where

STATUS VARIABLE показывает гораздо лучшую информацию. Для X вне диапазона:

Handler_read_prev 84181
Key_read_requests 11047

В диапазоне:

Handler_read_key 1
Key_read_requests 12

Если бы только был способ не допустить, чтобы Handler_read_prev когда-либо проходил мимо 1.

ОБНОВЛЕНИЕ. Я не могу принять свой собственный ответ, потому что он действительно не отвечает на вопрос (хотя HANDLER - отличная функция). Мне кажется, что нет никакого общего способа предотвратить полное сканирование MySQL. Хотя простые условия, такие как key = 'X', будут рассматриваться как "невозможные где", более сложные вещи, такие как BETWEEN, не будут.

Ответы [ 2 ]

1 голос
/ 24 октября 2009

Вы можете написать «полностью покрытый» подзапрос, который использует только данные, доступные в индексах. Основываясь на возвращенном первичном ключе, вы можете искать строки в главной таблице.

Следующий запрос полностью покрыт индексами on (id), (B, id) и (C, id):

select *
from a
where id in (
    select id
    from a 
    where x <= C
    and id in (
        select id
        from a
        where B <= X 
    )
)
limit 1

Каждый SELECT использует один индекс: самый внутренний индекс (B, id); средний SELECT использует индекс на (C, id), а внешний SELECT использует первичный ключ.

0 голосов
/ 26 октября 2009

Вот что я в итоге придумал:

HANDLER a OPEN;
HANDLER a READ BC <= (X);
HANDLER a CLOSE;

BC - это название ключа (B, C). Если мы упорядочим таблицу по B DESC, то результат будет гарантированно равен

SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1;

Теперь, если X не находится в диапазоне таблицы a, мы просто должны проверить, что aC больше, чем X, если нет, то X определенно находится за пределами диапазона, и нам не нужно смотреть дальше .

Это не очень элегантно, и вам придется прибегать к таблице при каждой вставке или обновлении.

...