1

現在LOGS、複数のログブック番号 (列 ) で分割された TABLEがXLOGあり、限られた時間範囲内でアクセスされています。

テーブルは、クラスター化された「自然な」主キーで宣言されXLOGます。XDATEXHW 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 XLOGandを使用して数百万行のデータをフェッチします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広範囲のXDATEs から数行だけを除外します)。したがって、この特定のソリューションは問題ありません。

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

多くの I/O を備えた適切なクエリ プラン

PS: ちなみに、PostgreSQL 9.1でも同じ動作をしています。つまり、データベースに関連したものではなく、クエリが間違っているか、テーブルの設計が間違っている可能性があります。

4

2 に答える 2

1

問題は、必要な最初の 100 行がすべて XLOG=1 であることをデータベースが認識していないため、考えられるすべての XLOG を取得してから、最初の 100 行を見つけるために並べ替える必要があることです。

2番目のケースでは、より多くの情報を提供するか、選択した行を削減して、オプティマイザがソートにインデックスを使用できるようにします。

もう 1 つの方法は、XDATE DESC、XLOG DESC でクラスター化インデックスを作成することです。これにより、オプティマイザーは、ソートして主キーをハッシュまたは他のインデックスにする必要がないことを認識します。これは、このクエリが最も使用されるクエリである場合にのみ意味があります。

于 2012-07-17T09:40:40.037 に答える
0

XDate による並べ替えが問題を引き起こしています。上位 100 を取得するには、データを xdate で並べ替える必要があるため、この並べ替えが必要です。最善の方法は、xdate、xlog にインデックスを作成することです。しかし、それはオーバーヘッドを追加します。これは、他のことが機能していない場合のオプションです。以下の方法を試してください。

SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG
into #mytop100
FROM LOGS 
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250' 
AND (XLOG = 1) 
ORDER BY XDATE DESC
union all
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 XDATE, XHW, XCELL, XMESSAGE, XLOG from #mytop 100 ORDER BY XDATE DESC, XLOG DESC 

また、#mytop100 を使用せずにメインの SQL を試して、適切なプランが得られるかどうかを確認してください。きっとそうなるでしょうが、それでもチェックしてください。

于 2012-07-17T11:17:24.447 に答える