web-dev-qa-db-ja.com

SQL INはパフォーマンスに悪いですか?

私は次のようなことをするクエリを持っています:

SELECT FieldX, FieldY FROM A
WHERE FieldW IN (108, 109, 113, 138, 146, 160,
307, 314, 370, 371, 441, 454 ,457, 458, 479, 480,
485, 488, 490, 492, 519, 523, 525, 534, 539, 543,
546, 547, 550, 564, 573, 629, 642, 643, 649, 650,
651, 694, 698, 699, 761, 762, 768, 772, 773, 774,
775, 778, 784, 843, 844, 848, 851, 852, 853, 854,
855, 856, 857, 858, 859, 860, 861, 862, 863, 864,
865, 868, 869, 871, 872, 873, 891) 

非常に多くのオプションを含むIN句があると、クエリのパフォーマンスが低下しますか?アプリケーションで多くのタイムアウトが発生していますが、この種の問題の原因になる可能性があります。適切なSQLヒントを使用して、数値を削除せずにクエリを最適化できますか?

編集:

@KMこれらは別のテーブルのキーです。これはフォーラムアプリケーションです。簡単に説明すると、c#はデータベースからすべてのフォーラムを取得し、アプリキャッシュに保存します。 C#は、これらのフォーラムとこのユーザーのスレッドを取得するプロシージャを呼び出す前に、アクセス許可とビジネスロジックを考慮して、「すべてのフォーラム」コレクションをフィルタリングするロジックを実行します。タイムアウトは、アプリケーション自体ではなくデータベースで発生します。クエリでこのすべてのロジックを実行するには、多くの内部結合が必要になり、プロシージャ内でこれをすべて実行できるかどうかは100%確信できません。

SQL Server 20を使用しています

59

IN演算子を使用してクエリを作成する場合、パフォーマンスに影響する可能性のあるいくつかの考慮事項があります。

最初に、IN句は通常、OR論理接続を使用するために、ほとんどのデータベースによって内部的に書き換えられます。したがってcol IN ('a','b','c')は次のように書き換えられます:(COL = 'a') OR (COL = 'b') or (COL = 'c')。両方のクエリの実行プランは、可能性が高いで、colにインデックスがあると仮定すると同等になります。

2番目に、INまたはORを可変数の引数とともに使用する場合、データベースでクエリを再解析して実行を再構築する必要がありますクエリの実行プランを作成するのはコストのかかるステップになる可能性があります。ほとんどのデータベースは、EXACTクエリテキストをキーとして使用して実行するクエリの実行プランをキャッシュします。同様のクエリを実行しますが、述語の引数値が異なります-データベースが実行プランの解析と構築にかなりの時間を費やす可能性が高いです。これが バインド変数の使用を強くお勧めします as最適なクエリパフォーマンスを確保する方法。

第三に、多くのデータベースには、実行できるクエリの複雑さに制限があります-それらの制限の1つは、述語に含めることができる論理接続詞の数です。あなたの場合、数十の値がデータベースの組み込み制限に達する可能性は低いですが、数百または数千の値をIN句に渡すと予想される場合、それは間違いなく起こります。この場合、データベースは単にクエリ要求をキャンセルします。

第4に、INとORを述部に含むクエリは、並列環境で常に最適に書き換えられるとは限りません。並列サーバーの最適化が適用されないさまざまなケース- MSDNにはまともな紹介があります 並列処理のためのクエリの最適化に使用されます。可能であれば、論理接続詞(OR and IN)など)。

118
LBushkin

FieldSに適切なインデックスがある場合、そのINを使用することは完全に正しいです。

テストしたばかりで、SQL 2000はINを使用するときにクラスター化インデックススキャンを実行します。

5
tekBlues

一時テーブルを作成して、値を挿入し、代わりにIN述語でテーブルを使用できます。

知る限り、_SQL Server 2000_は定数セットのハッシュテーブルを作成できません。これにより、オプティマイザーから_HASH SEMI JOIN_を使用する可能性が奪われます。

これは、FieldW(必要なインデックス)にインデックスがない場合にのみ役立ちます。

FieldX列とFieldY列をインデックスに含めることもできます。

_CREATE INDEX ix_a_wxy ON a (FieldW, FieldX, FieldY)
_

インデックスを使用することによってのみクエリを処理できます。

_SQL Server 2000_には_CREATE INDEX_のINCLUDEオプションがありません。これにより、DMLのパフォーマンスは少し低下しますが、クエリのパフォーマンスは向上します。

更新:

あなたの実行計画から、_(SettingsID, SectionID)_の複合インデックスが必要だと思います

_SQL Server 2000_は確かに定数リストからハッシュテーブルを構築できます(そしてそれを行います)が、_Hash Semi Join_はおそらくクエリクエリの_Nested Loop_よりも効率が悪いでしょう。

補足説明:WHERE条件を満たす行の数を知る必要がある場合は、COUNT(column)を使用せず、代わりにCOUNT(*)を使用してください。

COUNT(column)は、column値がNULLである行をカウントしません。

これは、まず、予期しない結果を取得できることを意味します。次に、列が次のインデックスでカバーされていない場合、オプティマイザーは追加の_Key Lookup_/_Bookmark Lookup_を実行する必要がありますWHERE条件を提供します。

ThreadIdは_CLUSTERED PRIMARY KEY_のように見えるので、このクエリには問題ありませんが、一般的には避けてください。

5
Quassnoi

より良いコーディング方法がありますが、特にSELECTのみの場合、タイムアウトの原因ではないでしょう。ただし、クエリトレースを見れば、それを判断できるはずです。しかし、これを再コーディングすることは、推測による最適化であり、その可能性はほとんどありません。

実際にタイムアウトになるクエリのクエリプランから始めましょう。どのクエリであるかを確実に知っていますか?

3
dkretz

データの分布によっては、WHERE句に述語を追加するとパフォーマンスが向上する場合があります。たとえば、IDのセットがテーブルの合計数に比べて小さく、IDが比較的近いことがわかっている場合(通常、それらは最近追加されたため、範囲の上限でクラスター化される可能性があります)、述語「AND FieldW BETWEEN 109 AND 891」を試して含めることができます(C#コードでセットの最小IDと最大IDを決定した後)。それらの列(インデックス付きの場合)で範囲スキャンを実行すると、現在使用されているものよりも速く動作する可能性があります。

3
Steve Broberg

INは、ORの大きなリストを作成するのとまったく同じです。また、ORはクエリをSARG不可能にすることが多いため、インデックスが無視される可能性があり、プランは完全スキャンに進みます。

2
Remus Rusanu

通常、IN句はパフォーマンスに有害ですが、「悪い」ことはアプリケーション、データ、データベースサイズなどによって異なります。最適なものを確認するには、独自のアプリをテストする必要があります。

1

基本的にそのwhere句が行うことは、「FieldW = 108 OR FieldW = 109 OR FieldW = 113 ...」です。複数選択し、それらを結合で結合します。例:

SELECT FieldX, FieldY FROM A WHERE FieldW = 108
UNION ALL
SELECT FieldX, FieldY FROM A WHERE FieldW = 109

しかし、当然のことながら、非常に多くの値と比較する場合は非現実的です。

別のオプションは、これらの値を一時テーブルに挿入し、Aテーブルをその一時テーブルに結合することです。

1
Tommi

この文を使用するときの速度は、テーブルのサイズによって決まります。非常に大きなテーブルではない場合...このステートメントはパフォーマンスに影響しません。

1
Eric

通常、このようなクエリにはユーザー定義のテーブルタイプを使用します。

CREATE TYPE [dbo].[udt_int] AS TABLE (
    [id] [int] NOT NULL
)

テーブル変数を使用して、各番号の行を入力すると、次のことができます。

SELECT 
    FieldX, 
    FieldY
FROM A
INNER JOIN @myIds B ON
    A.FieldW = B.id
1
Donald.Record

ここにあなたの答えがあります...

http://www.4guysfromrolla.com/webtech/031004-1.shtml

基本的に、文字列を分割し、分割された内容を一時テーブルに入力する関数を作成します。次に、その一時テーブルに結合してデータを操作できます。上記は物事をかなりよく説明しています。私はこのテクニックをよく使います。

特定のケースでは、in句の代わりにtempテーブルへの結合を使用すると、はるかに高速になります。

1
infocyde

次のようなものを試すことができます:

select a.FieldX, a.FieldY
from (
    select FieldW = 108 union
    select FieldW = 109 union
    select FieldW = 113 union
    ...
    select FieldW = 891
) _a
join A a on a.FieldW = _a.FieldW

単一のSQLステートメントを動的に生成する場合など、状況に適している場合があります。私のマシン(SQL Server 2008 Express)では、Aの少数(5)のFieldW値と多数(100,000)の行でテストします。おそらくあなたが探しているものです。

0
yfeldblum

パフォーマンスは、あなたがしようとしていることのコンテキストでのみ判断できます。この場合、約70行の取得を要求しています(これは一意の値であると仮定しています)ので、単一の値を取得する期間の70倍の時間を期待できます。キャッシングまたはコースが原因で少なくなる場合があります。

ただし、クエリオプティマイザーは、値を取得するために全表スキャンを実行する必要があるか、選択する場合があります。この場合、performaceは、同じアクセスプランを介して単一の値を取得する場合とほとんど変わりません。

0
David Aldridge

IN以外を使用できる場合:実行します(場合によってはINを使用していましたが、実際にはあまり良い方法ではありません。existで簡単に置き換えることができ、高速です)。

あなたの場合:それはそれほど悪くないようです。