existsが遅いときの対処法

existsを使用したクエリは行数が増えると遅くなります。
もちろんnot exstsにした場合も同様です。
そこで、遅くなった時にどのように高速化するかについて紹介します。

existsが遅いときの対処法

結論を先に述べますと

  • exists を高速化するには inner joinへ変更
  • not exists を高速化させるには left join へ変更

です。

まずは高速化したいexistsのクエリのテーブル構造です。
ユーザーテーブルに対して存在チェックする注文テーブルです。


続いてexistsのクエリです。


この「存在」チェックのクエリを高速化するにはinner joinを用います。


「存在しない」チェックの場合はleft joinを使用します。


left joinの特性として、結合できなかった列はnullになる性質をもっています。
そこに、「is null」を使用して「存在しない」を判定しています。

existsクエリをjoin句に変換するときは1対1にする

存在する/しない チェックの注意点は1対多になっていることが多いことです。
ですので、抽出データの重複を避けて存在チェックを1対1にするために、先に集計します。
今回はユーザー単位で存在チェックをしたいので、注文テーブルはuser_idでグループ化しています。

existsが遅いのはレコードをみる回数が多いから

exitsのイメージとしてはユーザーテーブルの1レコードに対して注文テーブルすべてを結合させるイメージです。
つまり、ユーザーテーブルが1000レコードあり、注文テーブルが1万レコードならば、最大で1000万になります。

掛け算ゆえに、exitsを用いたクエリはレコードが増えると劇的に遅くなります

(おまけ)存在チェック用のjoinにはコメントを入れてわかりやすく

別の人が読んだときにわかるように、簡単でよいのでコメントを入れることをお勧めします。
というのも、「select句に使用しない結合」になるので、
パッと見て「何故結合しているか」がわかりにくくなるためです。

おわりに

数百、数千程度ではexistsの速度低下は気にならないことが多いです。
ただ、大量のデータに対してexsitsをクエリに使用すると速度が急激に低下します。
ですので、最初からinner joinもしくはleft joinでクエリを作るのはアリだと思います。