web-dev-qa-db-ja.com

等しくクエリされる2つの日時列を含むテーブルにインデックスを付ける最良の方法

SQL Server 2008 R2。

約70mのレコード、1秒あたり約10挿入のテーブルがあります。

現在、常に増加するCreatedAt日時列にクラスター化されています。クエリの50%がこの列に関係しています。

別の日時列 "IssuedAt"がありますが、意味は異なりますが、通常はCreatedAtから1日程​​度です。クエリの50%がこの列に関係しています。この列のNCインデックス。

他にもいくつかのFK列があります-必要に応じて、おそらく約150バイト幅です。そして、さらにいくつかのインデックス-テーブルは、さまざまな方法でさまざまなレポートを頻繁に照会されます。

私の質問は、クラスター化インデックスと2つの日時列に関して、このテーブルにインデックスを付ける最良の方法です。

a)クラスタ化されたキーが必要以上に大きくなり(日付時刻+暗黙の識別子)、NCインデックスのサイズが大きくなることが心配です。 INT IDを追加する必要があります。代わりにその上にクラスター化しますか?

b)IssuedAt列に対する私のクエリは、ブックマークルックアップのために高額になる可能性があります。私はそれにますます多くの列を含めることに直面しています(書き込みパフォーマンスを傷つけます)。ここに代替の戦術はありますか?

前もって感謝します。

更新:

わかりやすくするために-私はベンチマークの必要性を認識しています-一部のクエリが満足に満たされていないことがわかります。

CIを含む日時列Aに依存するクエリの50%とNCIを含む日時列Bに依存するクエリの間には、固有の緊張があります。この緊張を相殺するために検討する価値のあるアプローチ/トリックがあるかもしれないと思っていました。たとえば、新しいフィルター処理されたインデックス、または一致する場合は両方の日付コンポーネントであるクラスター化インデックスへの移動、または他の人がこの緊張を和らげるために使用するそれらの線に沿った他の手法。

2回目の更新:

私は以下を検討しています:

  1. 新しい列を作成します:IssuedAtOffset(int)。これは、秒単位でのCreatedAtとIssuedAtの違いです。私はビジネスの現実から、intがこのデルタをキャプチャするのに十分であり、IssuedAtのミリ秒も無関係であるため、それが機能することを知っています。

  2. IssuedAtOffsetを正しい値に更新します。

  3. IssuedAtをドロップします。

  4. 今すぐCIを作成します:CreatedAt + IssuedAtOffset。

  5. CreatedAt + IssuedAtOffsetに基づいて計算列としてIssuedAtが返されるビューを作成します。

したがって、CreatedAtまたはIssuedAtに対するクエリは、そのビューをクエリするときにCIから直接提供できます。追加はすべての行で発生する必要があるため、より多くのCPUを意味しますが、IOの節約と比較すると、それは当然のことながらベンチマークです)。

ここで何か欠点や問題を見逃しましたか?

回目の更新:

上記のアプローチをテストするために2つのテーブルを作成しましたが、結果は期待したものとはかなり異なります。

T1は現在のセットアップを表します。

CREATE TABLE [dbo].[t1](
[CreatedAt] [datetime] NULL,
[IssuedAt] [datetime] NULL,
[Col1] [varchar](20) NULL,
[Col2] [char](4000) NULL

)[PRIMARY]

1つの列のCI。

CREATE CLUSTERED INDEX [IX_Clustered] ON [dbo].[t1] 

([CreatedAt] ASC)

2番目の日時列のNCI。

CREATE NONCLUSTERED INDEX [IX_t1_NC] ON [dbo].[t1] 

([IssuedAt] ASC)

T2は提案されたセットアップを表します。

CREATE TABLE [dbo].[t2](
[CreatedAt] [datetime] NULL,
[IssuedAt] [datetime] NULL,  ---this would be dropped in due course
[Col1] [varchar](20) NULL,
[Col2] [char](4000) NULL,
[IssuedAtOffset] [int] NOT NULL

)[PRIMARY]

両方の列に単一のCI。

CREATE CLUSTERED INDEX [IX_t2_CI] ON [dbo].[t2] 

([CreatedAt] ASC、[IssuedAtOffset] ASC)

100kレコードをt1に挿入し、t2にコピーして、IssuedAtOffsetをT2の正しい値に設定しました。

T2をより簡単に扱うためのビューを作成しました。

create view [dbo].[t2v] 

as createdcreated、dateadd(second、issuedatoffset、createdat)as 'IssuedAtO'、col1、col2 from t2

これで、パフォーマンスの変更を確認する時が来ました。

古いテーブル:

select col2 from t1 where createdat < '2013-01-30'

予想どおり、IX_ClusteredでCIシークを行います。

select col2 from t1 where issuedat  < '2013-01-30'

予想どおり、NCIを使用してからキー検索を使用します。

新しいテーブルで:

select col2 from t2v where createdat < '2013-01-30'

予想どおり、クラスター化インデックスシークを使用します。

CPU:0、読み取り:3、期間:

ここでおもしろい部分:t2vからcol2を選択します。IssuedAtO<'2013-01-30'はクラスター化インデックスSCANを使用します。 CPU:62、読み取り:61094、期間:61

Dateadd関数により、CPUの使用率が高くなると予想していました。また、Col2データにアクセスするためのブックマーク検索がないため、CIを使用できてうれしいです。

しかし、私がスキャンを期待していたかどうかはわかりません-おそらくこれはスキャンでありシークではないため、読み取りは屋根を通り抜けました。

これは、このアプローチの予想される利点を軽減する可能性があります。なぜそれがスキャンに変更されたのか、そして代わりにシークになるようにCIを構築できる方法があるのですか?

ありがとうございました。

6
Nik

ベンチマークに代わるものはありません。質問に答えるために、いくつかの可能なテーブルを作成してデータを入力します。次に、これらのテーブルを一般的なワークロードとベンチマークに公開します。

NCIに列を追加すると、変更が遅くなり、選択が速くなります。両方の頻度に基づいて、より少ないリソースを使用するアプローチを選択できます。行が平均で1年に2回読み取られる場合、すべての行が平均で1分に2回読み取られる場合と比較して、結論が異なる場合があります。

さらに、すべてのクエリが等しく生まれるわけではありません。一部のクエリが特定の時間内に完了する必要がある場合は、これらの要件が満たされていることを確認する必要があります。明らかに、このような要件は、上記の一般的な優れたアプローチよりも優先されます。実際の要件を知ることができるのはあなただけです。

8
A-K