web-dev-qa-db-ja.com

多くの「グループ化」列を持つほとんどすべての行を選択するときのクエリパフォーマンスを改善する

20列と約600,000レコードのテーブルがあります。最大行サイズは約100バイトのみです。テーブルは数日ごとに再入力されますが、レコード数はほぼ同じままです。

現時点では、クラスター化インデックスは1つしかありません。主キーのint ID列です。

このテーブルに依存するクエリとビューがいくつかあります。通常、実行には5〜10秒かかります。すべてのレコード(select * from myTable)を選択すると、すべての結果を取得するのに約4秒かかります。

SQL Serverで500,000レコードを選択するための関連するベンチマークを見つけることができませんでした。 今回は典型的ですか?

これは私がテーブルで実行する典型的なクエリです:

select  CO.Company
    ,CO.Location
    ,CO.Account
    ,CO.SalesRoute
    ,CO.Employee
    ,CO.ProductType
    ,CO.Item
    ,CO.LoadJDate
    ,CO.CommissionRate
    ,SUM(CO.[Extended Sales Price]) AS Sales_Dollars
    ,SUM(CO.[Delivered Qty]) AS Quantity
from    dbo.Commissions_Output CO
where   CO.[Extended Sales Price] <> 0
group by    CO.Company
        ,CO.Location
        ,CO.Account
        ,CO.SalesRoute
        ,CO.Employee
        ,CO.ProductType
        ,CO.Item
        ,CO.LoadJDate
        ,CO.CommissionRate

テーブルに少なくとも1つの非クラスター化インデックスがあると、次の結果が得られます。

スキャンカウント18、論理読み取り18372; CPU時間= 24818ミリ秒、経過時間= 8614ミリ秒。

私はさまざまなインデックスと組み合わせを試しました(フィルター列のインデックス、group-by列を含める、すべてのfilter/group-by列のインデックス、および集計列を含めるなど)。これらはすべて同じパフォーマンスを提供し、ほとんどの場合同じ実行プランを使用します。

クラスタ化インデックス(PK)以外をすべて削除すると、パフォーマンスが最大3〜4秒向上することがよくあります。スキャン回数が半分になる間、論理読み取りは削減されます。

データに関する注意事項:グループ化前のselectおよびwhere句の結果は約500,000行です(ほぼテーブル全体)。グループ化によって結合されるのは約10,000行だけですが、グループ化後も合計レコード数は約500,000のままです。

非クラスター化インデックスのない実行プランは、最もコストのかかる操作が、where句のハッシュ一致(49%)とクラスター化インデックススキャン(35%)であることを示しています。 MSSMSは、[Extended Sales Price]の非クラスター化インデックスを作成することをお勧めします。非クラスター化インデックスが少なくとも1つある実行プランは、最もコストのかかる操作が(group-by列での)並べ替えであることを示しています。

このクエリはほとんどすべてのレコードを返し、group-byは行数をほとんど減らしないと仮定すると、これはクエリが取得できる限りの速さですか?とても遅いようで、記事を読みますSO 1000ミリ秒未満で数十万行を返す人々に関する質問。何かが足りないのか、それともかなり標準的な速度なのか?このテーブルを正規化することは現在オプションではなく、どのようにしたらよいかわからない多くのことが役立ちます。

最後に、このテーブルへの結合を含むいくつかのビューとその他のクエリがあります(いくつかの正規化があります)。最初は結合が悪いなどの理由でそれらのビューとクエリは遅いと思っていましたが、本当の原因はこのテーブルとそれに対する最初のクエリのようです。ほとんどのクエリとビューは、テーブル内のほとんどすべてのデータを処理します。単一の列または行のごく一部を選択している場合、実行時間は問題ありませんが、これはまれです。

pdate:すべての実行時間、計画、およびIO統計です。各クエリを何百回も実行しませんでしたが、実行時間は1000ミリ秒を超える「ホット」と「コールド」の差。

非クラスター化インデックスなし、MAXDOP設定なし: nonc_nomaxdop

テーブル「Commissions_Output」。スキャンカウント9、論理読み取り11263、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。

CPU時間= 6690ミリ秒、経過時間= 4605ミリ秒。 (最大CPU時間= 7516ミリ秒、最小経過時間= 3754ミリ秒。)

非クラスター化インデックスあり、MAXDOP設定なし: nc_nomaxdop

テーブル「Commissions_Output」。スキャンカウント16、論理読み取り6227

CPU時間= 6591 ms、経過時間= 3717 ms。

非クラスター化インデックスなし、MAXDOP 1 nonc_maxdop

テーブル「Commissions_Output」。スキャンカウント1、論理読み取り10278

CPU時間= 2656ミリ秒、経過時間= 4991ミリ秒。

非クラスタ化インデックス付き、MAXDOP 1 nc_maxdop

テーブル「Commissions_Output」。スキャンカウント1、論理読み取り10278

CPU時間= 2656ミリ秒、経過時間= 4991ミリ秒。

使用される非クラスター化インデックス:

create nonclustered index IX_NC_Comm_Output on dbo.Commissions_Output([Extended Sales Price])
include (company, location, account, salesroute, employee, producttype, item, loadjdate, commissionrate, [delivered qty])
5
Zairja

テストした非クラスター化インデックスは、このクエリに最適ではありません。これは、WHERE句および全表スキャンの代わりにインデックススキャンを実行するために使用できますが、GROUP BYには使用できません。

最良のインデックスは、部分インデックス(WHERE句からの不要な行をフィルタリングするため)であり、すべての列がGROUP BYで使用され、次にINCLUDE allである必要があります。 SELECTで使用される他の列:

CREATE INDEX special_ix 
  ON dbo.Commissions_Output
    ( company, location, account, 
      salesroute, employee, producttype, 
      item, loadjdate, commissionrate ) 
INCLUDE 
  ( [Extended Sales Price], [Delivered Qty] ) 
WHERE 
  ( [Extended Sales Price] <> 0 ) ;
3
ypercubeᵀᴹ

別の角度から問題に取り組みたいのですが。

クエリを簡単にするためにいつでもインデックスを付けることができるという@ypercubeに同意します。それは言った:

  • あなたはテーブルが比較的少量のデータを保持していると述べました
  • テーブルは数日に1回だけ再構築されます
  • カバリングインデックスを作成した後でも、テキスト列の集計が一般的なクエリの中で最もコストのかかる部分であることを示しました

クエリを何度も実行する必要がないように、さらに先に進んで事前に集計を作成してみませんか? インデックス付きビュー の理想的なケースのようです。この場合、集約クエリの出力を早い段階で具体化するか、データをCommissions_Outputにロードするときに入力する従来の専用テーブルです。どちらの方法でも、パフォーマンスを大幅に向上させるために、わずかなディスク領域しか犠牲にしません。

インデックス付きビューには、それらを使用する環境に関して多くの制限がありますが、元のテーブルの代わりに自動的に使用されるという大きな利点があります 状況によっては

5
bartover