Tech Racho エンジニアの「?」を「!」に。
  • 開発

MySQLのサブクエリを改善する方法

MySQLのサブクエリは、一見問題なさそうなものも相関サブクエリに置換されて、極端に遅くなることがありますね。
INを適切に処理できないのが原因らしいです。

問題

今回は、以下のような「ユーザごとにスコアの履歴を保持していて、最新のスコアが有効」というテーブル設計で、「全ユーザの最新スコア合計を取得」しようとして問題が起きました。

  • score_log
カラム 説明
id int primary key auto_increment
user_id int ※外部キー
score int

最初考えたSQLは以下のようなものです。
ユーザごとの最新スコアIDを一括取得し、それに該当するスコアの合計を計算しました。

SELECT SUM(score_log.score) FROM score_log
   WHERE score_log.id IN (SELECT MAX(log2.id)
   FROM score_log AS log2 GROUP BY log2.user_id)

しかしこれだと、EXPLAINをつけると分かるように、DEPENDENT SUBQUERYになってしまいます。
参考サイトによると、MySQLがINをEXISTSに変換する際に、相関サブクエリにしてしまっているようです。
実行速度も、データ件数の二乗に比例し、極端に遅くなってしまいました。

解決方法

結果的に、以下のクエリに変更して解決しました。

SELECT SUM(score_log.score) FROM score_log,
   (SELECT MAX(log2.id) id FROM score_log AS log2 GROUP BY log2.user_id) each_cur
   WHERE score_log.id = each_cur.id

ユーザごとの最新スコアIDの一覧をテーブルとして、それをFROMに使ってしまうというわけですね。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。