6

5 つの列 (tableID、CompanyID、UserID、ProjectID、DailyHoursWorked、entryDate) しかない TimeCard という非常に長いテーブル (~3500 万行) があるとします。これは、会社ごと、プロジェクトごと、従業員の 1 日あたりの労働時間を記録する非常に単純な表です。

ここで、特定の会社の従業員の 1 か月あたりのプロジェクトごとの総労働時間を調べるレポートを生成する必要があります。レポートの実行時に必要な集計を実行する代わりに、すべての会社/プロジェクト/ユーザー データが月ごとに集計されたテーブルのようなデータ構造を構築したいので、レポートの実行時にそのデータ構造を直接クエリできます。 3,500 万件までのレコードには数分かかることがあるため、ランタイム集計を実行しません。

だから私は2つの異なる方法があります。(CompanyID、UserID、ProjectID、MonthlyHoursWorked、Month) を列として追加の物理テーブルを作成し、TimeCard テーブルでトリガーを使用して、追加のテーブルの値を変更します。または、インデックス付きビューを作成できます。なので両方試しました。最初に、次のコードでインデックス付きビューを試しました。

CREATE VIEW [dbo].[vw_myView] WITH SCHEMABINDING AS
SELECT 
 JobID,
 ProjectID,
 Sum(DailyHoursWorked) AS MonthTotal,
 DATEADD( Month, DATEDIFF( Month, 0, entryDate), 0 ) AS entryMonth,
 CompanyID,
 COUNT_BIG(*) AS Counter
FROM
 dbo.TimeCard 
Group By DATEADD( Month, DATEDIFF( Month, 0, entryDate ), 0 ), JobID, ProjectID, CompanyID

Go
CREATE UNIQUE CLUSTERED INDEX [IX_someIndex] ON [dbo].[vw_myView] 
(
 [CompanyID] ASC,
 [entryMonth] ASC,
 [UserID] ASC,
 [ProjectID] ASC
)

インデックス付きビューが正しく作成され、合計で合計 500 万行に達しました。

ただし、毎回 SQL キャッシュをクリアして次のクエリを実行すると、*select * from vw_myView where companyID = 1*、ほぼ 3 分かかります。上記のように追加のテーブル ルートを使用すると、キャッシュがクリアされた状態で、約 4 秒かかります。

私の質問は、インデックス付きビューはこの特定のシナリオでは悪い選択ですか? 特に、基になるテーブル (TimeCard) が変更されるたびに、またはそれに対してクエリが実行されるたびに、インデックス付きビュー全体が再計算/再集計されるかどうかを知りたいですか?

ありがとう!

4

6 に答える 6

2

EnterpriseエディションとDeveloperエディションのどちらも使用していない場合は、with (noexpand)ヒントを使用する必要があります。

select * 
from vw_myView with (noexpand)
where companyID = 1

基になるデータが変更されると、ビューはテーブル全体ではなく、変更されたデータに関連する行のみを更新します。これは、挿入の程度が高いOLTPデータベースに悪影響を与える可能性がありますが、使用量が中程度であれば、パフォーマンスの問題は発生しません。

Microsoftからのヒント:

一般的な推奨事項として、ビューまたはビューの基礎となるベーステーブルの変更または更新は、シングルトン操作ではなく、可能であればバッチで実行する必要があります。これにより、ビューのメンテナンスのオーバーヘッドがいくらか削減される可能性があります。

于 2010-03-10T19:46:02.123 に答える
1

インデックス ビューを使用して正しい道を進んでいると思います。TimeCard ただし、集計列について、クエリを実行しているテーブルにインデックスを配置しましたか。JobID, ProjectID, entryDate, CompanyID(1 インデックス)のインデックスを作成する必要があります 。各列に 1 つのインデックスを使用する場合、クエリは 4 つのインデックスすべてを一緒に使用する必要があるため、問題は解決しません。

トリガーの使用は遅くなると思いますが、方法は異なります。クエリは高速になりますが、へのすべての挿入が遅くなりますTimeCard。トリガーを使用することにした場合は、そのテーブルにもインデックスを作成するか、3分遅くはありませんが、データの並べ替えと戻りが遅くなる可能性があることを確認します.

于 2010-03-10T19:51:01.797 に答える
0

私はこれにビューを使用しませんでした。トリガーによって作成されたテーブルが進むべき道だと思います。ただし、挿入だけでなく、更新と削除の合計も調整することを忘れないでください。

于 2010-03-10T19:45:58.370 に答える
0

インデックス付きのビューが必要だとは思いません(インデックス付きのビューは悪い/良い考えだとは言いません)。「CompanyID」列と「EntryDate」列のインデックスが必要だと思います。その後、where条件 "WHERE CompanyID = @CompanyID AND EntryDate> = @StartDate AND EntryDate<=@EndDate"を使用する必要があります。

テーブルが主に「EntryDate」によって処理される場合は、「EntryDate」列でクラスターインデックスを使用できます。

この後、selectステートメントは今よりもはるかに速くなると思います。

于 2010-03-10T19:46:38.797 に答える
0

まあ、インデックス付きビューのアイデアは間違いなく優れており、クラスター化されたインデックスを作成できれば完璧です。クエリは 3 分よりもはるかに高速です。

一方、これらの情報のチャンクが、たとえば月に 1 回または週に 1 回 (または毎晩) だけDailyTimeCard更新される場合は、SSIS パッケージなどによって入力/更新される別のテーブルにそれらを配置することをお勧めします。定期的。

トリガーを使用してそのようなファクト テーブルを常に更新することもお勧めしません。1日の特定の秒ごとに最新のデータが本当に必要な場合は、インデックス付きビューを使用してください。

しかし、インデックス付きビューはかなりの重労働を行います - 合計、グループ化などです。基礎となるテーブルが変更されて更新されている間、それを常に最新の状態に保つとTimeCard、システムにいくらかの負荷がかかりますが、どれくらいかはわかりませんが、かなり目立つ可能性があります.

必要な情報を抽出する方法 (一度グループ化して合計し、その集計データを別のファクト テーブルに格納する方法) を見つけた場合は、DailyTimeCardテーブルに対する高速クエリとクイック クエリの両方が必要であり、システムの残りの部分は少なくなるはずです。インデックス付きビューを常に最新の状態に保つという負担がかかります。

探している解決策ではないかもしれませんが、少し考えてみてください。うまくいくかもしれませんし、うまくいかないかもしれません!

于 2010-03-10T20:43:32.590 に答える
0

テーブルのパーティション化を検討しましたか。リスト分割テーブルとハッシュ分割テーブルの組み合わせが考えられます。

于 2010-03-10T20:02:11.567 に答える