Vorteile von Common Table Expression (CTE)?

21

Von msdn :

Im Gegensatz zu einer abgeleiteten Tabelle kann ein CTE selbstreferenzierend sein und in derselben Abfrage mehrfach referenziert werden.

Ich benutze CTEs ziemlich oft, aber ich habe nie tief über die Vorteile nachgedacht, die sie mit sich bringen.

Wenn ich in derselben Abfrage mehrmals auf einen CTE verweise:

  • Gibt es einen Leistungsvorteil?
  • Überprüft SQL Server die Zieltabellen zweimal, wenn ich einen Self-Join durchführe?
Royi Namir
quelle
2
Der Profiler sollte Ihnen mitteilen, ob zweimal gescannt wird. Meiner Meinung nach sind CTEs fantastisch für Rekursionen.
Dan Andrews
3
Es gibt keine harten Antworten, wenn das Abfrageoptimierungsprogramm aktiv ist. Einige Abfragen bieten Leistungsvorteile, andere nicht. Manchmal ist die Verwendung einer temporären Tabelle anstelle eines CTE schneller, manchmal nicht.

Antworten:

25

In der Regel wird ein CTE NIEMALS die Leistung verbessern .

Ein CTE ist im Wesentlichen eine Einwegansicht. Es sind keine zusätzlichen Statistiken, keine Indizes usw. gespeichert. Sie fungieren als Kurzform für eine Unterabfrage.

Meiner Meinung nach können sie EINFACH überbeansprucht werden (ich sehe in meinem Job viel überbeanspruchten Code). Hier finden Sie einige gute Antworten. Wenn Sie jedoch mehr als einmal auf etwas verweisen müssen oder es sich um mehr als ein paar hunderttausend Zeilen handelt, geben Sie es #tempstattdessen in eine Tabelle ein und indizieren Sie es.

JNK
quelle
3
Zustimmen. Mit Ausnahme von rekursiven CTEs unterstützen sie einfach die Lesbarkeit
gbn
Was passiert, wenn der CTE nur wenige Zeilen zurückgibt (damit sie im Speicher bleiben), deren Berechnung teuer ist (Aggregation in einer großen Tabelle) und dieses Ergebnis mehrmals verwendet wird? Das sollte die Leistung verbessern, nicht wahr? (Zumindest ist das meine Erfahrung mit PostgreSQL und Oracle, wo temporäre Tabellen sehr selten verwendet werden)
a_horse_with_no_name
2
@a_horse_with_no_name - entspricht dem Erstellen einer Unterabfrage. Wenn das Ergebnis mehrmals in einer einzelnen Abfrage verwendet wird, wird es wiederverwendet und nicht neu berechnet. Wenn es in mehr als einer Abfrage verwendet wird, CTEist a eine schlechte Wahl, da die Ergebnisse nach der ersten Abfrage verworfen werden.
JNK
@JNK: danke. Scheint, dass sich SQL Server hier anders verhält.
a_horse_with_no_name
Einige Leute finden CTEs unter
rogerdpack
14

Neben der Rekursion finde ich CTEs unglaublich nützlich, wenn komplexe Berichtsabfragen erstellt werden. Ich benutze eine Reihe von CTEs, um die benötigten Daten zu erhalten und sie dann in der endgültigen Auswahl zu kombinieren. Ich finde, dass sie einfacher zu pflegen sind, als dasselbe mit vielen abgeleiteten Tabellen oder 20 Joins zu tun, und ich kann mir sicherer sein, dass sie aufgrund der Eins-zu-Viele-Beziehungen in die richtigen Daten ohne Auswirkung mehrerer Datensätze zurückgeben all die verschiedenen Joins. Lassen Sie mich ein kurzes Beispiel geben:

;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

Wenn Sie also die verschiedenen gewünschten Informationsblöcke herausfiltern, können Sie jedes Teil einzeln überprüfen (indem Sie die auskommentierten Auswahlen verwenden, jedes einzeln auskommentieren und nur bis zu dieser Auswahl ausführen) und wenn Sie eine Änderung an den Ausgaben vornehmen müssen Berechnung (in diesem Beispiel) ist es einfacher zu finden, als wenn sie alle zu einer massiven Abfrage zusammengemischt werden. Natürlich sind die tatsächlichen Berichtsabfragen, für die ich dies verwende, im Allgemeinen viel komplizierter als das Beispiel.

HLGEM
quelle
1
Nur um Anfragen zu melden? Die Systeme, an denen ich jeden Tag arbeite, haben so komplizierte Transaktionsabfragen. Seltsamerweise gehören unsere Berichtsabfragen oft zu unseren einfacheren. (Ausgenommen natürlich triviale CRUD-Abfragen ohne Join).
Kevin Cathcart
Ich habe dies als Beispiel verwendet, da dies in der Regel die kompliziertesten sind
HLGEM
+1 Manchmal ist eine logischere (vom Menschen lesbare) Abfrage einer potenziell leistungsfähigeren vorzuziehen.
Tag, wenn der
Ja. Angesichts der Tatsache, dass ein CTE normalerweise den gleichen Plan erstellt, sehe ich keinen Grund, schrecklich verschachtelte Monstrositäten mit mehreren Unterabfragen zu erstellen - wenn wir stattdessen jede Komponente visuell in der Reihenfolge auslegen könnten, in der sie benötigt werden. Ich importiere XML-Dateien und mache verschiedene akrobatische Übungen, um die Daten in die richtige Form zu bringen, die ohne CTEs nicht zu schreiben / lesen wären. (Einige meiner alten Codes haben wahrscheinlich überall schreckliche Unterabfragen!)
underscore_d
0

Wie immer kommt es darauf an, aber es gibt Fälle, in denen die Leistung stark verbessert wird. Ich sehe es bei INSERT INTO SELECT-Anweisungen, bei denen Sie einen CTE für die Auswahl verwenden und diesen dann in INSERT INTO verwenden. Dies hat möglicherweise mit der Aktivierung von RCSI für die Datenbank zu tun. In Zeiten, in denen nur sehr wenig ausgewählt ist, kann dies jedoch zu einer erheblichen Verbesserung führen.

Ron S
quelle