2

簡単なクエリがあります:

select top 10 * 
FROM Revision2UploadLocations r2l
inner join Revisions r on r2l.RevisionId = r.Id
INNER JOIN [Databases] [D] on [R].[DatabaseId] = [D].[Id]
INNER JOIN [SqlServers] [S] on [D].[InstanceId] = [S].[Id]
where --r.ValidationStatus in (2, 3) and 
r2l.[ChecksumWasSent] = 0 AND r2l.Status = 2

このクエリは通常 0.5 秒間実行されます。 実行計画 1

しかし、コメントされていない条件を持つ同じクエリが 5 秒間実行され (!!!)、非常に奇妙な実行計画があります (リンクされた列がなく、最も選択的な条件 "r2l.[ChecksumWasSent] = 0 AND r2l があるにもかかわらず、リビジョンと SqlServers が結合されます.Status = 2" は、クエリ処理の最後に実行されます。 実行計画 2

ValidationStatus は通常の int 非 null 列です。列 Revision2UploadLocations.RevisionId、Revisions.DatabaseId、Databases.InstanceId にインデックスが付けられます。テーブルの説明は次のとおりです。

CREATE TABLE [SqlServers]
(
    [Id] int identity(1,1) NOT NULL CONSTRAINT PK_SqlServers PRIMARY KEY,
...
)

CREATE TABLE [Databases](
    [Id] int identity(1,1) NOT NULL CONSTRAINT PK_Databases PRIMARY KEY,
    [InstanceId] int NOT NULL,
    [Name] nvarchar(128) NOT NULL,
...
    CONSTRAINT FK_Databases_SqlServers FOREIGN KEY ([InstanceId]) REFERENCES [SqlServers]([Id])
)

CREATE INDEX [IX_Databases_DatabaseId] ON [Databases] ([InstanceId] ASC)

CREATE TABLE [Revisions]
(
    [Id] int identity(1, 1) NOT NULL,
    [DatabaseId] int NOT NULL,
    [BackupStatus] tinyint NOT NULL,
    [ValidationStatus] tinyint NOT NULL,
...
    CONSTRAINT PK_Revisions PRIMARY KEY([Id]),
    CONSTRAINT FK_Revisions_Databases FOREIGN KEY ([DatabaseId]) REFERENCES [Databases]([Id])
)

CREATE INDEX [IX_Revisions_DatabaseId] ON [Revisions] ([DatabaseId] ASC)

CREATE TABLE [Revision2UploadLocations]
(
    [Id] int NOT NULL IDENTITY (1, 1) CONSTRAINT PK_Revision2UploadLocations PRIMARY KEY,
    [Status] int NOT NULL,
    RevisionId int NOT NULL,
    [ChecksumWasSent] bit NOT NULL,
    CONSTRAINT FK_r2l_Revisions FOREIGN KEY ([RevisionId]) REFERENCES [Revisions]([Id])
)

CREATE INDEX [IX_Revision2UploadLocations_RevisionId] ON [Revision2UploadLocations] ([RevisionId] ASC)

このクエリのパフォーマンスを改善するにはどうすればよいですか?

EDIT今、いくつかの詳細があります: 一部のテーブル (SqlServers および Databases) には 1 ~ 10 のレコードがありますが、Revisions および Revision2UploadLocations) には 500K+ のレコードがあるため、クエリの最適化では、小さなテーブルのインデックス検索の代わりにフル スキャンを使用することを決定し、最初に取得します。 クエリ パフォーマンス チューニング (SQL Server Compact) :

小さなテーブルとは、コンテンツが 1 つまたは数ページのデータ ページに収まるテーブルです。通常、テーブル スキャンを実行する方が効率的であるため、非常に小さなテーブルのインデックス作成は避けてください。

一時的な解決策として、クエリ ヒントFORCE ORDER : Query Hint (SQL Server Compact)を使用しようとしまし たが、応答時間が 5 秒から 0.5 秒に短縮されました。

しかし、私はそれが良い解決策だとは思いません。

4

2 に答える 2

0

Geoffrey のソリューションでは、期待した結果が得られません。最初のステートメントは、r.ValidationStatus が 2 または 3 であることを保証せずに 10 行を選択します。クエリを次のように書き直すことができると思います。

SELECT top 10 *     
FROM Revisions r
  INNER JOIN Revision2UploadLocations r2l 
    ON r2l.RevisionId = r.Id
      AND r2l.[ChecksumWasSent] = 0 
      AND r2l.Status = 2
  INNER JOIN [Databases] [D] on [D].[Id] = [R].[DatabaseId]
  INNER JOIN [SqlServers] [S] on [S].[Id] = [D].[InstanceId]
WHERE r.ValidationStatus in (2, 3)

r2l.[ChecksumWasSent] データ型がビット (ブール値) の場合:

  • 1 より大きい 0 の場合、RevisionId + Status でインデックスを作成できます
  • 1 が 0 よりもはるかに多い場合、RevisionId + ChecksumWasSent + Status を作成して調べることができます
于 2015-10-07T08:18:28.020 に答える
0

過去に、最初にクエリの最初の部分を一時テーブルに挿入し、さらにフィルターをかけたいフィールド ("ValidationStatus"] を挿入すると、一時テーブルにクエリを実行すると、パフォーマンス/速度が大幅に向上することがわかりました。最初のクエリは次のようになります。

select * 
into #tmp
FROM Revision2UploadLocations r2l
inner join Revisions r on r2l.RevisionId = r.Id
INNER JOIN [Databases] [D] on [R].[DatabaseId] = [D].[Id]
INNER JOIN [SqlServers] [S] on [D].[InstanceId] = [S].[Id]
where --r.ValidationStatus in (2, 3) and 
r2l.[ChecksumWasSent] = 0 AND r2l.Status = 2

最終的な選択は次のようになります。

select * from #tmp 
where ValidationStatus in (2,3)

インデックスは必要ありません。オプティマイザーが常に機能するとは限らないことはわかっていますが、このアプローチは過去に何度か役に立ちました。

于 2015-10-06T13:30:25.457 に答える