web-dev-qa-db-ja.com

データロギングマスター/詳細テーブルの結合でftsではなくインデックスを使用するようオプティマイザに教える方法は?

SQL Server 2012オプティマイザーは正しく機能しません。

テストケース、概要:

これは簡略化されたテストシナリオです。下部にあるDDLステートメント。

データロギング用の2つのテーブルABがあります。 1:nの関係があります-Aにはa_timeという日時のヘッダーレコードがあり、Bには詳細レコードがあり、フィールドB.akeyA.idを参照しています、およびフィールドnameおよび(data)

Aは約25,000,000レコード、Bには近似値があります。 500,000,000レコード。 Bには、Aの各レコードを参照する約200のレコードがあります。 1つのAと約200個のBレコードが5分ごとに一度に一緒に挿入され、A.a_timeによって反映されます。

クラスター化インデックスは、主キーid、タイプint identityです。

Bには、IX_B_akeyB.akeyという名前の1つの非クラスター化インデックスがあります。

A.a_timeも(非クラスター化)インデックス化されています。

今、このクエリ:

 SELECT A.a_time, B.*
 FROM B
   join A on B.akey = A.id
 where  
    A.a_time > '2017-01-13T01:30:00' and A.a_time < '2017-01-14T07:30:00'
       and B.name in ('name33', 'name55', 'name66')

データベースサーバーで約3分かかります。実行計画: ここ (より正確な実行計画については以下を参照)

IX_B_akeyを使用するための簡単なヒントを追加すると:

 SELECT A.a_time, B.*
 FROM B
   with (index(IX_B_akey))
   join A on B.akey = A.id
 where  
    A.a_time > '2017-01-13T01:30:00' and A.a_time < '2017-01-14T07:30:00'
       and B.name in ('name33', 'name55', 'name66')

1秒未満で実行されます。実行計画: ここ (より正確な実行計画については以下を参照)

両方のテーブルで手動でupdate statisticsを実行しても、これは変わりません。

ヒントのないクエリのクエリプランは、サーバーがBでテーブルスキャンを実行し、一致するnamesを探すことを示しています。これに時間がかかるのは当然のことです。ヒントを使用して、インデックスを使用し、一致するBレコードを参照するAレコードのインデックスを介したルックアップを実行します。これははるかに高速です。

クエリオプティマイザーコードをソフトウェアに挿入したくありません。また、NHibernateを使用しています。可能ですが、NHibernateインターセプターを使用してそのSQLを編集するのは醜いでしょう。

多分オプティマイザは、1つのBレコードを参照するすべてのAレコードが物理的に互いに隣接していることを認識していません。同時に挿入されているため、互いに隣接しています。それらがデータベース全体に散在している場合、すべての検索を実行する方がコストがかかる可能性があります。

質問:オプティマイザがクエリのヒントなしで高速プランを選択できるようにするにはどうすればよいですか?ここで役立つ特定の統計を追加できますか?保存されたクエリプランは必要ですか?

参考までに、テーブルの作成に使用されるDDLステートメントを次に示します。

create table A (
    id int not null identity(1,1),
    a_time datetime,
    constraint pkA primary key (id)
)

create table B (
    id int not null identity(1,1),
    akey int not null references A (id),
    name nvarchar(50),
    d decimal(5,3),
    constraint pkB primary key (id)
    )

create index IX_B_akey on B (akey)
create index IX_A_a_time on A (a_time)

更新:nameをインデックスIX_B_Akeyに追加すると効果的ですが、データ量がほぼ2倍になります。これは良いオプションではありません。

実行計画の更新:質問を投稿した後、同じデータ構造でより多くのデータを持つ別のテストシナリオを作成しました。クエリは同じですが、クエリされる日付範囲が拡張されています。データベースには、Aに1つのmioレコード、Bに200のmioレコードが含まれています。これにより、実際の実行計画を提供できます。

ヒントなしのクエリ、27秒かかり、時間は再現可能

ヒント付きクエリ、3秒かかり、再現可能

5
Moritz Both

悪い計画は難しい選択から来ます。 2つのネストされたループ結合を持つプランと大きな並列ハッシュ結合を持つプランのどちらかをオプティマイザに選択させる代わりに、Bを再編成して、AからBへのアクセスパスを最適化できます。

ここでの最良のインデックスは、Bのクラスター化されたPK(akey、id)を作成することです。その場合、すでに高速の計画にはネストされたループ結合が1つだけあり、並列ハッシュ結合計画よりも明らかに優れています。

計画を強制することもできますが、NHibernateが常にまったく同じクエリを生成することを前提としています(そうすることもできます)。 B)に対して4万2千回の操作を行う必要があるとは考えていません。

問題は、それらのRIDルックアップのコストが非常に高くなることを期待し、スキャンの予想コストが少なくなることです。あまり多くのRIDルックアップを実行する必要がないと説得した場合は、統計を偽造しているため、より適切な計画が得られる可能性があります。ただし、このクエリ以外にも影響を与える可能性があるため、これは一般的に危険です。

だから私は最初に計画を強制することを試みるでしょう。

1
Rob Farley