web-dev-qa-db-ja.com

SQL Serverインデックス-昇順または降順、どのような違いがありますか?

MS SQL Serverで1つまたは複数の列にインデックスを作成するとき(バージョン2005を使用しています)、各列のインデックスを昇順または降順のどちらかに指定できます。この選択がここにある理由を理解するのに苦労しています。バイナリソート技術を使用すると、検索はどちらの方法でも同じくらい高速ではないでしょうか?どの順序を選択すると、どのような違いが生じますか?

127
Joshua Carmody

これは、主に複合インデックスとともに使用する場合に重要です。

_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_を構成する列は、常にそのテーブルの他のインデックスの末尾の列です。

126
Quassnoi

真の単一列インデックスの場合、クエリオプティマイザーの観点とほとんど違いはありません。

テーブル定義用

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スキャンのみを並列化できるという点でわずかな違いがあります。

Plan

ただし、論理的な断片化に関して大きな違いが生じる可能性があります。インデックスは降順のキーで作成されますが、新しい行に昇順のキー値が追加されると、すべてのページが論理的な順序から外れてしまう可能性があります。これは、テーブルをスキャンするときにキャッシュにない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 %% )

enter image description here

67
Martin Smith

ソート順は、個々のレコードではなく、大量のソートされたデータを取得する場合に重要です。

(質問で提案しているように)ソート順は通常、インデックスを作成する列よりもはるかに重要ではありません(順序が希望と逆の場合、システムはインデックスを逆に読み取ることができます)。インデックスのソート順を考えることはめったにありませんが、インデックスの対象となる列に苦労します。

@Quassnoiは、does問題の場合の 素晴らしい例 を提供します。

8
Michael Haren