Quelle est la différence entre un CTE et une table temporaire?

174

Quelle est la différence entre une expression de table commune (CTE) et une table temporaire? Et quand devrais-je utiliser l'un sur l'autre?

CTE

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

Table temporaire

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
Rachel
la source

Réponses:

200

C'est assez large, mais je vais vous donner une réponse aussi générale que possible.

CTE ...

  • Sont non indexables (mais peuvent utiliser des index existants sur des objets référencés)
  • Impossible d'avoir des contraintes
  • Sont essentiellement des VIEWs jetables
  • Ne persiste que jusqu'à la prochaine requête
  • Peut être récursif
  • Ne pas avoir de statistiques dédiées (utilisez des statistiques sur les objets sous-jacents)

#Temp Tables ...

  • Sont des tables matérialisées réelles qui existent dans tempdb
  • Peut être indexé
  • Peut avoir des contraintes
  • Persister pour la vie de la connexion actuelle
  • Peut être référencé par d'autres requêtes ou sous-procédures
  • Avoir des statistiques dédiées générées par le moteur

En ce qui concerne le moment d'utiliser chacun, ils ont des cas d'utilisation très différents. Si vous avez un jeu de résultats très volumineux ou si vous avez besoin de vous y référer plusieurs fois, placez-le dans un #temptableau. Si elle doit être récursive, jetable ou simplement pour simplifier quelque chose de manière logique, l'option CTEest préférable.

En outre, a neCTE devrait jamais être utilisé pour la performance . Vous n'allez presque jamais accélérer les choses en utilisant un CTE, parce que, encore une fois, ce n'est qu'une vue jetable. Vous pouvez faire des choses intéressantes avec eux, mais accélérer une requête n'en fait pas partie.

JNK
la source
Accélérer une grosse fusion en utilisant CTE est une chose
AgentFire
1
Accélérer de nombreuses requêtes à l'aide de CTE est également une chose car avec les CTE, vous pouvez ajouter vos propres connaissances métier pour surpasser l'optimiseur de requêtes. Par exemple, vous pouvez choisir dans la partie 1 de votre CTE parmi des tables dans lesquelles vous savez que les lignes obtenues seront très petites. Dans la même requête, vous pouvez joindre ce petit ensemble de résultats à un ensemble de résultats plus important et contourner complètement les problèmes causés par des statistiques obsolètes, etc. Pour cela, vous devez ajouter des indicateurs de requête pour forcer la commande. Cela fonctionne, cela améliore les performances.
Dave Hilditch
"ne jamais être utilisé pour la performance" est une déclaration large et quelque peu subjective, même si je comprends votre point de vue. Bien que, en plus des autres commentaires, l’utilisation d’un CTE puisse générer un gain de performance supplémentaire lors du passage à un CTE récursif à partir d’une autre forme de récursivité, telle que des appels de procédure récursifs ou un curseur.
JD
29

MODIFIER:

S'il vous plaît voir les commentaires de Martin ci-dessous:

Le CTE n'est pas matérialisé sous la forme d'une table en mémoire. C'est juste une façon d'encapsuler une définition de requête. Dans le cas du PO, ce sera en ligne et comme si vous veniez de le faire SELECT Column1, Column2, Column3 FROM SomeTable. La plupart du temps, ils ne se matérialisent pas à l’avance. C’est pourquoi nous ne renvoyons aucune ligne WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.Xet vérifions également les plans d’exécution. Bien qu'il soit parfois possible de pirater le plan pour obtenir une bobine. Un élément de connexion demande un indice pour cela. - Martin Smith 15 février 12 à 17:08


Réponse originale

CTE

En savoir plus sur MSDN

Un CTE crée la table utilisée en mémoire, mais n'est valide que pour la requête spécifique qui la suit. Lorsque vous utilisez la récursivité, cela peut être une structure efficace.

Vous pouvez également envisager d'utiliser une variable de table. Ceci est utilisé comme une table temporaire et peut être utilisé plusieurs fois sans avoir besoin d'être re-matérialisé pour chaque jointure. En outre, si vous devez conserver quelques enregistrements maintenant, ajoutez quelques enregistrements supplémentaires après la sélection suivante, ajoutez quelques enregistrements supplémentaires après une autre opération, puis renvoyez uniquement ces quelques enregistrements. Cette structure peut donc être pratique. pas besoin d'être abandonné après l'exécution. Surtout juste du sucre syntaxique. Toutefois, si vous maintenez un nombre de lignes faible, il ne se matérialise jamais sur le disque. Voir Quelle est la différence entre une table temporaire et une variable de table dans SQL Server? pour plus de détails.

Table temporaire

En savoir plus sur MSDN - Faites défiler environ 40% du chemin

Une table temporaire est littéralement une table créée sur le disque, mais dans une base de données spécifique pouvant être supprimée par tous. Il incombe à un bon développeur de détruire ces tables lorsqu'elles ne sont plus nécessaires, mais un administrateur de base de données peut également les effacer.

Les tables temporaires sont de deux types: locale et globale. En termes de serveur MS SQL, vous utilisez une #tableNamedésignation pour local et une ##tableNamedésignation pour global (notez l'utilisation d'un simple ou d'un double # comme caractéristique d'identification).

Notez qu'avec les tables temporaires, par opposition aux variables de table ou au CTE, vous pouvez appliquer des index, etc., car il s'agit légitimement de tables au sens normal du terme.


En général, j'utilisais des tables temporaires pour les requêtes plus longues ou plus longues, ainsi que des CTE ou des variables de table si j'avais déjà un petit jeu de données et si je voulais simplement écrire rapidement un peu de code pour quelque chose de petit. L’expérience et les conseils d’autres personnes indiquent que vous devez utiliser des CTE pour lesquels vous ne recevez qu’un petit nombre de lignes. Si vous avez un grand nombre, vous bénéficierez probablement de la possibilité d'indexer sur la table temporaire.

jcolebrand
la source
11
Le CTE n'est pas matérialisé sous la forme d'une table en mémoire. C'est juste une façon d'encapsuler une définition de requête. Dans le cas du PO, ce sera en ligne et identique à ce que nous venons de faireSELECT Column1, Column2, Column3 FROM SomeTable
Martin Smith
4
La plupart du temps, ils ne se matérialisent pas à l’avance. C’est pourquoi nous ne renvoyons aucune ligne WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.Xet vérifions également les plans d’exécution. Bien qu'il soit parfois possible de pirater le plan pour obtenir une bobine. Un élément de connexion demande un indice pour cela.
Martin Smith
16

La réponse acceptée ici dit "un CTE ne doit jamais être utilisé pour la performance" - mais cela pourrait induire en erreur. Dans le contexte des CTE par rapport aux tables temporaires, je viens juste de finir de supprimer une série de fichiers indésirables d'une suite de procédures stockées, car certains doofus ont pensé qu'il n'y avait que peu ou pas de temps système pour utiliser des tables temporaires. J'ai mis le paquet dans les CTE, à l' exception de ceux qui allaient légitimement être réutilisés tout au long du processus. J'ai gagné environ 20% de performance par tous les indicateurs. J'ai ensuite entrepris de supprimer tous les curseurs qui essayaient d'implémenter un traitement récursif. C'est là que j'ai vu le plus grand gain. J'ai fini par réduire les temps de réponse d'un facteur dix.

Les CTE et les tables temporaires ont des cas d'utilisation très différents. Je tiens simplement à souligner que, bien que n'étant pas une panacée, la compréhension et l'utilisation correcte des CTE peuvent conduire à des améliorations réellement remarquables à la fois en termes de qualité / maintenabilité et de rapidité du code. Depuis que je les maîtrise, je considère les tables temporaires et les curseurs comme les grands maux du traitement SQL. Je peux me débrouiller très bien avec les variables de table et les CTE pour presque tout maintenant. Mon code est plus propre et plus rapide.

Mel Padden
la source
Maintenant, soyons justes - les curseurs sont le grand mal; Les tables de travail sont au pire un moindre mal. :-) C'est vraiment injuste de les mettre au même niveau que vous vous êtes vus.
RDFozz
@RDFozz, l' enfer a 9 cercles, comme nous le savons tous . Permet de mettre les tables temporaires au 2ème et les curseurs au ... 7ème? ;)
ypercubeᵀᴹ
1
Vous savez ce qu'est le "grand mal" dans la programmation? Quand les gens disent qu'une technique particulière est mauvaise. Il y a une place pour les curseurs. Ils peuvent surperformer les autres techniques dans certains scénarios. Il n'y a pas de mal ici - vous devez apprendre à utiliser le bon outil pour le travail. Mesurez ce que vous faites et ne croyez pas le battage publicitaire que CTE, les tables temporaires ou les curseurs sont diaboliques. Mesurer - parce que la vérité dépend du scénario.
Dave Hilditch
@DaveHilditch c'est un commentaire juste, mais c'est également un commentaire juste d'affirmer que dans de très nombreuses situations, les curseurs ne sont pas la bonne solution, c'est donc une généralisation viable de les avoir, enfin, presque en dernier recours.
Mel Padden
1
D'après mon expérience, un CURSEUR n'est pas mauvais en soi. CURSORS sont généralement utilisés "à tort" par les développeurs parce que dans la plupart des langages de programmation, vous devez penser de manière itérative, par opposition à SQL où vous devez penser principalement par lots. Je sais que c'est une erreur courante sur mon lieu de travail, où les développeurs ne peuvent tout simplement pas "voir" le moyen de résoudre un problème autre qu'avec un CURSEUR. C'est pourquoi un bon administrateur de base de données est pratique pour les enseigner et les corriger. @DaveHilditch a parfaitement raison: il ne manque que le bon outil pour le bon travail.
Philippe
14

Un CTE peut être appelé à plusieurs reprises dans une requête et est évalué chaque fois qu'il est référencé - ce processus peut être récursif. S'il est simplement référencé une fois, il se comporte alors comme une sous-requête, bien que les CTE puissent être paramétrés.

Une table temporaire est physiquement persistante et peut être indexée. En pratique, l'optimiseur de requête peut également conserver des résultats de jointure ou de sous-requête intermédiaires en coulisse, comme dans les opérations de spoule. Il n'est donc pas strictement vrai que les résultats des CTE ne sont jamais conservés sur le disque.

Les variables de table IIRC (en revanche) sont toujours des structures en mémoire.

Préoccupé parTonbridgeWells
la source
4
Les CTE peuvent être paramétrés? Comment? De plus, les variables de table ne sont pas toujours des structures en mémoire. Voir l' excellente réponse de Martin à une question connexe.
Paul White
11

La table temporaire est un objet réel dans tempdb, mais cte n’est qu’une sorte de wrapper autour d’une requête complexe pour simplifier la syntaxe d’organiser la récursivité en une étape.

Oleg Dok
la source
8

La principale raison d'utiliser les CTE est d'accéder aux fonctions de la fenêtre telles que row_number(), entre autres.

Cela signifie que vous pouvez faire des choses comme obtenir la première ou la dernière ligne par groupe TRÈS TRÈS rapidement et efficacement - plus efficacement que les autres moyens dans la plupart des cas pratiques .

with reallyfastcte as (
select *, 
row_number() over (partition by groupingcolumn order by sortingcolumn) as rownum
from sometable
)
select *
from reallyfastcte
where rownum = 1;

Vous pouvez exécuter une requête similaire à celle décrite ci-dessus en utilisant une sous-requête corrélée ou en utilisant une sous-requête, mais le CTE sera plus rapide dans presque tous les scénarios.

De plus, les CTE peuvent vraiment aider à simplifier votre code. Cela peut entraîner des gains de performances car vous comprenez mieux la requête et pouvez introduire davantage de logique métier pour aider l'optimiseur à être plus sélectif.

De plus, les CTE peuvent augmenter les performances si vous comprenez votre logique métier et les parties de la requête à exécuter en premier. En règle générale, placez les requêtes les plus sélectives en premier lieu pour aboutir à des ensembles de résultats pouvant utiliser un index dans leur prochaine jointure, puis ajoutez la option(force order)requête. allusion

Enfin, les CTE n'utilisent pas tempdb par défaut, vous réduisez ainsi les conflits sur ce goulot d'étranglement grâce à leur utilisation.

Les tables temporaires doivent être utilisées si vous devez interroger les données plusieurs fois, ou si vous mesurez vos requêtes et les découvrez en les insérant dans une table temporaire, puis en ajoutant un index pour améliorer vos performances.

Dave Hilditch
la source
tous les bons points ... +1
Mel Padden
6

Il semble y avoir un peu de négativité ici envers les CTE.

Si je comprends bien, le CTE est essentiellement une sorte de point de vue ad hoc. SQL est un langage à la fois déclaratif et basé sur un ensemble. Les CTE sont un excellent moyen de déclarer un ensemble! Ne pas être en mesure d'indexer un CTE est en fait une bonne chose car vous n'en avez pas besoin! C'est vraiment une sorte de sucre syntaxique pour rendre la requête plus facile à lire / écrire. Tout optimiseur décent déterminera le meilleur plan d’accès en utilisant des index sur les tables sous-jacentes. Cela signifie que vous pourriez accélérer efficacement votre requête CTE en suivant les conseils d'index sur les tables sous-jacentes.

De même, le fait que vous ayez défini un ensemble en tant que CTE ne signifie pas que toutes les lignes de cet ensemble doivent être traitées. En fonction de la requête, l'optimiseur peut traiter "juste assez" de lignes pour satisfaire la requête. Peut-être n’avez-vous besoin que des 20 premiers exemplaires de votre écran. Si vous avez construit une table temporaire, vous devez vraiment lire / écrire toutes ces lignes!

Sur cette base, je dirais que les CTE sont une excellente fonctionnalité de SQL et peuvent être utilisés partout où ils facilitent la lecture de la requête. Je ne penserais qu'à une table temporaire pour un traitement par lots qui aurait vraiment besoin de traiter chaque enregistrement. Même alors, après tout, ce n'est pas vraiment recommandé, car dans une table temporaire, la base de données a beaucoup plus de difficulté à vous aider à mettre en cache et à indexer. Il peut être préférable d’avoir une table permanente avec un champ PK unique pour votre transaction.

Je dois admettre que mon expérience concerne principalement DB2; je suppose donc que le travail de CTE fonctionne de manière similaire dans les deux produits. Je serai heureusement corrigé si les CTE sont inférieurs au serveur SQL. ;)

Ben Thurley
la source