web-dev-qa-db-fra.com

Avantages de Common Table Expression (CTE)?

De msdn :

Contrairement à une table dérivée, un CTE peut être auto-référencé et peut être référencé plusieurs fois dans la même requête.

J'utilise beaucoup les CTE, mais je n'ai jamais vraiment réfléchi aux avantages de les utiliser.

Si je référence plusieurs fois un CTE dans la même requête:

  • Y a-t-il un avantage en termes de performances?
  • Si je fais une auto-jointure, SQL Server analysera-t-il les tables cibles deux fois?
21
Royi Namir

En règle générale, un CTE n'améliorera JAMAIS les performances .

Un CTE est essentiellement une vue jetable. Il n'y a pas de statistiques supplémentaires stockées, pas d'index, etc. Il fonctionne comme un raccourci pour une sous-requête.

À mon avis, ils peuvent être FACILEMENT surutilisés (je vois beaucoup de surutilisation dans le code dans mon travail). Quelques bonnes réponses sont ici, mais si vous devez faire référence à quelque chose plusieurs fois, ou si c'est plus de quelques centaines de milliers de lignes, mettez-le dans un #temp table à la place et indexez-la.

25
JNK

Outre la récursivité, je trouve que les CTE sont extrêmement utiles lors de la création de requêtes de rapports complexes. J'utilise une série de CTE pour obtenir des morceaux des données dont j'ai besoin, puis je les combine dans la sélection finale. Je trouve qu'ils sont plus faciles à maintenir que de faire la même chose avec beaucoup de tables dérivées ou 20 jointures et je trouve que je peux être plus sûr qu'il renvoie les données correctes sans effet de plusieurs enregistrements en raison des relations un-plusieurs dans toutes les différentes jointures. Permettez-moi de donner un exemple rapide:

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

Donc, en séparant les différents morceaux d'informations que vous souhaitez, vous pouvez vérifier chaque partie individuellement (en utilisant les sélections commentées, en décommentant chacune individuellement et en ne l'exécutant que dans la mesure sélectionnée) et si vous avez besoin de modifier les dépenses calcul (dans cet exemple), il est plus facile à trouver que lorsqu'ils sont tous mélangés en une seule requête massive. Bien sûr, les requêtes de rapport réelles pour lesquelles j'utilise ceci sont généralement beaucoup plus compliquées que l'exemple.

14
HLGEM

Comme toujours, cela dépend mais il y a des cas où les performances sont grandement améliorées. Je le vois avec les instructions INSERT INTO SELECT où vous utilisez un CTE pour la sélection, puis utilisez-le dans INSERT INTO. Cela peut être dû au fait que RCSI est activé pour la base de données, mais pour les moments où très peu est sélectionné, cela peut aider un peu.

0
Ron S