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

Réponses:

62

Je dirais que ce sont des concepts différents mais pas trop différents pour dire «craie et fromage».

  • Une table temporaire est bonne pour la réutilisation ou pour effectuer plusieurs passes de traitement sur un ensemble de données.

  • Un CTE peut être utilisé soit pour récurer, soit simplement pour améliorer la lisibilité.
    Et, comme une vue ou une fonction de table en ligne, la fonction peut également être traitée comme une macro à développer dans la requête principale

  • Une table temporaire est une autre table avec quelques règles autour de la portée

J'ai stocké des procs où j'utilise les deux (et des variables de table aussi)

gbn
la source
12
Les tables temporaires permettent également des index et même des statistiques qui sont parfois nécessaires, contrairement à un CTE.
CodeCowboyOrg
9
Je pense que cette réponse ne met pas suffisamment en évidence le fait que les CTE peuvent conduire à des performances terribles. Je me réfère généralement à cette réponse sur dba.stackexchange. Votre question arrive en deuxième position dans mon moteur de recherche si je cherche, cte vs temporary tablesdonc à mon humble avis, cette réponse doit mettre en évidence les inconvénients de mieux CTE. TL; DR de la réponse liée: un CTE ne doit jamais être utilisé pour la performance. . Je suis d'accord avec cette citation car j'ai expérimenté les inconvénients de CTE.
TT.
2
@TT. Intéressant. Je trouve que les CTE fonctionnent beaucoup mieux
Squ1rr3lz
198

Ça 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 valeurs de table en ligne. Notez que tandis que BOL dit 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. En outre, 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

Plan 1

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 ici en matérialisant le CTE en une table temporaire intermédiaire serait massivement contre-productif.

Matérialiser 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, alors il y a encore la surcharge de la sélection à partir de celle-ci.

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 des 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.

entrez la description de l'image ici

Ce serait un bon candidat pour matérialiser le résultat intermédiaire. La réécriture de la table temporaire équivalente 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 

Avec plan

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 tirant parti des statistiques sur le résultat matérialisé. Un exemple de cette approche se trouve dans l'article SQL Cat When To Break Down Complex Queries .

Dans certaines circonstances, SQL Server utilisera un spool pour mettre en cache un résultat intermédiaire, par exemple un CTE, et évitera d'avoir à réévaluer cette sous-arborescence. Ceci est abordé dans l'élément Connect (migré). Fournissez 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 mises en file d'attente devait être très différent de l'estimation, il n'est pas possible pour le plan d'exécution en cours de s'adapter dynamiquement en réponse (du moins dans les versions actuelles. Des plans de requête adaptatifs peuvent devenir possibles dans l'avenir).

Martin Smith
la source
33
C'est la seule réponse qui répond à la question réelle (qui demande ce qui a la meilleure performance et non quelle est la différence ou quelle est votre préférée), et elle répond correctement à cette question: "Cela dépend" est la bonne réponse. C'est également la seule réponse avec des données à l'appui pour expliquer, plusieurs autres (avec un nombre élevé de votes) affirment clairement que l'un est meilleur que l'autre sans références ni preuves ... Pour être clair, toutes ces réponses sont également fausses . Parce que "ça dépend"
Arkaine55
2
C'est aussi une réponse bien écrite et bien référencée. Sérieusement de premier ordre.
Dan Williams
50

CTE a ses utilisations - lorsque les données dans le CTE sont petites et qu'il y a une forte amélioration de la lisibilité comme dans le cas des tables récursives. Cependant, ses performances ne sont certainement pas meilleures que celles des variables de table et lorsqu'il s'agit de tables très volumineuses, les tables temporaires surpassent considérablement le CTE. Cela est dû au fait que vous ne pouvez pas définir d'index sur un CTE et lorsque vous avez une grande quantité de données qui nécessite de se joindre à une autre table (CTE est simplement comme une macro). Si vous joignez plusieurs tables avec des millions de lignes d'enregistrements dans chacune, CTE fonctionnera nettement moins bien que les tables temporaires.

CSW
la source
9
J'ai vu cela de ma propre expérience. Les CTE fonctionnent beaucoup plus lentement.
goku_da_master
7
Les CTE fonctionnent également plus lentement car les résultats ne sont pas mis en cache. Ainsi, chaque fois que vous utilisez le CTE, il réexécute la requête, le plan et tout.
goku_da_master
1
Et le moteur de base de données peut choisir de réexécuter la requête non seulement pour chaque référence, mais pour chaque ligne de la requête consommateur, en tant que sous-requête corrélée ... vous devez toujours faire attention à cela si ce n'est pas souhaité.
Mike M du
La table temporaire est stockée dans tempdb sur SQL Server, qui est un disque mais présente l'avantage d'être indexée et l'optimiseur SQL fonctionne bien sur les requêtes de sélection dans ce cas. Vous ne savez pas sur quelle base de données ou zone de disque le CTE est stocké (quand il dépasse la taille de la mémoire et est mis en file d'attente pour la pagination d'E / S), mais il n'est jamais optimisé avec le grand volume de données. J'ai parfois utilisé l'option du compilateur (avec recompilation) pour le rendre plus rapide
rmehra76
33

Les tables temporaires sont toujours sur le disque - donc tant que votre CTE peut être conservé en mémoire, il sera probablement plus rapide (comme une variable de table aussi).

Mais là encore, si la charge de données de votre CTE (ou variable de table temporaire) devient trop importante, elle sera également stockée sur le disque, donc il n'y a pas de gros avantage.

En général, je préfère un CTE à une table temporaire car il est parti après que je l'ai utilisé. Je n'ai pas besoin de penser à le laisser explicitement ou quoi que ce soit.

Donc, pas de réponse claire à la fin, mais personnellement, je préférerais CTE aux tables temporaires.

marc_s
la source
2
Dans le cas de SQLite et PostgreSQL, les tables temporaires sont automatiquement supprimées (généralement à la fin d'une session). Cependant, je ne connais pas les autres SGBD.
Serrano
1
CTE est comme une vue temporaire. Les données AFAIK ne sont pas stockées, donc rien ne peut être conservé en mémoire ou stocké sur disque. Remarque importante, chaque fois que vous utilisez le CTE, la requête s'exécute à nouveau.
Rob
1
Personnellement, je n'ai jamais vu un CTE fonctionner mieux qu'une table Temp pour la vitesse. Et bien le débogage est beaucoup plus facile avec la table temporaire
Mark Monforti
7

Ainsi, la requête qui m'a été assignée pour optimiser a été écrite avec deux CTE dans le serveur SQL. Cela prenait 28 secondes.

J'ai passé deux minutes à les convertir en tables temporaires et la requête a pris 3 secondes

J'ai ajouté un index à la table temporaire sur le champ sur lequel il était joint et je l'ai réduit à 2 secondes

Trois minutes de travail et maintenant son fonctionnement 12 fois plus rapide en supprimant CTE. Personnellement, je n'utiliserai pas les CTE car ils sont également plus difficiles à déboguer.

Ce qui est fou, c'est que les CTE n'ont été utilisés qu'une seule fois et que leur mise en place d'un index s'est avérée 50% plus rapide.

Mark Monforti
la source
6

CTE ne prendra aucun espace physique. C'est juste un ensemble de résultats que nous pouvons utiliser join.

Les tables temporaires sont temporaires. Nous pouvons créer des index, des contraintes comme des tables normales pour cela nous devons définir toutes les variables.

Portée de la table temporaire uniquement dans la session. EX: ouvre deux fenêtres de requête SQL

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Exécutez cette requête dans la première fenêtre, puis exécutez la requête ci-dessous dans la deuxième fenêtre, vous pouvez trouver la différence.

select * from #temp
Selvaraj
la source
4
>> "c'est juste un ensemble de résultats que nous pouvons utiliser join." -> Ce n'est pas exact. CTE n'est pas un "jeu de résultats" mais un code en ligne. Le moteur de requête SQL Server analyse le code CTE dans le cadre du texte de la requête et crée un plan d'exécution en fonction. L'idée que CTE est en ligne est le gros avantage de l'utilisation de CTE, car elle permet au serveur de créer un "plan d'exécution combiné"
Ronen Ariely
4

J'ai utilisé les deux, mais dans des procédures complexes massives, j'ai toujours trouvé les tables temporaires plus faciles à utiliser et plus méthodiques. Les CTE ont leurs utilisations mais généralement avec de petites données.

Par exemple, j'ai créé des sprocs qui reviennent avec des résultats de gros calculs en 15 secondes, mais convertissent ce code pour qu'il s'exécute dans un CTE et je l'ai vu s'exécuter plus de 8 minutes pour obtenir les mêmes résultats.

Andy_RC
la source
3

En retard à la fête, mais ...

L'environnement dans lequel je travaille est très contraint, prenant en charge certains produits de fournisseurs et fournissant des services à «valeur ajoutée» comme le reporting. En raison des limites de la politique et du contrat, je n'ai généralement pas le luxe de disposer d'un espace table / données séparé et / ou de créer du code permanent [cela s'améliore un peu, selon l'application].

IOW, je ne peux pas généralement développer une procédure stockée ou des UDF ou des tables temporaires, etc. Je dois pratiquement tout faire via l'interface de mon application (Crystal Reports - ajouter / lier des tables, définir les clauses where de w / in CR, etc. ). Une petite grâce salvatrice est que Crystal me permet d'utiliser les COMMANDES (ainsi que les expressions SQL). Certaines choses qui ne sont pas efficaces grâce à la fonctionnalité régulière d'ajout / liaison de tables peuvent être effectuées en définissant une commande SQL. J'utilise les CTE à travers cela et j'ai obtenu de très bons résultats "à distance". Les CTE aident également à la maintenance des rapports, ne nécessitant pas que le code soit développé, remis à un DBA pour compiler, crypter, transférer, installer, puis exiger des tests à plusieurs niveaux. Je peux faire des CTE via l'interface locale.

L'inconvénient de l'utilisation des CTE avec CR est que chaque rapport est séparé. Chaque CTE doit être maintenu pour chaque rapport. Là où je peux faire des SP et des UDF, je peux développer quelque chose qui peut être utilisé par plusieurs rapports, ne nécessitant qu'un lien vers le SP et en passant des paramètres comme si vous travailliez sur une table normale. CR n'est pas vraiment bon pour gérer les paramètres dans les commandes SQL, donc cet aspect de l'aspect CR / CTE peut faire défaut. Dans ces cas, j'essaie généralement de définir le CTE pour renvoyer suffisamment de données (mais pas TOUTES les données), puis j'utilise les capacités de sélection d'enregistrements dans CR pour trancher et couper cela.

Donc ... mon vote est pour les CTE (jusqu'à ce que j'obtienne mon espace de données).

Marc
la source
3

Une utilisation dans laquelle j'ai trouvé les excellentes performances de CTE était celle où je devais joindre une requête relativement complexe à quelques tables de quelques millions de lignes chacune.

J'ai utilisé le CTE pour sélectionner d'abord le sous-ensemble basé sur les colonnes indexées afin de réduire d'abord ces tables à quelques milliers de lignes pertinentes chacune, puis j'ai joint le CTE à ma requête principale. Cela a réduit de façon exponentielle le temps d'exécution de ma requête.

Alors que les résultats pour le CTE ne sont pas mis en cache et que les variables de table auraient pu être un meilleur choix, je voulais vraiment les essayer et j'ai trouvé le scénario ci-dessus.

acheteur
la source
De plus, je pense que puisque je n'utilise que le CTE dans la jointure, je n'exécute vraiment le CTE qu'une seule fois dans ma requête, donc la mise en cache des résultats n'était pas un si gros problème à cet égard
achat
1

C'est une question vraiment ouverte, et tout dépend de la façon dont elle est utilisée et du type de table temporaire (variable de table ou table traditionnelle).

Une table temporaire traditionnelle stocke les données dans la base de données temporaire, ce qui ralentit les tables temporaires; mais pas les variables de table.

JoshBerke
la source
1

Je viens de tester cela - à la fois CTE et non-CTE (où la requête a été tapée pour chaque instance d'union) a pris tous les deux ~ 31 secondes. CTE a rendu le code beaucoup plus lisible - le réduire de 241 à 130 lignes, ce qui est très agréable. La table de température, d'autre part, l'a réduite à 132 lignes et a pris CINQ SECONDES pour fonctionner. Sans blague. tous ces tests ont été mis en cache - les requêtes ont toutes été exécutées plusieurs fois auparavant.

user2989981
la source
1

De mon expérience dans SQL Server, j'ai trouvé l'un des scénarios où CTE surperformait la table Temp

J'avais besoin d'utiliser un DataSet (~ 100000) à partir d'une requête complexe une seule fois dans ma procédure stockée.

  • La table temporaire provoquait une surcharge sur SQL où ma procédure s'exécutait lentement (car les tables temporaires sont de vraies tables matérialisées qui existent dans tempdb et persister pendant la durée de ma procédure actuelle)

  • D'autre part, avec CTE, CTE persiste uniquement jusqu'à ce que la requête suivante soit exécutée. Ainsi, CTE est une structure en mémoire pratique avec une portée limitée. Les CTE n'utilisent pas tempdb par défaut.

Il s'agit d'un scénario dans lequel les CTE peuvent vraiment vous aider à simplifier votre code et à surpasser la table temporaire. J'avais utilisé 2 CTE, quelque chose comme

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO
Amardeep Kohli
la source
1
Votre réponse semble être très générique ... Comment mesurez-vous que "CTE a surpassé la table de température"? Avez-vous des mesures de temps? À mon avis, vous devriez modifier votre réponse et ajouter plus de détails.
Il Vic
Oui, j'ai des mesures de temps et un plan d'exécution pour soutenir ma déclaration.
Amardeep Kohli
Impossible d'ajouter l'img pour le plan d'exécution en raison de privilèges limités.Mettre à jour les détails une fois qu'il sera résolu
Amardeep Kohli