読者です 読者をやめる 読者になる 読者になる

大きいテーブルをスキャンしないようにする

SQL
  • BigTableは一日最大1000件のペースで増えていく
  • nameを持つが、重複がかなり多い
  • SmallTableは200〜300件のデータ量
  • nameを持つが、重複は存在しない(PK)
  • SmallTableの一覧をGUIに表示する必要がある
    • ただし、BigTableにnameが同じデータがある場合、マークをつける必要がある

こんな条件で上がってきたプログラムを実行すると、なんかまだデータ数が少ないのにえらく重い。
処理を見てみると、SmallTableを全件取得して、一件一件削除を試みて外部キー違反の例外をcatchすることでチェックしてる*1・・・
そりゃ重いよ、と突っ返したら、今度はBigTableを全件取得してローカルでループまわして存在チェックをかけるコードが・・・
開発時だから*2さくさく動くものの、それは駄目だろう、とまた突っ返したら、今度はBigTableをnameでGROUP BYしたコードが*3・・・
そりゃ返ってくるデータは少なくなるけどさ、大きいテーブルなんだから極力スキャンじゃなくてシークで済むようにするのが常識じゃないのか?


と言うことで、普通に考えるとこんな感じのコードになると思うんだけど。

-- マークをつける対象を取得
SELECT
    name
FROM
    SmallTable Small
WHERE
    EXISTS(
      SELECT * FROM BigTable Big
      WHERE Big.name = Small.name
    )
;

こうすると、もしスキャンになったとしてもサブクエリ実行中に一件でも見つかればその時点でスキャンは終了するので高速。
推定実行プランを見てみたらシークを使ってたんで、さらに高速。

*1:削除できた場合はロールバック

*2:BigTableに全然データが入ってない

*3:細かいことだけど、意味的には本来DISTINCTを使うべき。それ以前の問題だけどね