1

SQLフィドル:http ://sqlfiddle.com/#!3/23cf8

このクエリではIn、Idに句があり、他の列も選択すると、最初にInが評価され、次に、RIDルックアップを介してDetails列と他の列が取得されます。

--In production and in SQL Fiddle, Details is grabbed via a RID Lookup after the In clause is evaluated
    SELECT [Id]
      ,[ForeignId]    
     ,Details    
      --Generate a numbering(starting at 1) 
      --,Row_Number() Over(Partition By ForeignId Order By Id Desc) as ContactNumber --Desc because older posts should be numbered last
  FROM SupportContacts
  Where foreignId In (1,2,3,5)

このクエリでは、詳細はテーブルスキャンを介して取得されます。

With NumberedContacts AS 
(
    SELECT [Id]
      ,[ForeignId]
      --Generate a numbering(starting at 1) 
      ,Row_Number() Over(Partition By ForeignId Order By Id Desc) as ContactNumber --Desc because older posts should be numbered last
  FROM SupportContacts
  Where ForeignId In (1,2,3,5) 
)
Select nc.[Id]
      ,nc.[ForeignId]   
      ,sc.[Details]
From NumberedContacts nc
Inner Join SupportContacts sc on nc.Id = sc.Id
Where nc.ContactNumber <= 2 --Only grab the last 2 contacts per ForeignId
;

SqlFiddleでは、2番目のクエリは実際にRIDルックアップを取得しますが、100万レコードの本番環境では、テーブルスキャンが生成されます(IN句は行の99%を削除します)

それ以外の場合、SQL Fiddleに表示されるクエリプランは同じです。唯一の違いは、2番目のクエリのSQL FiddleのRIDルックアップは、本番環境でのテーブルスキャンです:(

  1. この動作を引き起こす可能性を理解したいですか?ここでテーブルスキャンを使用して原因を特定するために、どのようなことを検討しますか?

  2. そこでRIDルックアップを使用するように影響を与えるにはどうすればよいですか?

実際の実行プランで運用コストを見ると、RIDルックアップを使用できるようになれば、2番目のクエリのパフォーマンスを最初のクエリに非常に近づけることができると思います。列を選択しない場合Detail、両方のクエリのパフォーマンスは本番環境で非常に近くなります。そのような他の列を追加した後でのみDetail、2番目のクエリのパフォーマンスが大幅に低下します。それをSQLFiddleに入れて、実行プランがRIDルックアップを使用していることを確認したとき、私は驚きましたが、少し混乱しました...

異なるクラスター化インデックスを使用したテストでは、このクエリや他のクエリのパフォーマンスがわずかに低下したため、クラスター化インデックスはありません。それは私が他の列を追加し始める前Detailsでした、そして私はそれをもっと実験することができます、しかし私がランダムなインデックスで暗闇の中で撮影を始める前に今何が起こっているのかを理解したいと思います。

4

3 に答える 3

2

Detailsメインインデックスを変更して列を含めるとどうなりますか?

使用する場合:

CREATE NONCLUSTERED INDEX [IX_SupportContacts_ForeignIdAsc_IdDesc] 
ON SupportContacts ([ForeignId] ASC, [Id] DESC)
INCLUDE (Details);

その場合、クエリはインデックス自体からのみ満たすことができるため、RIDルックアップもテーブルスキャンも必要ありません。

于 2012-12-08T08:34:51.610 に答える
1

クエリプランの違いは、存在するインデックスのタイプと、さまざまな環境にあるそれらのテーブルのデータの統計に依存します。

オプティマイザーは、統計(主にデータ頻度のヒストグラム)と使用可能なインデックスを使用して、どの実行プランが最も高速になるかを決定します。

したがって、たとえば、[詳細]列を含めるとパフォーマンスが低下することに気づきました。これは、「詳細」列がインデックスの一部ではないか、インデックスの一部である場合、インデックスアクセスが同等(またはほぼ同等)になるように、その列のデータがほとんど一意であることを示すほぼ確実な兆候です。テーブルスキャンに。

多くの場合、この状況が発生すると、オプティマイザーはインデックスアクセスよりもテーブルスキャンを選択します。これは、ブロック読み取りなどを利用して、インデックスのフラグメント化された読み取りよりも高速にテーブルレコードにアクセスできるためです。

オプティマイザーによって選択されるパスに影響を与えるには、インデックスアクセスをより効率的にするために追加/変更できる可能性のあるインデックスを調べる必要がありますが、他のクエリにも悪影響を与える可能性があるため、注意して行う必要があります。インサートのパフォーマンスを低下させる可能性があります。

オプティマイザーを支援するために実行できるその他の重要なアクティビティは、テーブル統計が最新の状態に保たれ、テーブルデータの度数分布の変化率に適した頻度で更新されるようにすることです。

于 2012-12-08T00:03:52.113 に答える
1

関連するインデックス+RIDを使用してクエリを実行した場合、行の99%が省略されることが事実である場合、本番環境で最も可能性の高い問題は、統計が古く、オプティマイザーが( 1,2,3,5)は、結果セットを合計データの1%に制限します。

Pinal Daveの統計について詳しく知るための良いリンクは、次のとおりです。http: //blog.sqlauthority.com/2010/01/25/sql-server-find-statistics-update-date-update-statistics/

統計を更新せずにオプティマイザーに正しいパスをたどるように強制する場合は、テーブルヒントを使用できます。プランで使用する必要のある、ID列とForeignID列を含むインデックスがわかっている場合は、それをヒントとしてクエリに貼り付けます。 SQLオプティマイザーにインデックスの使用を強制します。

http://msdn.microsoft.com/en-us/library/ms187373.aspx

参考までに、2番目のクエリで最高のパフォーマンスが必要な場合は、このインデックスを使用して、発生している頭痛の種を完全に回避してください。

create index ix1 on SupportContacts(ForeignID, Id DESC) include (Details);
于 2012-12-08T20:56:08.447 に答える