MS SQL Serverで1つまたは複数の列にインデックスを作成するとき(バージョン2005を使用しています)、各列のインデックスを昇順または降順のどちらかに指定できます。この選択がここにある理由を理解するのに苦労しています。バイナリソート技術を使用すると、検索はどちらの方法でも同じくらい高速ではないでしょうか?どの順序を選択すると、どのような違いが生じますか?
これは、主に複合インデックスとともに使用する場合に重要です。
_CREATE INDEX ix_index ON mytable (col1, col2 DESC);
_
次のいずれかに使用できます。
_SELECT *
FROM mytable
ORDER BY
col1, col2 DESC
_
または:
_SELECT *
FROM mytable
ORDER BY
col1 DESC, col2
_
、ただし:
_SELECT *
FROM mytable
ORDER BY
col1, col2
_
単一の列のインデックスは、両方の方法でソートに効率的に使用できます。
詳細については、私のブログの記事を参照してください。
更新:
実際、これは単一の列インデックスでも問題になりますが、それほど明白ではありません。
クラスター化されたテーブルの列のインデックスを想像してください。
_CREATE TABLE mytable (
pk INT NOT NULL PRIMARY KEY,
col1 INT NOT NULL
)
CREATE INDEX ix_mytable_col1 ON mytable (col1)
_
_col1
_のインデックスは、行への参照とともに_col1
_の順序付けられた値を保持します。
テーブルはクラスター化されているため、行への参照は実際にはpk
の値です。また、_col1
_の各値内で順序付けられます。
これは、インデックスのリーフが実際に_(col1, pk)
_で順序付けられることを意味し、このクエリは次のとおりです。
_SELECT col1, pk
FROM mytable
ORDER BY
col1, pk
_
ソートする必要はありません。
次のようにインデックスを作成する場合:
_CREATE INDEX ix_mytable_col1_desc ON mytable (col1 DESC)
_
、その後_col1
_の値は降順でソートされますが、_col1
_の各値内のpk
の値は昇順でソートされます。
これは、次のクエリを意味します。
_SELECT col1, pk
FROM mytable
ORDER BY
col1, pk DESC
_
_ix_mytable_col1_desc
_では提供できますが、_ix_mytable_col1
_では提供できません。
つまり、テーブルの_CLUSTERED INDEX
_を構成する列は、常にそのテーブルの他のインデックスの末尾の列です。
真の単一列インデックスの場合、クエリオプティマイザーの観点とほとんど違いはありません。
テーブル定義用
CREATE TABLE T1( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] ASC))
クエリ
SELECT TOP 10 *
FROM T1
ORDER BY ID DESC
実行計画に見られるように、スキャン方向BACKWARD
の順序付きスキャンを使用します。ただし、現時点では、FORWARD
スキャンのみを並列化できるという点でわずかな違いがあります。
ただし、論理的な断片化に関して大きな違いが生じる可能性があります。インデックスは降順のキーで作成されますが、新しい行に昇順のキー値が追加されると、すべてのページが論理的な順序から外れてしまう可能性があります。これは、テーブルをスキャンするときにキャッシュにないIO読み取りのサイズに重大な影響を与える可能性があります。
断片化の結果を見る
avg_fragmentation avg_fragment
name page_count _in_percent fragment_count _size_in_pages
------ ------------ ------------------- ---------------- ---------------
T1 1000 0.4 5 200
T2 1000 99.9 1000 1
以下のスクリプト用
/*Uses T1 definition from above*/
SET NOCOUNT ON;
CREATE TABLE T2( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
BEGIN TRAN
GO
INSERT INTO T1 DEFAULT VALUES
GO 1000
INSERT INTO T2 DEFAULT VALUES
GO 1000
COMMIT
SELECT object_name(object_id) AS name,
page_count,
avg_fragmentation_in_percent,
fragment_count,
avg_fragment_size_in_pages
FROM
sys.dm_db_index_physical_stats(db_id(), object_id('T1'), 1, NULL, 'DETAILED')
WHERE index_level = 0
UNION ALL
SELECT object_name(object_id) AS name,
page_count,
avg_fragmentation_in_percent,
fragment_count,
avg_fragment_size_in_pages
FROM
sys.dm_db_index_physical_stats(db_id(), object_id('T2'), 1, NULL, 'DETAILED')
WHERE index_level = 0
空間結果タブを使用して、両方のケースで後のページにキー値が昇順であるためであるという仮定を検証することができます。
SELECT page_id,
[ID],
geometry::Point(page_id, [ID], 0).STBuffer(4)
FROM T1
CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
UNION ALL
SELECT page_id,
[ID],
geometry::Point(page_id, [ID], 0).STBuffer(4)
FROM T2
CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
ソート順は、個々のレコードではなく、大量のソートされたデータを取得する場合に重要です。
(質問で提案しているように)ソート順は通常、インデックスを作成する列よりもはるかに重要ではありません(順序が希望と逆の場合、システムはインデックスを逆に読み取ることができます)。インデックスのソート順を考えることはめったにありませんが、インデックスの対象となる列に苦労します。
@Quassnoiは、does問題の場合の 素晴らしい例 を提供します。