SQLのパフォーマンス“ EXISTS”使用法のバリエーション
-
05-07-2019 - |
質問
次の3つのSQLステートメントのパフォーマンスに違いはありますか?
SELECT * FROM tableA WHERE EXISTS (SELECT * FROM tableB WHERE tableA.x = tableB.y)
SELECT * FROM tableA WHERE EXISTS (SELECT y FROM tableB WHERE tableA.x = tableB.y)
SELECT * FROM tableA WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.x = tableB.y)
これらはすべて機能し、同じ結果セットを返す必要があります。しかし、内部SELECTがtableBのすべてのフィールド、1つのフィールド、または単なる定数を選択するかどうかは重要ですか?
すべてのステートメントが同等に動作する場合のベストプラクティスはありますか?
解決
EXISTS句についての真実は、SELECT句がEXISTS句で評価されないことです-試すことができます:
SELECT *
FROM tableA
WHERE EXISTS (SELECT 1/0
FROM tableB
WHERE tableA.x = tableB.y)
...また、ゼロによる除算エラーが予想されますが、評価されていないため、そうではありません。これが、私の習慣がSELECTを無視できることを示すためにEXISTSにNULLを指定する理由です:
SELECT *
FROM tableA
WHERE EXISTS (SELECT NULL
FROM tableB
WHERE tableA.x = tableB.y)
EXISTS句で重要なのは、FROMおよびbeyond句-WHERE、GROUP BY、HAVINGなどです
この質問は、データベースを念頭に置いてマークされていませんでした。ベンダーが物事を異なる方法で処理しているので、テストする必要があります。バージョン間で動作が変わる可能性があります...
他のヒント
間違いなく#1。 「見える」怖いですが、オプティマイザーが正しいことを行い、意図を表現していることを理解してください。また、誤ってEXISTSであるがINと入力した場合のわずかなタイプミスのボーナスもあります。 #2は受け入れられますが、表現力はありません。 3番目の選択肢は、それほど謙虚な意見ではありません。 「値が存在しない場合」と言うには近すぎます。快適に。
一般に、他の利点を提供し、実際にパフォーマンスに影響を与えない場合は、非効率に見えるコードを書くことを恐れないことが重要です。
つまり、オプティマイザーはほとんどの場合、複雑な結合/選択/グループ化ウィザードを実行して、単純なEXISTS /サブクエリを同じ方法で保存します。
自分自身に kudos を与えた後、その厄介なORの結合を巧みに書き換えて、最終的にはオプティマイザーが、同じ安っぽい実行プランを使用して、埋め込みORを使用したクエリを理解しやすいように解決していることに気付いてください。
話の教訓は、プラットフォームオプティマイザーを知っていることです。さまざまなことを試して、実際に何が行われているのかを確認してください。「装飾的な」クエリの最適化に関するひざまずきの前提は、ほとんど常に不正確であり、私の経験とは無関係です。
これは古い投稿であることに気づきましたが、ある形式を別の形式よりも選択する理由を明確にすることが重要だと思いました。
最初に、他の人が指摘したように、データベースエンジンはSelect句を無視する 想定 します。 SQL Serverのすべてのバージョンには、Oracle、MySQLなどがあります。データベース開発の多くの面で、Select句を適切に無視しなかった1つのDBMS、Microsoft Accessにしか出会ったことがありません。具体的には、MS Accessの古いバージョン(現在のバージョンとは話せません)。
この「機能」を発見する前は、 Exists(Select * ...
を使用していました。ただし、MS Accessがサブクエリのすべての列にストリーミングしてから、それらを破棄します( Select 1/0
も機能しません)。これにより、 Select 1
に切り替えることができました。1つのDBMSでもバカな場合、別のDBMSが存在する可能性があります。
Writing Exists(Select 1 ...
は、意図を伝える上で非常に明確です(快適さのために「もし「値がない」と言うのはあまりにも近すぎます) 。")を使用して、DBMSがSelectステートメントで愚かなことをする可能性をほとんど不可能にします。 Select Null
は同じ目的を果たしますが、書く文字数が増えます。
Exists(Select 1
に切り替えて、DBMSがバカにならないようにしました。しかし、それは何ヶ月も前のことでしたが、今日ではほとんどの開発者が Exists(Select *
これはまったく同じように機能します。
とはいえ、DBMSが適切に評価したとしても、 Exists(Select *
を回避する理由は1つあります。 Select *
Exists句で使用するすべてのインスタンスをスキップする必要がない場合。
少なくともSQL Serverでは、
ディスクから読み取れるデータの最小量は、単一の「ページ」です。ディスク容量。プロセッサは、サブクエリの述語を満たす1つのレコードを読み取るとすぐに停止できます。サブクエリは、単独で実行されているようには実行されず、外部クエリに含まれ、全体に対する完全なクエリプランの一部として実行されます。そのため、サブクエリとして使用する場合、Select句の内容は実際には関係なく、何も返されません"とにかく、単一のレコードが見つかったかどうかを示すブール値を除き、外部クエリに...
3つすべてがまったく同じ実行計画を使用しています
サブクエリから特に何かが返されることを意味するのではなく、読みやすいと思うので、常に[Select * From ...]を使用します。
編集:デイブコスタコメントから... Oracleも3つのオプションすべてに同じ実行計画を使用しています
これは、ある種の神聖な戦争を開始しようとする質問の1つです。
それについてはかなり良い議論がありますこちら
答えはおそらく3番目のオプションを使用することだと思いますが、速度の増加はごくわずかなので、心配する価値はありません。とにかく、SQL Serverが内部で最適化できるクエリの種類であるため、すべてのオプションが同等であることがわかります。
EXISTS
は、実際のデータではなく boolean を返します。ベストプラクティスは#3を使用することであると述べています。
実行計画。 学び、使い、愛します
実際に推測する方法はありません。
他の人が言ったことに加えて、 SELECT 1
の使用方法は古いMicrosoft SQL Server(2005年以前)に由来します-そのクエリオプティマイザーは、 SELECT *
の表。私の知る限り、他のDBMSにはこの欠陥がありません。
EXISTSは行の存在をテストしますが、行の内容ではないため、上記と同様のオプティマイザーの癖を除き、SELECTリストの内容は実際には関係ありません。
SELECT *
は最も一般的ですが、他のものも受け入れられます。
#3最適なものである必要があります。とにかく返されたデータは必要ありません。フィールドを持ち込むと、余分なオーバーヘッドが追加されるだけです