IN 演算子の高速化

IN演算子の高速化について紹介します。

INをEXISTSに変更する

IN演算子をEXISTSに変更することで高速化できるケースがあります。

IN演算子の後にサブクエリがある場合、SQLはサブクエリから実行されます。 一方 EXISTS演算子は後にサブクエリがある場合でも、メインクエリから実行されます。

下記のテーブルを例にします。

products
idmodelnamecategoryprice
1C-XM01モーダンチェアチェア56000
2X-XD05ラージデスクテーブル87000
3A-DA40ラウンドダイニングチェアチェア28000
4O-XX100ナチュラルオフィスチェア13800
5R-D400ラウンドダイニングテーブルテーブル128000
6R7000ウッドキャビネットその他32000
7B-200リネンベッドベッド184500
8B-250ホワイトダブルベッドベッド324850
9W-80ワーキングチェアチェア45000
10EG-10Xエルゴノミクスデスクテーブル88500
sale
idcategory
1チェア
2ベッド
select * from products where category in (select category from sale)
上記のSQLであれば、IN以降の副問い合わせは入力として処理されるため、下記のコードと同様になります。
foreach(_record in execsql("select category from sale"))
 match = execsql("select * from products where category = _record");
 if (match==true){
   (結果セットに挿入)
 }
}

select * from products where exists (select id from sale where sale.category = products.category)
一方、上記のSQLの場合は、相関問い合わせ(相関クエリ/相関サブクエリ)になり、メインのクエリの結果に対してサブクエリでの評価をするため、下記のコードと同様になります。
foreach(_record in execsql("select * from products")){
 match = execsql("select id from sale where _record.category = sale.category");
 if (match==true){
   (結果セットに挿入)
 }
}

IN演算子を用いた場合、サブクエリの結果は展開されメモリ等の一時テーブルに記録されます。 そのため、サブクエリの結果サイズが大きい場合はパフォーマンスの低下を起こす可能性があります。
今回の例ではproductsテーブルのレコードサイズが増えると速度が低下する見込みです。

速度比較

以下のSQLの実行時間を比較します。

SQL : IN演算子利用

select * from (大きなテーブル) as T
where (フィールド名) not in (select (パターン列名) from (パターンのテーブル))

SQL : EXISTS演算子利用

select * from (大きなテーブル) as T
where not exists (select id from (パターンのテーブル) where T.(フィールド名) = (パターン列名) )
(パターンのテーブルは約20レコード、大きなテーブルは270万レコード)

結果

IN演算子EXISTS演算子
3回の実行速度平均1:151:35

今回の例ではパターンのテーブルのサイズが小さかったためか、INよりEXISTSのほうが若干遅い結果となりました。 一般的にEXISTSのほうが高速とされていますが、昨今のデータベースエンジンでは、 クエリの最適化や実行計画が良くなっているためか、あまり速度差が出ないのかもしれません。
補足
利用しないフィールドはselectに含めないなどの一般的なSQLクエリのチューニングでも高速化が期待できます。
著者
iPentec.com の代表。ハードウェア、サーバー投資、管理などを担当。
Office 365やデータベースの記事なども担当。
最終更新日: 2024-05-02
作成日: 2014-08-12
iPentec all rights reserverd.