これは一時テーブルからの単純な選択であり、既存のテーブルを主キーに結合したままにします。結合されたテーブルを参照するトップ1を使用する2つのサブ選択があります。
コードで:
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND
TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
JoinedTable.WhereColumn IN (1, 3)
これは私のクエリの正確なレプリカです。
2つのサブ選択を削除すると、うまく実行されます。 2つのサブ選択を使用すると、1秒あたり約100レコードを取得します。これは、ほぼ100万のレコードを返す必要があるため、このクエリでは非常に低速です。
すべてのテーブルに主キーがあるかどうかを確認しましたが、すべてあります。それらはすべて、それらのWHERE句にあるものやJOIN句にあるものと同様に、重要な列のインデックスと統計を持っています。主キーもインデックスも定義されていない唯一のテーブルは一時テーブルですが、遅い副選択に関連するものではないため問題でもありません。前述したように、副選択がないため、問題なく実行されます。
それらなしでTOP 1
複数の結果を返し、エラーを発生させます。
誰か助けて?
[〜#〜]編集[〜#〜]:
そのため、実行計画から、インデックスが欠落していることがわかりました。私はそれを作成し、他のいくつかのインデックスを再作成しました。しばらくすると、実行プランはそれらを使用していたため、クエリは高速に実行されます。唯一の問題は、同じクエリに対して、別のサーバーでこれを再度実行できないことです。したがって、私の解決策は、SQL Serverが使用するインデックスをHINTすることです。
100万件のレコードクエリでは、_OUTER JOINS
_のようなものを避ける必要があると思います。 _UNION ALL
_の代わりに_LEFT JOIN
_を使用することをお勧めします。 _CROSS APPLY
_がselect句のサブクエリよりも効率的であると思う限り、私は正しいと思うConard Frixによって書かれたクエリを変更します。
今:クエリの変更を開始したときに、WHERE句がJoinedTable.WhereColumn IN (1, 3)
であることに気付きました。この場合、フィールドがnullの場合、条件はfalseになります。では、null値の行をフィルタリングしているときに、なぜLEFT JOINを使用しているのでしょうか。 _LEFT JOIN
_を_INNER JOIN
_に置き換えるだけで、高速になることを保証します。
INDEX:について
テーブルにインデックスがある場合は、
_table1(a int, b nvarchar)
_
そしてあなたのインデックスは:
_nonclustered index ix1 on table1(a)
_
そしてあなたはこのようなことをしたいです:
_select a,b from table1
where a < 10
_
インデックスに列b
を含めていないので、どうなりますか?
sql-serverがインデックスを使用する場合、"Index Seek"と呼ばれるインデックスを検索してから、メインテーブルを参照してと呼ばれる列b
を取得する必要があります。 「ルックアップ」。この手順は、テーブル自体をスキャンするよりもはるかに時間がかかる可能性があります:"Table Scan"。
しかし、sql-serverが持っている統計に基づいて、そのような状況では、インデックスをまったく使用しない可能性があります。
そのため、まず_Execution Plan
_をチェックして、インデックスが使用されているかどうかを確認します。
はいまたはいいえ両方の場合、選択しているすべての列が含まれるようにインデックスを変更します。次のように言います:
_nonclustered index ix1 on table1(a) include(b)
_
この場合、Look Upは必要なく、クエリは非常に高速に実行されます。
そのサブは、遅い選択を引き起こしているあなたの列選択で選択します。左結合で副選択を使用するか、以下で定義した派生テーブルを使用する必要があります。
番目のテーブルの2つのインスタンスへの左結合の使用
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
JoinedTable.WhereColumn IN (1, 3)
派生テーブルの使用
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
DerivedTable.Col1,
DerivedTable.Col2,
DerivedTable.ThirdTableColumn1,
DerivedTable.ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN (SELECT
JoinedTable.PKColumn2,
JoinedTable.Col1,
JoinedTable.Col2,
JoinedTable.WhereColumn,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM JoinedTable
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn)
DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND
TempTable.PKColumn2 = DerivedTable.PKColumn2)
WHERE
DerivedTable.WhereColumn IN (1, 3)
代わりにクロス適用を試してください
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTableColumn1.col1,
ThirdTableColumn2.col1
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTablePKColumn2)
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
WHERE
JoinedTable.WhereColumn IN (1, 3)
CTEとrow_number、またはMINを使用したインラインクエリを使用することもできます。
JOINビットを句の主要部分の外に移動し、副選択として配置します。それをWHEREおよびJOINセクションに移動すると、TOP 1を何度も選択する必要がないことが保証されます。これが遅い理由です。これを確認したい場合は、実行計画を確認してください。
ThirdTable
参照(例ではサブ選択)には、クエリの他の部分と同じインデックスアテンションが必要です。
副選択を使用するかどうかに関係なく:
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
LEFT JOINS(John Hartsockの提案による):
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
CROSS APPLY(Conrad Frixが提案):
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2
covering indexes
がThirdTable.SomeColumn
およびThirdTable.SomeOtherColumn
に対して定義され、インデックスが一意であることを確認する必要があります。つまり、ThirdTable
参照をさらに修飾して、複数の行の選択を排除し、パフォーマンスを向上させる必要があります。 sub selects
、LEFT JOIN
、CROSS APPLY
の選択は、固有の選択性を確保するために列を追加してThirdTable.SomeColumn
およびThirdTable.SomeOtherColumn
の選択性を改善するまで、実際には重要ではありません。それまでは、引き続きパフォーマンスが低下することを期待しています。
covering index
トピックは、Maziar Taheriによって適切に導入されています。彼の仕事は繰り返さないが、カバーインデックスの使用を心に留める必要性を強調する。
つまり、関連するテーブル内の列を追加して一意の行の一致を保証することにより、ThirdTable.SomeColumn
およびThirdTable.SomeOtherColumn
クエリ(または結合)の選択性を向上させます。これが不可能な場合は、エンジンが行のプルでビジー状態になるため、引き続き破棄されるため、パフォーマンスの問題が引き続き発生します。これは、I/O、CPU、そして最終的には実行計画に影響を与えます。