サブクエリを正しく使用しないと、パフォーマンスが著しく低下することはよく知られています。ユーザーがフィルター処理された一連のレコードをテーブルから取得する必要がある非常に特殊なシナリオがあります。多種多様なフィルターが利用可能になり、合成をサポートする必要があります。さらに、新しいフィルターは、開発者グループによって定期的に作成されます。
膨大な数のパラメーターを使用して、1 つの成長するモノリシックな SQL クエリを作成するという考えは好きではありません。私は、同一の SELECT ステートメントとさまざまな WHERE 句を使用した一連の自律型 SQL クエリのアイデアが好きではありません。動的 SQL クエリのアイデアは気に入っていますが、どのような構造を使用すればよいかわかりません。4 つの基本的なオプションを考えることができます (不足しているオプションが他にもある場合は、遠慮なく提案してください)。
- "INNER JOIN": INNER JOINS を使用してフィルターを連結し、結果をフィルター処理します。
- 「FROM サブクエリ」: FROM ステートメントのサブクエリを介してフィルターをスタックします。
- 「WHERE サブクエリ」: WHERE 句のサブクエリを介してフィルタを連結します。
- 「INNER JOIN サブクエリ」: 奇妙なハイブリッド。
それらをデモンストレーション(およびプロファイル)するためにSQLフィドルを作成しました:
以下は、私が話していることのアイデアを提供するためのフィドルからの抜粋です。
------------------------------------------------------------------------
--THIS IS AN EXCERPT FROM THE SQL FIDDLE -- IT IS NOT MEANT TO COMPILE--
------------------------------------------------------------------------
--
--"INNER JOIN" test
SELECT COUNT(*)
FROM
@TestTable Test0
INNER JOIN @TestTable Test1 ON Test1.ID=Test0.ID AND Test1.ID % @i = 0
INNER JOIN @TestTable Test2 ON Test2.ID=Test0.ID AND Test2.ID % @j = 0
INNER JOIN @TestTable Test3 ON Test3.ID=Test0.ID AND Test3.ID % @k = 0
--
--"FROM subqueries" test
SELECT COUNT(*) FROM (
SELECT * FROM (
SELECT * FROM (
SELECT * FROM @TestTable Test3 WHERE Test3.ID % @k = 0
) Test2 WHERE Test2.ID % @j = 0
) Test1 WHERE Test1.ID % @i = 0
) Test0
--
--"WHERE subqueries" test
SELECT COUNT(*)
FROM @TestTable Test0
WHERE
Test0.ID IN (SELECT ID FROM @TestTable Test1 WHERE Test1.ID % @i = 0)
AND Test0.ID IN (SELECT ID FROM @TestTable Test2 WHERE Test2.ID % @j = 0)
AND Test0.ID IN (SELECT ID FROM @TestTable Test3 WHERE Test3.ID % @k = 0)
--
--"INNER JOIN subqueries" test
SELECT COUNT(*)
FROM
TestTable Test0
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @i = 0) Test1 ON Test1.ID=Test0.ID
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @j = 0) Test2 ON Test2.ID=Test0.ID
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @k = 0) Test3 ON Test3.ID=Test0.ID
--
--"EXISTS subqueries" test
SELECT COUNT(*)
FROM TestTable Test0
WHERE
EXISTS (SELECT 1 FROM TestTable Test1 WHERE Test1.ID = Test0.ID AND Test1.ID % @i = 0)
AND EXISTS (SELECT 1 FROM TestTable Test2 WHERE Test2.ID = Test0.ID AND Test2.ID % @j = 0)
AND EXISTS (SELECT 1 FROM TestTable Test3 WHERE Test3.ID = Test0.ID AND Test3.ID % @k = 0)
ランキング(テスト実行時間)
SQL フィドル:
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 5174 | 777 | 7240 | 5478 | 7359 |
ローカル環境: (キャッシュなし: すべてのテストの前にバッファをクリア)
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 3281 | 2851 | 2964 | 3148 | 3071 |
ローカル環境: (キャッシュあり: クエリを 2 回続けて実行し、2 回目の実行時間を記録します)
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 284 | 50 | 3334 | 278 | 408 |
各ソリューションには長所と短所があります。WHERE 句のサブクエリのパフォーマンスは非常に悪いです。FROM 句のサブクエリは、かなり優れたパフォーマンスを発揮します (実際には、通常は最高のパフォーマンスを発揮します) (注: この方法では、インデックスの利点が無効になると思いますか?)。サブクエリとは異なり、INNER JOIN は同じコンテキストで動作するため (テーブル エイリアスの衝突を避けるために中間システムが必要になるため)、興味深いスコープの問題がいくつか発生しますが、INNER JOIN はかなり優れたパフォーマンスを発揮します。
全体として、最もクリーンなソリューションは FROM 句のサブクエリだと思います。フィルタは簡単に記述およびテストできます (INNER JOIN とは異なり、コンテキスト/ベース クエリを提供する必要がないため)。
考え?これはサブクエリの有効な使用法ですか、それとも災害が起こるのを待っていますか?
更新 (2012/10/04):
- 「EXISTS」メソッドのテストを含めるように SQL Fiddle を更新しました
- SQL Fiddle とローカル環境からのパフォーマンス テストを追加