テーブルが1つあるとしましょう
CREATE TABLE Ticket (
TicketId int NOT NULL,
InsertDateTime datetime NOT NULL,
SiteId int NOT NULL,
StatusId tinyint NOT NULL,
AssignedId int NULL,
ReportedById int NOT NULL,
CategoryId int NULL
);
この例TicketId
では主キーです。
ユーザーがこのテーブルに対して「部分的にアドホック」なクエリを作成できるようにしたい。クエリのいくつかの部分が常に修正されるので、私は部分的に言います:
- クエリは常に範囲フィルターを実行します
InsertDateTime
- クエリは常に
ORDER BY InsertDateTime DESC
- クエリは結果をページングします
ユーザーは、オプションで他の列をフィルターに掛けることができます。フィルターは、なし、1つ、または多くでフィルターできます。そして、各列に対して、ユーザーは分離として適用される値のセットから選択できます。例えば:
SELECT
TicketId
FROM (
SELECT
TicketId,
ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
FROM Ticket
WHERE InsertDateTime >= '2013-01-01' AND InsertDateTime < '2013-02-01'
AND StatusId IN (1,2,3)
AND (CategoryId IN (10,11) OR CategoryId IS NULL)
) _
WHERE RowNum BETWEEN 1 AND 100;
ここで、テーブルに100,000,000行あると想定します。
私が思いつくことができる最高のものは、「オプション」の各列を含むカバリングインデックスです。
CREATE NONCLUSTERED INDEX IX_Ticket_Covering ON Ticket (
InsertDateTime DESC
) INCLUDE (
SiteId, StatusId, AssignedId, ReportedById, CategoryId
);
これにより、次のようなクエリプランが得られます。
- 選択する
- フィルタ
- 上
- シーケンスプロジェクト(計算スカラー)
- セグメント
- インデックスシーク
- セグメント
- シーケンスプロジェクト(計算スカラー)
- 上
- フィルタ
なかなかいい感じです。コストの約80%〜90%は、理想的なIndex Seekオペレーションによるものです。
この種の検索を実装するためのより良い戦略はありますか?
「固定」部分の結果セットが100秒または1000秒になる場合があるため、オプションのフィルタリングをクライアントにオフロードする必要はありません。その場合、クライアントはソートとページングも担当しますが、これはクライアントにとって作業が多すぎる可能性があります。
RowNum BETWEEN 101 AND 200
?