web-dev-qa-db-ja.com

SQL Server 2008:日時による順序付けが遅すぎる

私のテーブル(SQL Server 2008)には100万以上のレコードがあり、日時でレコードを並べ替えようとすると1秒かかりますが、ID(int)で並べ替えると約0.1秒しかかかりません。

効率を上げる方法はありますか? (私はすでに日時列をインデックスに追加しました)

23
silent

idによる順序付けは、おそらくクラスター化インデックススキャンを使用しますが、datetimeによる順序付けは、並べ替えまたはインデックスルックアップのいずれかを使用します。

これらの方法はどちらも、クラスター化インデックススキャンよりも低速です。

テーブルがidでクラスター化されている場合、基本的にはすでにソートされていることを意味します。レコードは、ページをidの順序でリンクするリンクリストを持つB+Treeに含まれています。エンジンは、リンクリストをトラバースして、idで並べ替えられたレコードを取得する必要があります。

idsが順番に挿入された場合、これは、行の物理的な順序が論理的な順序と一致し、クラスター化インデックスのスキャンがさらに高速になることを意味します。

レコードをdatetimeで並べ替える場合は、次の2つのオプションがあります。

  • テーブルからすべてのレコードを取得し、それらを並べ替えます。遅さは明らかです。
  • datetimeのインデックスを使用します。インデックスはディスクの別のスペースに保存されます。つまり、エンジンはネストされたループでインデックスページとテーブルページの間を行き来する必要があります。それももっと遅いです。

順序を改善するために、datetimeに個別のカバーインデックスを作成できます。

CREATE INDEX ix_mytable_datetime ON mytable (datetime) INCLUDE (field1, field2, …)

、クエリで使用するすべての列をそのインデックスに含めます。

このインデックスはテーブルのシャドウコピーに似ていますが、データは異なる順序で並べ替えられています。

これにより、キールックアップを取り除くことができ(インデックスにはすべてのデータが含まれているため)、datetimeによる順序付けがidの順序付けと同じくらい速くなります。

更新:

この問題に関する新しいブログ投稿:

26
Quassnoi

ORDER BYを尊重するには、エンジンには2つの選択肢があります。

  • 要求された順序を提供するインデックスを使用して行をスキャンします
  • 行を並べ替える

最初のオプションは高速で、2番目のオプションは低速です。問題は、使用するために、インデックスがcoveringインデックスでなければならないことです。つまり、SELECTプロジェクションリストのすべての列と、WHERE句で使用されるすべての列が含まれます(少なくとも)。インデックスがカバーしていない場合、エンジンは、必要な列の値を取得するために、各行のクラスター化インデックス(つまり「テーブル」)を検索する必要があります。この値の一定のルックアップはコストがかかり、エンジンが(当然のことながら)クラスター化インデックスをスキャンして結果をソートする方が効率的であると判断する転換点があり、事実上、非クラスター化インデックスは無視されます。詳細については、 転換点クエリの回答 を参照してください。

次の3つのクエリについて考えてみます。

_SELECT dateColumn FROM table ORDER BY dateColumn
SELECT * FROM table ORDER BY dateColumn
SELECT someColumn FROM table ORDER BY dateColumn
_

1つ目は、dateColumnで非クラスター化インデックスを使用することです。ただし、2つ目は、dateColumnのインデックスを使用せず、100万行の代わりにスキャンと並べ替えを選択する可能性があります。一方、3番目のクエリは、Table(dateColumn) INCLUDE (someColumn)のインデックスの恩恵を受けることができます。

このトピックは、MSDNで広く取り上げられています。 インデックスデザインの基本一般的なインデックスデザインガイドライン非クラスター化インデックスデザインガイドライン または ハウツー)を参照してください。 :SQLインデックスの最適化

最終的に、テーブルデザインの最も重要な選択は、使用するクラスター化インデックスです。ほとんどの場合、主キー(通常は自動インクリメントされたID)はクラスター化インデックスとして残されます。これは、特定のOLTPロードのみに役立つ決定です。

そして最後に、かなり明白な質問です。なぜ世界で100万行を注文するのでしょうか。表示できないかもしれませんね。ユースケースについてもう少し説明すると、より良い答えを見つけるのに役立つ場合があります。

6
Remus Rusanu

日時を新しいインデックスに追加しますが、IDに追加してもあまり役に立ちません。

2
Mark Dickinson

Int列にはインデックスがありますが、datetime列にはインデックスがない可能性がありますか?実行計画を見てください。

1
Nestor

datatimeをintとして格納する場合でも、データを格納または取得するたびに変換に時間がかかる場合があります。 (IPアドレスなどのスタッフを保存し、シーク時間を短縮するために使用される一般的な手法)

サーバーが日時をどのように格納するかをサーバーにチェックインする必要があります。サーバーがすでにintまたはbigintとして格納している場合は、何も変更されません。

0
Dani

日時フィールドに多数の個別の値が含まれていて、それらの値がほとんど変更されない場合は、日時フィールドにクラスター化インデックスを定義します。これにより、実際のデータが日時値で並べ替えられます。クラスター化インデックスの使用については、 http://msdn.Microsoft.com/en-us/library/aa933131(SQL.80).aspx を参照してください。

ただし、これにより、非クラスター化インデックスの使用に追いやられるため、int検索が遅くなります。

0
badbod99

DateTimeフィールドを「the」インデックスまたは排他的インデックスに追加しましたか?別のフィールドとDateTimeで選択をフィルタリングしますか、それともこれだけでフィルタリングしますか?

パフォーマンスを最適化するには、フィルタリングするすべてのフィールドを含むインデックスが必要です。

0
j.a.estevan