TL;DR

ヒント句を使ってWHEREでのINDEXを使わないようにする。

また、同時にORDER BYでINDEXを使うように明示すると可読性が上がる。

事例: WHEREで十分にデータを絞れないが、ORDER BY + LIMITを使う

ORDER BYとWHEREに同じカラムを使用しており、WHEREの方で十分にデータを絞りきれないが、ORDER BY + LIMITで一部のデータを取得すればいいというケースを考える。

テーブル構造とSQL

以下のようなテーブルについて、投稿から30分以上経過しているものを、投稿日時の降順で10件取得したいとする。

投稿ID(id) 投稿日時(post_datetime) 投稿ステータス, etc
9999 2021-04-06 12:10:00 PUBLIC, etc
9998 2021-04-06 11:56:10 PUBLIC, etc
9997 2021-04-06 11:52:06 PUBLIC, etc
9996 2021-04-06 12:00:00 PUBLIC, etc
... ... ...
1 2020-01-01 00:00:00 PUBLIC, etc
PK: id
INDEX: post_datetime DESC

※レコード作成時にpost_datetimeが入力されるとは限らないことや予約投稿機能等でid順とpost_datetime順が一致しない可能性あり

SQLは以下の通り。

SELECT *
FROM post
WHERE post_datetime <= NOW() - INTERVAL 30 DAY_MINUTE
ORDER BY post_datetime DESC
LIMIT 10

EXPLAINするとINDEXは使われるのだが、rowsがほぼ全データになってしまい、あまり意味のないINDEXの使われ方になってしまう。(今はWHEREがpost_datetimeだけなのでExtraにはUsing index conditionと表示される分、まだ意味のあるINDEXだが、他の条件がWHEREに入ったらUsing index condition; Using whereとなるためINDEXだけで完結せずデータアクセスが走ってしまい、ほぼ意味がなくなる。)

ヒント句でWHEREでINDEXを使わないようにする

解決策として、ヒント句(インデックスヒント)でWHEREでINDEXを使わないようにする。

先ほどのSQLにヒント句を加える。

SELECT *
FROM post
IGNORE INDEX FOR JOIN (post_datetime)
WHERE post_datetime <= NOW() - INTERVAL 30 DAY_MINUTE
ORDER BY post_datetime DESC
LIMIT 10

これでEXPLAINすると、keyにpost_datetimeが採用されるのにExtraはUsing whereとなる。rowsは10に絞られている。(他の条件をWHEREに追加しても同じ。)

つまりORDER BY狙いのINDEXとLIMITの効果が発揮され十~数十件程度のINDEX走査とデータアクセスしか走らないことになる。

ヒント句でORDER BYのINDEXを明示する

WHERE句のINDEX利用に関わるヒント句(FOR JOIN)をつけるだけで問題なく動くが、SQLの可読性のためにORDER BYのINDEXも併せて記載したほうがいいと思う。(ORDER BYでINDEX利用をしたいということの明示。ヒント句がついていると、SQLを見たときに「このヒント句はなんのため?」と考えてしまうため。)

SELECT *
FROM post
IGNORE INDEX FOR JOIN (post_datetime)
USE INDEX FOR ORDER BY (post_datetime)
WHERE post_datetime <= NOW() - INTERVAL 30 DAY_MINUTE
ORDER BY post_datetime DESC
LIMIT 10

備考

動作確認環境

  • MySQL 8

類似記事

MySQL公式ドキュメント