Différence entre CTE et SubQuery?

143

De ce post Comment utiliser ROW_NUMBER dans la procédure suivante?

Il existe deux versions de réponses où l'une utilise a sub-queryet l'autre utilise a CTEpour résoudre le même problème.

Maintenant, quel est l'avantage d'utiliser une CTE (Common Table Expression)sur une `` sous-requête '' (donc, plus lisible ce que la requête fait réellement)

Le seul avantage d'utiliser un CTEover sub-selectest que je peux nommer le fichiersub-query . Existe-t-il d'autres différences entre les deux lorsqu'un CTE est utilisé comme un CTE simple (non récursif)?

dance2die
la source
Question dérivée avec une bonne discussion: stackoverflow.com/q/11169550/781695
utilisateur
7
OMI, quiconque pense qu'un CTE est moins lisible qu'une gigantesque goutte de sous-requêtes entrelacées n'a pas vu le tas d'ordures de requêtes déroutantes en forme de dents de scie utilisées dans la majorité des systèmes de gestion de données d'entreprise. Les requêtes volumineuses et non triviales sont généralement beaucoup plus faciles à lire plus tard ou par de nouveaux yeux que les sous-requêtes, et au moins dans le cas de Postgres, elles fonctionnent comme par magie beaucoup mieux dans de nombreux cas. ([Pour des raisons que je n'ai pas encore compris [( stackoverflow.com/questions/33731068/… ), car le contraire semble plus probable.)
zxq9

Réponses:

102

Dans la sous-requête vs les versions CTE simples (non récursives), elles sont probablement très similaires. Vous devrez utiliser le profileur et le plan d'exécution réel pour repérer les différences, ce qui serait spécifique à votre configuration (nous ne pouvons donc pas vous donner la réponse dans son intégralité).

En général ; Un CTE peut être utilisé de manière récursive; une sous-requête ne peut pas. Cela les rend particulièrement bien adaptés aux structures arborescentes.

Marc Gravell
la source
1
Désolé, j'aurais dû être plus clair dans ma question. Quelle serait la différence entre CTE et Subquery dans le contexte où CTE est utilisé comme sous-requête LIKE?
dance2die
2
@Marc Gravell: Nous pouvons faire plus que cela, car le comportement du profileur n'est pas garanti, par rapport au comportement du CTE, qui est (en termes d'évaluation).
casperOne
1
Je ne sais pas à quel point cette déclaration a du sens pour les personnes qui recherchent la différence entre CTS et les sous-requêtes - A CTE can be used recursively; a sub-query cannot. Un exemple aurait été formidable.
Aniket Thakur
88

Le principal avantage de l' expression de table commune (lorsque vous ne l'utilisez pas pour les requêtes récursives ) est l'encapsulation, au lieu d'avoir à déclarer la sous-requête à chaque endroit où vous souhaitez l'utiliser, vous pouvez la définir une fois, mais avoir plusieurs références à lui.

Cependant, cela ne signifie pas qu'il est exécuté une seule fois (comme par les itérations précédentes de cette même réponse , merci à tous ceux qui ont commenté). La requête a certainement le potentiel d'être exécutée plusieurs fois si elle est référencée plusieurs fois; l'optimiseur de requêtes prend finalement la décision quant à la façon dont le CTE doit être interprété.

casperOne
la source
"Pensez à un CTE comme une variable de table temporaire" cela signifie-t-il que le CTE est stocké sur disque ou en mémoire?
dance2die
Vous ne pouvez pas utiliser le CTE ou la sous-requête dans plusieurs requêtes, par définition. Je suis à peu près sûr que l'optimiseur gère la sous-requête de la même manière qu'il traiterait le CTE (en évaluant le jeu de résultats une seule fois, quel que soit le nombre de fois qu'il est utilisé dans la requête 1)
AlexCuse
@AlexCuse: Je pense avoir suffisamment clarifié le contexte du CTE, mais j'en ai ajouté plus pour essayer de clarifier davantage.
casperOne
@AlexCuse: Il n'y a pas non plus d'implication que le CTE ou la sous-requête peut être utilisé à plusieurs endroits. La différence entre le CTE et l'optimiseur est que le comportement du CTE est garanti, alors que le comportement de l'optimiseur ne l'est pas.
casperOne
et je concéderai qu'il pourrait y avoir des cas extrêmes où l'optimiseur s'étouffe et la sous-requête est évaluée plus d'une fois, je n'en ai rencontré aucun. Là encore, j'utilise CTE partout où je peux;)
AlexCuse
15

CTEsont les plus utiles pour la récursivité:

WITH hier(cnt) AS (
        SELECT  1
        UNION ALL
        SELECT  cnt + 1
        FROM    hier
        WHERE   cnt < @n
        )
SELECT  cnt
FROM    hier

renverra des @nlignes (jusqu'à 101). Utile pour les calendriers, les ensembles de lignes factices, etc.

Ils sont également plus lisibles (à mon avis).

En dehors de cela, CTEles et subqueriessont identiques.

Quassnoi
la source
Dans MSSQL, vous devez ajouter un point-virgule (;) avant WITH, dans l'ordre, vous obtiendrez une erreur. ça devrait être;WITH blabla AS ...)
Obinna Nnenanya
2
@ObinnaNnenanya: uniquement si ce n'est pas la première instruction du lot. Vos déclarations Mettre fin est une bonne points - virgules idée de toute façon, même si SQL Server ne l' applique pas dans les versions actuelles d' autres qu'auparavant WITH, MERGEet même
Quassnoi
10

Une différence qui n'a pas été mentionnée est qu'un seul CTE peut être référencé dans les différentes parties d'un syndicat

user340140
la source
8

À moins que je ne manque quelque chose, vous pouvez nommer les CTE et les sous-requêtes tout aussi facilement.

Je suppose que la principale différence est la lisibilité (je trouve le CTE plus lisible car il définit votre sous-requête à l'avant plutôt qu'au milieu).

Et si vous avez besoin de faire quoi que ce soit avec la récursivité, vous allez avoir un peu de mal à le faire avec une sous-requête;)

AlexCuse
la source
1
Je ne suis pas sûr qu'il y ait une différence non esthétique (même si je pense que dans certaines situations, il peut y avoir de légères différences dans le plan d'exécution). Tu veux m'éclairer?
AlexCuse
2
Vous pouvez nommer des CTE, mais vous ne pouvez créer des alias que pour les sous-requêtes. La différence est que vous pouvez réutiliser des CTE avec plusieurs alias (cf. l'exemple de @Michael Petito dans son commentaire à casperOne). Je ne connais aucun moyen de faire cela avec des sous-requêtes.
kmote
7

Un fait important que personne n'a mentionné est que (au moins dans postgres), les CTE sont des clôtures d'optimisation:

https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

Autrement dit, ils seront traités comme leur propre requête atomique, plutôt que repliés dans l'ensemble du plan de requête. Je n'ai pas l'expertise pour donner une meilleure explication, mais vous devriez vérifier la sémantique de la version de SQL que vous utilisez; pour les utilisateurs avancés, la possibilité de créer une clôture d'optimisation peut améliorer les performances si vous êtes expert dans le contrôle du planificateur de requêtes; dans 99% des cas, cependant, vous devriez éviter d'essayer de dire au planificateur de requêtes ce qu'il doit faire, car ce que vous pensez être plus rapide est probablement pire que ce qu'il pense sera plus rapide. :-)

Ajax
la source
6

En ajoutant aux réponses des autres, si vous avez une seule et même sous-requête utilisée plusieurs fois, vous pouvez remplacer toutes ces sous-requêtes par un CTE. Cela vous permet de mieux réutiliser votre code.

AK
la source
4

Une chose que vous devez également comprendre est que dans les anciennes versions de SQL Server (oui, de nombreuses personnes doivent encore prendre en charge les bases de données SQL Server 2000), les CTE ne sont pas autorisés et la table dérivée est votre meilleure solution.

HLGEM
la source
2

CONSEIL: (MAXRECURSION n)

vous pouvez limiter le nombre de niveaux de récursivité autorisés pour une instruction spécifique en utilisant l' MAXRECURSIONindicateur et une valeur comprise entre 0 et 32 767 dans la OPTIONclause

Par exemple, vous pouvez essayer:

OPTION 
      (MAXRECURSION 150)

GO
De base_
la source