2017-02-21 4 views
1

2つのテーブルを結合したときにTOP(1)(またはEXISTS)selectステートメントでパフォーマンスに問題があります。TOP(1)のパフォーマンスが複数のテーブルで選択されています

私はSQL Server 2008 R2を使用しています。

私は2つのテーブルがあります。テーブルが100万枚のレコードのレコードと値で1.5億の周りに、多くのデータが含まれている、と彼らはまだある

CREATE NONCLUSTERED INDEX IDX_Records ON Records(User ASC, RecordType ASC) INCLUDE(Id) 
CREATE NONCLUSTERED INDEX IDX_Values ON Values(RecordId ASC, Field ASC) INCLUDE(Value) 
CREATE NONCLUSTERED INDEX IDX_ValuesByVal ON Values(Field ASC, Value ASC) INCLUDE(RecordId) 

:インデックスと

CREATE TABLE Records(
    Id PRIMARY KEY INT NOT NULL, 
    User INT NOT NULL, 
    RecordType INT NOT NULL) 

CREATE TABLE Values(
    Id PRIMARY KEY BIGINT NOT NULL, 
    RecordId INT NOT NULL, 
    Field INT NOT NULL, 
    Value NVARCHAR(400) NOT NULL, 
    CONSTRAINT FK_Values_Record FOREIGN KEY(RecordId) REFERENCES Records(Id)) 

を成長する。いくつかのユーザーには、大量のデータがあり、少量のデータしかありません。

一部のユーザーとフィールドの組み合わせでは、値テーブルにレコードが存在しない場合がありますが、その他のユーザー/フィールドでは、そのユーザーのレコードテーブルとほぼ同じ数のレコードが値テーブルにあります。

ユーザー/フィールドの組み合わせに関するデータがあるかどうかをテストしたいと思います。このクエリでの問題は、実行計画は、サーバーのキャッシュになかったし、最初のユーザーが大量のデータを持っていなかった場合は、サーバが実行計画を置くこと、だった

SELECT TOP(1) V.Field 
FROM Records R 
INNER JOIN Values V ON V.RecordId = R.Id 
WHERE R.User = @User 
AND R.RecordType = @RecordType 
AND V.Field = @Field 

:私の最初の試みは、このでした多くのデータを持つユーザーにとってうまく機能しなかったこのクエリでは、タイムアウト(15秒以上)が発生しました。同じ問題がRecordTypesまたはFieldsで発生しました。だから、変数を使うのではなく、そのIDをハードコードする必要があった。

SELECT TOP(1) V.Field 
FROM Records R 
INNER JOIN Values V ON V.RecordId = R.Id 
WHERE R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 

でも、サーバーでも、利用可能なインデックスを使用するのではなく、テーブルスキャンを実行してタイムアウトになることがあります。

SELECT TOP(1) V.Field 
FROM Records R WITH (FORCESEEK) 
INNER JOIN Values V WITH (FORCESEEK) ON V.RecordId = R.Id 
WHERE R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 

しかし、今でも、サーバーは時々最初のレコードテーブルに追求して、値テーブルでは、代わりの値テーブルにして、記録に求めて最初:だから私は、クエリにFORCESEEKを追加する必要がありましたまた、タイムアウトが発生します。なぜこの結果がタイムアウトになるのか分かりませんが、それはあります。フィールドが私のモデルでRECORDTYPEにリンクされているように、私は、私はもはや任意のタイムアウトを持って、この最後の変更により値テーブルに

SELECT TOP(1) V.Field 
FROM Records R WITH (FORCESEEK) 
INNER JOIN Values V WITH (FORCESEEK) ON V.RecordId = R.Id 
WHERE R.User = 123 
AND V.Field = 67 

を最初のシークのサーバーを強制的に、RECORDTYPE句を削除し、まだ問い合わせができ1~2秒程度、時には5~7秒程度かかることもあります。

これはなぜこの多くの時間がかかるのかまだ分かりません。

長いクエリ時間を避けるためにこのクエリを改善する方法はありますか?

+0

ファーストを試みるためにあなたはそれが意味をなすようにする必要があります。 ORDER BYを持たないTOPは、決定不能な行を返すので、最初にそれを決定し、それをクエリに導入する必要があります。 – LoztInSpace

+0

TOP(1)をEXISTS(同じ実行プランがあります)として使用していますが、どちらの行が返されても気にしません。 ORDER BYを追加すると、サーバーはWHERE句に準拠したすべての(千の)行を選択し、TOP文を実行する前にソートします。 – Marc

+2

'OPTION(RECOMPILE)'を使用して、渡される特定の値のクエリを再コンパイルすることができます。 –

答えて

0

は、任意の違いを作るべきではなく、にやにや笑いは

SELECT TOP(1) 1 
FROM Records R 
JOIN Values V 
    ON V.RecordId = R.Id 
AND R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 
関連する問題