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:
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.
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.
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.