Оптимизация запросов ORDER BY LIMIT в MySQL - PullRequest
1 голос
/ 05 марта 2011

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

Чтобы получить следующий и предыдущий идентификатор, я выполняю два запроса:

Для предыдущего:

SELECT id FROM pages WHERE (id<$requestedPageId) ORDER BY id DESC LIMIT 1

А для следующего:

SELECT id FROM pages WHERE (id>$requestedPageId) ORDER BY id LIMIT 1

EXPLAIN говорит, что типом запроса является "диапазон", а в столбце строк указано, что он будет проверять все строки, у которых идентификатор больше или меньше идентификатора страницы (большое число). В дополнительной строке написано «Использование где».

Кажется, MySQL игнорирует, что я хочу только одну строку. Разве MySQL не достаточно умен, чтобы оптимизировать такого рода запросы, чтобы он находил строку для страницы и искал первую соответствующую строку назад / вперед?

Есть ли лучший способ получить идентификатор следующей и предыдущей страницы?

Дополнительные примечания:

  • Эта проблема, по-видимому, существует при каждом запросе типа ORDER BY LIMIT (например, когда я разбиваю длинный список на несколько страниц).
  • Где предложение не такое простое (я хочу разрешить пользователю доступ к следующей / предыдущей странице, к которой у него есть права доступа. Однако присоединений нет.)
  • Все столбцы появляются в ГДЕ проиндексированы (id является первичным ключом)
  • переменные защищены от инъекций.

EDIT1:

Итак, запрос, который я сейчас использую:

SELECT id
FROM reports
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient))
ORDER BY id DESC
LIMIT 1

Или когда я перефакторинг, как ответ сказал:

SELECT MAX(id)
FROM reports
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient))

Ответы [ 2 ]

3 голосов
/ 05 марта 2011

за предыдущий

SELECT MAX(id) FROM pages WHERE id<$requestPageId

и за следующий

SELECT MIN(id) FROM pages WHERE id>$requestedPageId
1 голос
/ 05 марта 2011

База данных ведет себя как ожидалось. Ваш запрос является запросом диапазона из-за символа «меньше» (id <$ requiredPageId). Оператор OR затрудняет использование одного индекса для поиска результатов. И, сортировка результатов означает, что для выполнения сортировки необходимо получить все соответствующие строки, даже если вам нужна только 1 строка. </p>

Вы не сможете сделать этот запрос типа "const", но вы сможете оптимизировать его, используя индексы, подзапросы и / или операторы объединения.

Вот один запрос, чтобы управлять ими всеми. Я не говорю, что это лучшее решение, а всего лишь один из способов решения проблемы. Для начала этот запрос будет работать лучше, если вы создадите два индекса, один для receientId, а другой для isPublic.

SELECT
GREATEST(
  ( SELECT MAX( id ) FROM reports 
    WHERE id < $requestedPageId AND recipientId = $recipient ),
  ( SELECT MAX( id ) FROM reports 
    WHERE id < $requestedPageId AND isPublic = 1 )
) AS prev_id
LEAST(
  ( SELECT MIN( id ) FROM reports 
    WHERE id > $requestedPageId AND recipientId = $recipient ),
  ( SELECT MIN( id ) FROM reports 
    WHERE id > $requestedPageId AND isPublic = 1 )
) AS next_id
...