web-dev-qa-db-fra.com

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

Quels sont les plus performants, CTE ou Temporary Tables?

126
Blankman

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

  • Une table temporaire peut être réutilisée ou pour effectuer plusieurs passes de traitement sur un ensemble de données.

  • Un CTE peut être utilisé pour récidiver ou simplement pour améliorer la lisibilité.
    Et, comme une fonction de vue ou de table inline, 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é les procs où j'utilise les deux (et les variables de table aussi)

62
gbn

Ç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 pouvant également être utilisées en tant qu'expressions de table en ligne dans SQL Server. Tables dérivées, vues et fonctions valorisées de tables en ligne. Notez que si BOL dit qu'un CTE "peut être considéré comme un ensemble de résultats temporaire", il s'agit d'une description purement logique. Plus souvent qu'autrement, il n'est pas matérialisé en soi.

Qu'est-ce qu'une table temporaire?

Il s'agit d'un ensemble 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

Plan 1

Remarquez dans le plan ci-dessus qu'il n'y a aucune mention de CTE1. Il accède directement aux tables de base 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 serait extrêmement contre-productif.

Matérialiser la définition du 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, mais il y aurait toujours la surcharge de la sélectionner aussi.

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.

Seulement 15 lignes sur un million de valeurs générées de manière aléatoire correspondent au prédicat, mais l'analyse coûteuse de la table coûteuse se produit 16 fois pour les localiser.

enter image description here

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 

With Plan

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

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

162
Martin Smith

Le CTE a ses utilisations - lorsque les données dans le CTE sont petites et que la lisibilité est nettement améliorée, comme dans le cas des tables récursives. Toutefois, ses performances ne sont certainement pas meilleures que les variables de table et, lorsque l’on traite de très grandes tables, les tables temporaires dépassent considérablement les performances de CTE. En effet, 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 rejoindre une autre table (CTE est simplement comme une macro). Si vous joignez plusieurs tables contenant des millions de lignes d'enregistrements, le CTE sera nettement moins performant que les tables temporaires.

47
CSW

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

Mais là encore, si le chargement de données de votre CTE (ou variable de table temporaire) devient trop important, il sera également stocké sur disque, de sorte que vous ne retirerez aucun avantage important.

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

Donc, pas de réponse claire au final, mais personnellement, je préférerais le CTE aux tables temporaires.

32
marc_s

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, 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
6
selvaraj

Donc, la requête que je devais optimiser a été écrite avec deux CTE sur le serveur SQL. Il prenait 28sec.

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 elle était jointe 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, mais 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 qu'il était toujours 50% plus rapide de les indexer.

5
Mark Monforti

J'ai utilisé les deux, mais dans les procédures complexes massives, j'ai toujours trouvé que les tables temporaires étaient meilleures 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 renvoient des résultats de calculs volumineux en 15 secondes, mais convertissent ce code pour qu'il s'exécute dans un CTE et le voient fonctionner plus de 8 minutes pour obtenir les mêmes résultats.

4
Andy_RC

Pour ce qui est des performances excellentes de CTE, j’ai trouvé un moyen d’utiliser une requête relativement complexe sur plusieurs tables de quelques millions de lignes.

J'ai utilisé le CTE pour sélectionner d’abord le sous-ensemble basé sur les colonnes indexées pour couper d’abord ces tables à quelques milliers de lignes pertinentes, puis joigner le CTE à ma requête principale. Cela a réduit de manière exponentielle le temps d'exécution de ma requête.

Bien que les résultats pour le CTE ne soient pas mis en cache et que les variables de table aient pu être un meilleur choix, je voulais simplement les essayer et trouver la correspondance avec le scénario ci-dessus.

3
purchas

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

L’environnement dans lequel je travaille est soumis à de fortes contraintes: il prend en charge certains produits de fournisseurs et fournit des services à "valeur ajoutée", tels que les rapports. En raison des limites imposées par la politique et les contrats, le luxe de disposer d'un espace table/données séparé et/ou la possibilité de créer un code permanent ne me permet généralement pas.

IOW, I ne peut pas généralement développer une procédure stockée, des fichiers UDF ou des tables temporaires, etc. w/en CR, etc.). Un petit avantage, c’est que Crystal me permet d’utiliser COMMANDS (ainsi que des expressions SQL). Certaines choses qui ne sont pas efficaces avec la fonctionnalité régulière de tables d'ajout/lien 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, sans exiger que le code soit développé, transmis à un administrateur de base de données pour compiler, chiffrer, transférer, installer, puis exiger des tests à plusieurs niveaux. Je peux faire des CTE via l'interface locale.

L'inconvénient de l'utilisation de 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, nécessitant uniquement une liaison avec le SP) et en transmettant les paramètres comme si vous travailliez sur une table normale. CR est pas vraiment doué pour gérer les paramètres dans les commandes SQL, de sorte que cet aspect de l'aspect CR/CTE peut être insuffisant. 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 utiliser l'enregistrement capacités de sélection en CR pour découper cela en dés.

Donc ... mon vote est pour les CTE (jusqu'à ce que je reçoive mon espace de données).

3
Marc

C'est une question très ouverte, et tout dépend de la manière dont il est utilisé 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; Cependant, les variables de table ne le font pas.

1
JoshBerke

D'après mon expérience avec SQL Server, j'ai trouvé l'un des scénarios où CTE surperformait la table Temp

J'avais besoin d'utiliser un ensemble de données (~ 100 000) à partir d'une requête complexe une seule fois dans ma procédure stockée.

  • La table temporaire entraînait une surcharge sur SQL où ma procédure fonctionnait lentement (car les tables temporaires sont de véritables tables matérialisées existant dans tempdb et Persist pour la durée de ma procédure actuelle)

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

C'est un scénario dans lequel les CTE peuvent réellement aider à simplifier votre code et Outperform Temp Table. 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
1
Amardeep Kohli

Je viens de tester ceci: les CTE et les non-CTE (où la requête était tapée pour chaque instance d'union) prenaient tous les deux environ 31 secondes. CTE a rendu le code beaucoup plus lisible, le coupant de 241 à 130 lignes, ce qui est très agréable. Le tableau Temp, d’autre part, a réduit le nombre de lignes à 132 et pris cinq secondes à courir. Sans blague. tous ces tests étaient mis en cache - les requêtes avaient toutes été exécutées plusieurs fois auparavant.

1
user2989981