Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Quelles sont les tables les plus performantes, CTE ou temporaires ?

Cela dépend.

Tout d'abord

Qu'est-ce qu'une expression de table commune ?

Un CTE (non récursif) est traité de manière très similaire aux autres constructions qui peuvent également être utilisées comme expressions de table en ligne dans SQL Server. Tables dérivées, vues et fonctions de table en ligne. Notez que bien que BOL indique qu'un CTE "peut être considéré comme un ensemble de résultats temporaire", il s'agit d'une description purement logique. Le plus souvent, il n'est pas matérialisé à part entière.

Qu'est-ce qu'une table temporaire ?

Il s'agit d'une collection de lignes stockées sur des pages de données dans tempdb. Les pages de données peuvent résider partiellement ou entièrement en mémoire. De plus, la table temporaire peut être indexée et avoir des statistiques de colonne.

Données de test

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Exemple 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

Remarquez dans le plan ci-dessus qu'il n'y a aucune mention de CTE1. Il accède simplement aux tables de base directement et est traité de la même manière que

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Réécrire en matérialisant le CTE dans une table temporaire intermédiaire ici serait massivement contre-productif.

Matérialisation de la définition CTE de

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Cela impliquerait de copier environ 8 Go de données dans une table temporaire, il y a toujours la surcharge de la sélection également.

Exemple 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

L'exemple ci-dessus prend environ 4 minutes sur ma machine.

Seules 15 lignes sur les 1 000 000 de valeurs générées aléatoirement correspondent au prédicat, mais l'analyse coûteuse de la table se produit 16 fois pour les localiser.

Ce serait un bon candidat pour matérialiser le résultat intermédiaire. La réécriture équivalente de la table temporaire a pris 25 secondes.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

La matérialisation intermédiaire d'une partie d'une requête dans une table temporaire peut parfois être utile même si elle n'est évaluée qu'une seule fois - lorsqu'elle permet de recompiler le reste de la requête en profitant des statistiques sur le résultat matérialisé. Un exemple de cette approche se trouve dans l'article SQL Cat Quand décomposer les requêtes complexes.

Dans certaines circonstances, SQL Server utilisera un spool pour mettre en cache un résultat intermédiaire, par ex. d'un CTE, et éviter d'avoir à réévaluer ce sous-arbre. Ceci est abordé dans l'élément Connect (migré) Fournir un indice pour forcer la matérialisation intermédiaire des CTE ou des tables dérivées. Cependant, aucune statistique n'est créée à ce sujet et même si le nombre de lignes spoolées devait être très différent de l'estimation, il n'est pas possible que le plan d'exécution en cours s'adapte dynamiquement en réponse (au moins dans les versions actuelles. Les plans de requête adaptatifs peuvent devenir possibles dans l'avenir).