現在LOGS
、複数のログブック番号 (列 ) で分割された TABLEがXLOG
あり、限られた時間範囲内でアクセスされています。
テーブルは、クラスター化された「自然な」主キーで宣言されXLOG
ます。XDATE
XHW and XCELL
CREATE TABLE [dbo].[LOGS](
[XDATE] [datetime] NOT NULL,
[XHW] [nvarchar](3) NOT NULL,
[XCELL] [nvarchar](3) NOT NULL,
[XALIAS] [nvarchar](255) NULL,
[XMESSAGE] [nvarchar](255) NULL,
[XLOG] [int] NOT NULL,
CONSTRAINT [PK_LOG] PRIMARY KEY CLUSTERED ([XLOG] ASC,[XDATE] ASC,[XHW] ASC,[XCELL] ASC)
XLOG = 1 OR XLOG = 1002
問題は、同じクエリ (たとえば、以下のサンプル クエリ) のリクエスト #1 で複数のログブックにアクセスするときに発生する恐ろしい実行計画です。
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 1 OR XLOG = 1002)
ORDER BY XDATE DESC, XLOG DESC
編集:必要な 100 行は、ログブック #1 からだけでなく、両方のログブックから、日付順で混在しています。それが両方のクエリが返すものです。
統計はテスト前に更新されました。
実際の実行計画では、基本的にクラスター化インデックス シークを使用して、述語 on XLOG
andを使用して数百万行のデータをフェッチしますXDATE
(ここでは、XLOG= があり、XDATE で TOP オーダーを行っているため、最初と最後の 100 行のみをフェッチする場合があります)。
クラスター化インデックスのシーク操作の詳細:
期待される実行計画は
クエリを書き直そうとしましたが、 以外の方法が見つかりませんでしたUNION ALL
。結果のクエリは同じ結果を返します (正しい計画で!) が、複雑すぎるように感じます (XLOG の JOIN では適応できませんが、それは問題ではありません) リクエスト #2 :
WITH A AS (SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1
ORDER BY XDATE DESC),
B AS (SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1002
ORDER BY XDATE DESC)
SELECT TOP 100 * FROM (
SELECT * FROM A
UNION ALL
SELECT * FROM B
) A
ORDER BY XDATE DESC, XLOG DESC
質問:リクエスト #1 の何が問題になっていますか? 何百万もの行をソートしようとする前に「TOP」を考慮して、どのように書き換え/変更できますか? 問題を解決するには、別のインデックス、HINT、または追加の統計が必要ですか? リクエスト #2 のようにクエリを書き直す必要がありますか?
編集:定量的に、このテーブルには数十のログブックが含まれています。月に 1 つのイベントしかないものもあれば、月に数百万のイベントがあるものもあります。
この種のクエリは、このテーブルに対して最もよく使用されます (追加のフィルターを使用するバリアントは他にもありますが、この問題には関係ありません -- リクエスト #2 を使用する場合の複雑さを除いて)。
編集 #2:クラスター化インデックスを (XLOG,XDATE,...) ではなく (XDATE,XLOG,...) に変更する解決策を試してみました -- nb: この複合主キーは、列 XLOG の選択性。
数千行しかないログブックに対して、このクエリを実稼働データベースのコピーでテストしました。クエリプランは大量の I/O を生成します (XLOG=12
広範囲のXDATE
s から数行だけを除外します)。したがって、この特定のソリューションは問題ありません。
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 12 AND XALIAS LIKE 'KEYWORD%' )
ORDER BY XDATE DESC, XLOG DESC, XHW DESC, XCELL DESC
PS: ちなみに、PostgreSQL 9.1でも同じ動作をしています。つまり、データベースに関連したものではなく、クエリが間違っているか、テーブルの設計が間違っている可能性があります。