次のT-SQLコードがあるとします。
SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId;
WHERE b.IsApproved = 1;
次のコードも同じ行のセットを返します。
SELECT * FROM Foo f
INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId);
これはここでの最良の例ではないかもしれませんが、これら2つの間にパフォーマンスの違いはありますか?
いいえ、クエリオプティマイザーは両方の例で同じ実行プランを選択できるほどスマートです。
SHOWPLAN
を使用して実行プランを確認できます。
それでも、すべての結合接続はON
句に、すべての制限はWHERE
句に置く必要があります。
外部結合との違いに注意してください。 ON
のJOIN
条件にb.IsApproved
(右側のテーブルではBar)のフィルターが追加されたクエリ:
SELECT *
FROM Foo f
LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId);
[〜#〜]ではない[〜#〜]は、WHERE
句にフィルターを配置するのと同じです:
SELECT *
FROM Foo f
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved = 1);
Bar
への「失敗した」外部結合(つまり、b.BarId
にf.BarId
がない場合)のため、このような失敗したすべての結合行のb.IsApproved
はNULL
のままになり、これらの行は除外された。
これを別の見方で見ると、最初のクエリでは、LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId)
は常にLEFTテーブル行を返します。これは、LEFT OUTER JOIN
は、結合が失敗した場合でもLEFTテーブル行が返されることを保証するためです。ただし、(b.IsApproved = 1)
条件にLEFT OUTER JOIN
を追加すると、(b.IsApproved = 1)
がfalseの場合、つまり、LEFT JOIN
の(b.BarId = f.BarId)
条件に通常適用されるのと同じルールに従って、右側のテーブル列がNULLになります。
更新:コンラッドが尋ねた質問を完了するには、オプションフィルターの同等のLOJは次のようになります。
SELECT *
FROM Foo f
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved IS NULL OR b.IsApproved = 1);
つまり、WHERE
句は、結合が失敗するかどうかの条件(NULL)
とフィルターが無視される条件、および結合が成功し、フィルターを適用する必要がある場所の両方を考慮する必要があります。 (b.IsApproved
またはb.BarId
はNULL
でテストできます)
SqlFiddleをここにまとめます を使用して、JOIN
に対するb.IsApproved
フィルターのさまざまな配置の違いを示します。
SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;
これがより良いフォームです。読みやすく、変更も簡単です。ビジネスの世界では、これはあなたがやりたいことです。ただし、パフォーマンスに関しては同じです。
私は、4つのテーブルに対してクエリのテストを実行しました-1つのプライマリテーブルに3つのINNER JOINと合計4つのパラメーターがあり、両方のアプローチの実行プランを比較しました(JOINのONのフィルター条件を使用して、さらにWHERE句)。
実行計画はまったく同じです。これはSQL Server 2008 R2で実行しました。
最近のバージョンのMSSQLでもオプティマイザが十分にスマートではない場合がいくつかありますが、パフォーマンスの違いはひどいものでした。
しかし、これは例外であり、ほとんどの場合、SQL Serverオプティマイザは問題を解決して適切な計画を立てます。
そのため、WHERE句でフィルターを使用するポリシーを維持し、必要に応じて最適化します。