web-dev-qa-db-fra.com

Existe-t-il une différence de performance entre CTE, sous-requête, table temporaire ou variable de table?

Dans cet excellent SO question , les différences entre CTE et sub-queries ont été discutées.

Je voudrais demander spécifiquement:

Dans quelles circonstances chacun des éléments suivants est-il plus efficace/plus rapide?

  • CTE
  • Sous-requête
  • Table temporaire
  • Table Variable

Traditionnellement, j'ai utilisé beaucoup de temp tables dans le développement de stored procedures, car ils semblent plus lisibles que de nombreuses sous-requêtes entrelacées.

Non-recursive CTEs encapsule très bien les ensembles de données et est très lisible, mais existe-t-il des circonstances spécifiques dans lesquelles on peut affirmer qu'elles seront toujours plus performantes? ou faut-il toujours chercher les différentes options pour trouver la solution la plus efficace?


EDIT

On m'a récemment dit qu'en termes d'efficacité, les tables temporaires constituent un bon premier choix car elles possèdent un histogramme associé, à savoir des statistiques.

188
whytheq

SQL est un langage déclaratif, pas un langage procédural. C'est-à-dire que vous construisez une instruction SQL pour décrire les résultats souhaités. Vous ne dites pas au moteur SQL comment de faire le travail.

En règle générale, il est judicieux de laisser le moteur SQL et l'optimiseur SQL trouver le meilleur plan de requête. Le développement d'un moteur SQL nécessite de nombreuses années-personnes. Laissez donc les ingénieurs faire ce qu'ils savent faire.

Bien entendu, il existe des situations où le plan de requête n'est pas optimal. Ensuite, vous souhaitez utiliser les indicateurs de requête, restructurer la requête, mettre à jour les statistiques, utiliser des tables temporaires, ajouter des index, etc. pour obtenir de meilleures performances.

Quant à ta question. Les performances des CTE et des sous-requêtes devraient, en théorie, être identiques, car elles fournissent toutes les mêmes informations à l'optimiseur de requêtes. Une différence est qu'un CTE utilisé plus d'une fois peut être facilement identifié et calculé une fois. Les résultats pourraient ensuite être stockés et lus plusieurs fois. Malheureusement, SQL Server ne semble pas tirer parti de cette méthode d'optimisation de base (on pourrait appeler cette élimination commune des sous-requêtes).

Les tables temporaires sont une autre affaire, car vous fournissez davantage de conseils sur la manière dont la requête doit être exécutée. Une différence majeure est que l'optimiseur peut utiliser les statistiques de la table temporaire pour établir son plan de requête. Cela peut entraîner des gains de performance. De même, si vous utilisez un CTE (sous-requête) complexe utilisé plusieurs fois, son stockage dans une table temporaire donnera souvent un gain de performances. La requête est exécutée une seule fois.

La réponse à votre question est que vous devez jouer pour obtenir les performances que vous attendez, en particulier pour les requêtes complexes qui sont exécutées régulièrement. Dans un monde idéal, l'optimiseur de requêtes trouverait le chemin d'exécution idéal. Bien que cela arrive souvent, vous pourrez peut-être trouver un moyen d’améliorer les performances.

210
Gordon Linoff

Il n'y a pas de règle. Je trouve les CTE plus lisibles et les utilise sauf si ils présentent un problème de performances. Dans ce cas, j'étudie le problème réel plutôt que de deviner que le CTE est le problème et essayer de le réécrire en utilisant un autre approche. La question est généralement plus complexe que la façon dont j'ai choisi d'exprimer de manière déclarative mes intentions concernant la requête.

Dans certains cas, vous pouvez démêler les CTE ou supprimer des sous-requêtes et les remplacer par une table #temp afin de réduire la durée. Cela peut être dû à plusieurs facteurs, tels que les statistiques obsolètes, l’incapacité à obtenir des statistiques précises (par exemple, l’association à une fonction de valeur tabulaire), le parallélisme ou même l’impossibilité de générer un plan optimal en raison de la complexité de la requête ( dans ce cas, une rupture peut donner une chance à l'optimiseur). Mais il existe également des cas où les E/S impliquées dans la création d'une table #temp peuvent l'emporter sur les autres aspects de performance qui peuvent rendre moins attrayante la forme d'un plan particulier utilisant un CTE.

Très honnêtement, il y a beaucoup trop de variables pour fournir une réponse "correcte" à votre question. Il n'y a pas de moyen prévisible de savoir quand une requête peut basculer en faveur d'une approche ou d'une autre - sachez qu'en théorie, la même sémantique pour un CTE ou une seule sous-requête devrait exécute exactement la même chose. Je pense que votre question aurait plus de valeur si vous présentiez des cas où ce n'est pas vrai - il se peut que vous ayez découvert une limitation dans l'optimiseur (ou que vous en découvriez un connu), ou que vos requêtes ne soient pas sémantiquement équivalentes. ou celui-ci contient un élément qui contrarie l'optimisation.

Je vous suggère donc d'écrire la requête de la manière qui vous semble la plus naturelle et de ne vous écarter que lorsque vous découvrez un problème de performances réel rencontré par l'optimiseur. Personnellement, je les classe CTE, puis sous-requête, avec #temp table en dernier recours.

69
Aaron Bertrand

#temp est materalized et CTE non.

CTE est juste une syntaxe donc en théorie c'est juste une sous-requête. C'est exécuté. La température est matérialisée. Ainsi, un CTE coûteux dans une jointure qui est exécutée plusieurs fois peut être meilleur dans une #temp. De l’autre côté, s’il s’agit d’une évaluation facile qui n’est pas exécutée mais à quelques reprises, elle ne vaut pas la surcharge de #temp.

Il y a des personnes sur SO qui n'aiment pas les variables de table, mais je les aime car elles sont matérialisées et plus rapides à créer que #temp. Il arrive que l'optimiseur de requêtes obtienne de meilleurs résultats avec une #temp qu'avec une variable de table.

La possibilité de créer une PK sur une variable #temp ou table donne à l'optimiseur de requêtes plus d'informations qu'un CTE (car vous ne pouvez pas déclarer une PK sur un CTE).

16
paparazzo

À mon avis, il n’est TOUJOURS pas préférable d’utiliser une table temporaire plutôt qu’un CTE:

  1. Vous ne pouvez pas mettre une clé primaire sur un CTE, les données auxquelles le CTE accède devront alors traverser chacun des index des tables du CTE plutôt que d'accéder simplement à la clé ou à l'index de la table temporaire.

  2. Parce que vous ne pouvez pas ajouter de contraintes, d’index et de clés primaires à un CTE, ils sont plus sujets aux bogues qui s’introduisent et aux mauvaises données.


hier, hier

Voici un exemple où des contraintes #table peuvent empêcher des données erronées, ce qui n’est pas le cas dans CTE

DECLARE @BadData TABLE ( 
                       ThisID int
                     , ThatID int );
INSERT INTO @BadData
       ( ThisID
       , ThatID
       ) 
VALUES
       ( 1, 1 ),
       ( 1, 2 ),
       ( 2, 2 ),
       ( 1, 1 );

IF OBJECT_ID('tempdb..#This') IS NOT NULL
    DROP TABLE #This;
CREATE TABLE #This ( 
             ThisID int NOT NULL
           , ThatID int NOT NULL
                        UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
     AS (SELECT *
           FROM @BadData)
     SELECT *
       FROM This_CTE;
10
ShanksPranks