web-dev-qa-db-fra.com

Index SQL Server - croissant ou décroissant, quelle différence cela fait-il?

Lorsque vous créez un index sur une colonne ou un nombre de colonnes dans MS SQL Server (j'utilise la version 2005), vous pouvez spécifier que l'index de chaque colonne soit croissant ou décroissant. J'ai du mal à comprendre pourquoi ce choix est même ici. En utilisant des techniques de tri binaire, une recherche ne serait-elle pas aussi rapide dans les deux cas? Quelle différence cela fait-il quelle commande je choisis?

127
Joshua Carmody

Cela importe surtout lorsqu'il est utilisé avec des index composites:

CREATE INDEX ix_index ON mytable (col1, col2 DESC);

peut être utilisé pour:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2 DESC

ou:

SELECT  *
FROM    mytable
ORDER BY
        col1 DESC, col2

, mais pas pour:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2

Un index sur une seule colonne peut être efficacement utilisé pour trier dans les deux sens.

Voir l'article sur mon blog pour plus de détails:

Mise à jour:

En fait, cela peut avoir de l'importance même pour un index de colonne unique, bien que ce ne soit pas si évident.

Imaginez un index sur une colonne d'une table en cluster:

CREATE TABLE mytable (
       pk INT NOT NULL PRIMARY KEY,
       col1 INT NOT NULL
)
CREATE INDEX ix_mytable_col1 ON mytable (col1)

L'index sur col1 conserve les valeurs ordonnées de col1 avec les références aux lignes.

Puisque la table est en cluster, les références aux lignes sont en réalité les valeurs de pk. Ils sont également classés dans chaque valeur de col1.

Cela signifie que les feuilles de l'index sont en réalité ordonnées sur (col1, pk), et cette requête:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk

pas besoin de trier.

Si nous créons l'index comme suit:

CREATE INDEX ix_mytable_col1_desc ON mytable (col1 DESC)

, alors les valeurs de col1 sera trié par ordre décroissant, mais les valeurs de pk dans chaque valeur de col1 sera trié par ordre croissant.

Cela signifie que la requête suivante:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk DESC

peut être servi par ix_mytable_col1_desc mais pas par ix_mytable_col1.

En d'autres termes, les colonnes qui constituent un CLUSTERED INDEX sur une table, figurent toujours les colonnes de fin de tout autre index de cette table.

126
Quassnoi

Pour un véritable index de colonne unique, cela ne fait guère de différence du point de vue de l'Optimiseur de requête.

Pour la définition de la table

CREATE TABLE T1( [ID] [int] IDENTITY NOT NULL,
                 [Filler] [char](8000) NULL,
                 PRIMARY KEY CLUSTERED ([ID] ASC))

La requête

SELECT TOP 10 *
FROM T1
ORDER BY ID DESC

Utilise une analyse ordonnée avec le sens d'analyse BACKWARD comme indiqué dans le plan d'exécution. Il y a cependant une légère différence dans le fait qu'actuellement, seuls les scans FORWARD peuvent être parallélisés.

Plan

Cependant cela peut faire une grande différence en termes de fragmentation logique. Si l'index est créé avec des clés descendantes mais que les nouvelles lignes sont ajoutées avec des valeurs de clé croissantes, vous pouvez vous retrouver avec chaque page en dehors de l'ordre logique. Cela peut avoir un impact important sur la taille de la IO lit lors de l'analyse de la table et celle-ci n'est pas dans le cache.

Voir les résultats de la fragmentation

                    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

pour le script ci-dessous

/*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 

Il est possible d'utiliser l'onglet de résultats spatiaux pour vérifier si cela est supposé, car les pages suivantes ont des valeurs de clé croissantes dans les deux cas.

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

L'ordre de tri est important lorsque vous souhaitez extraire un grand nombre de données triées, et non des enregistrements individuels.

Notez que (comme vous le suggérez avec votre question), l'ordre de tri est généralement beaucoup moins important que les colonnes que vous indexez (le système peut lire l'index à l'envers si l'ordre est opposé). Je ne pense que très rarement à l'ordre de tri des index, alors que j'agonise les colonnes couvertes par l'index.

@Quassnoi fournit un bon exemple pour savoir quand est important .

8
Michael Haren