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