web-dev-qa-db-ja.com

SQL Selectの実行に時間がかかりすぎる

これは一時テーブルからの単純な選択であり、既存のテーブルを主キーに結合したままにします。結合されたテーブルを参照するトップ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することです。

9
Smur

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は必要なく、クエリは非常に高速に実行されます。

7
Maziar Taheri

そのサブは、遅い選択を引き起こしているあなたの列選択で選択します。左結合で副選択を使用するか、以下で定義した派生テーブルを使用する必要があります。

番目のテーブルの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)
6
John Hartsock

代わりにクロス適用を試してください

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を使用したインラインクエリを使用することもできます。

2
Conrad Frix

JOINビットを句の主要部分の外に移動し、副選択として配置します。それをWHEREおよびJOINセクションに移動すると、TOP 1を何度も選択する必要がないことが保証されます。これが遅い理由です。これを確認したい場合は、実行計画を確認してください。

2

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 indexesThirdTable.SomeColumnおよびThirdTable.SomeOtherColumnに対して定義され、インデックスが一意であることを確認する必要があります。つまり、ThirdTable参照をさらに修飾して、複数の行の選択を排除し、パフォーマンスを向上させる必要があります。 sub selectsLEFT JOINCROSS APPLYの選択は、固有の選択性を確保するために列を追加してThirdTable.SomeColumnおよびThirdTable.SomeOtherColumnの選択性を改善するまで、実際には重要ではありません。それまでは、引き続きパフォーマンスが低下することを期待しています。

covering indexトピックは、Maziar Taheriによって適切に導入されています。彼の仕事は繰り返さないが、カバーインデックスの使用を心に留める必要性を強調する。

つまり、関連するテーブル内の列を追加して一意の行の一致を保証することにより、ThirdTable.SomeColumnおよびThirdTable.SomeOtherColumnクエリ(または結合)の選択性を向上させます。これが不可能な場合は、エンジンが行のプルでビジー状態になるため、引き続き破棄されるため、パフォーマンスの問題が引き続き発生します。これは、I/O、CPU、そして最終的には実行計画に影響を与えます。

2
Robert Miller