Welche sind performanter, CTE oder temporäre Tabellen?


Antworten:


62

Ich würde sagen, es sind unterschiedliche Konzepte, aber nicht zu unterschiedlich, um "Kreide und Käse" zu sagen.

  • Eine temporäre Tabelle eignet sich zur Wiederverwendung oder zur Durchführung mehrerer Verarbeitungsdurchläufe für einen Datensatz.

  • Ein CTE kann entweder zur Rekursion oder einfach zur Verbesserung der Lesbarkeit verwendet werden.
    Und wie eine Ansicht oder eine Inline-Tabelle kann auch eine Wertfunktion wie ein Makro behandelt werden, das in der Hauptabfrage erweitert wird

  • Eine temporäre Tabelle ist eine andere Tabelle mit einigen Regeln zum Gültigkeitsbereich

Ich habe Prozesse gespeichert, in denen ich sowohl (als auch Tabellenvariablen) verwende.


12
Temp-Tabellen ermöglichen auch Indizes und sogar Statistiken, die manchmal erforderlich sind, während ein CTE dies nicht tut.
CodeCowboyOrg

9
Ich denke, diese Antwort unterstreicht nicht genug die Tatsache, dass CTEs zu einer schrecklichen Leistung führen können. Normalerweise verweise ich auf diese Antwort auf dba.stackexchange. Ihre Frage steht in meiner Suchmaschine an zweiter Stelle, wenn ich cte vs temporary tablesnachschaue. IMHO muss diese Antwort die Nachteile von CTEs besser hervorheben. TL; DR der verknüpften Antwort: Ein CTE sollte niemals für die Leistung verwendet werden. . Ich stimme diesem Zitat zu, da ich die Nachteile von CTEs erlebt habe.
TT.

2
@TT. Interessant. Ich finde, dass CTEs viel besser
abschneiden

197

Es hängt davon ab, ob.

Zuerst

Was ist ein allgemeiner Tabellenausdruck?

Ein (nicht rekursiver) CTE wird sehr ähnlich wie andere Konstrukte behandelt, die auch als Inline-Tabellenausdrücke in SQL Server verwendet werden können. Abgeleitete Tabellen, Ansichten und Funktionen mit Inline-Tabellenwerten. Beachten Sie, dass BOL zwar sagt, dass ein CTE "als temporäre Ergebnismenge betrachtet werden kann", dies jedoch eine rein logische Beschreibung ist. Meistens wird es nicht eigenständig materialisiert.

Was ist eine temporäre Tabelle?

Dies ist eine Sammlung von Zeilen, die auf Datenseiten in Tempdb gespeichert sind. Die Datenseiten können sich teilweise oder vollständig im Speicher befinden. Zusätzlich kann die temporäre Tabelle indiziert sein und Spaltenstatistiken enthalten.

Testdaten

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Beispiel 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

Plan 1

Beachten Sie, dass im obigen Plan CTE1 nicht erwähnt wird. Es greift einfach direkt auf die Basistabellen zu und wird genauso behandelt wie

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Das Umschreiben durch Materialisieren des CTE in eine temporäre Zwischentabelle wäre hier massiv kontraproduktiv.

Materialisierung der CTE-Definition von

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Würde das Kopieren von ungefähr 8 GB Daten in eine temporäre Tabelle erforderlich sein, dann besteht immer noch der Aufwand, auch daraus auszuwählen.

Beispiel 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Das obige Beispiel dauert auf meinem Computer ungefähr 4 Minuten.

Nur 15 Zeilen der 1.000.000 zufällig generierten Werte stimmen mit dem Prädikat überein, aber der teure Tabellenscan wird 16 Mal durchgeführt, um diese zu lokalisieren.

Geben Sie hier die Bildbeschreibung ein

Dies wäre ein guter Kandidat für die Verwirklichung des Zwischenergebnisses. Das Umschreiben der entsprechenden temporären Tabelle dauerte 25 Sekunden.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Mit Plan

Die Zwischenmaterialisierung eines Teils einer Abfrage in eine temporäre Tabelle kann manchmal nützlich sein, selbst wenn sie nur einmal ausgewertet wird - wenn der Rest der Abfrage unter Verwendung von Statistiken über das materialisierte Ergebnis neu kompiliert werden kann. Ein Beispiel für diesen Ansatz finden Sie im SQL Cat-Artikel Wann komplexe Abfragen aufgeschlüsselt werden .

Unter bestimmten Umständen verwendet SQL Server einen Spool, um ein Zwischenergebnis, z. B. eines CTE, zwischenzuspeichern und zu vermeiden, dass dieser Unterbaum neu bewertet werden muss. Dies wird im (migrierten) Connect-Element erläutert. Geben Sie einen Hinweis, um die Zwischenmaterialisierung von CTEs oder abgeleiteten Tabellen zu erzwingen . Hierzu werden jedoch keine Statistiken erstellt, und selbst wenn die Anzahl der gespoolten Zeilen stark von der geschätzten abweichen sollte, kann sich der laufende Ausführungsplan nicht dynamisch anpassen (zumindest in aktuellen Versionen. Adaptive Abfragepläne können in möglich werden die Zukunft).


33
Dies ist die einzige Antwort, die die eigentliche Frage beantwortet (die fragt, welche eine bessere Leistung hat, nicht was der Unterschied ist oder welche Ihr Favorit ist), und sie beantwortet diese Frage richtig: "Es kommt darauf an" ist die richtige Antwort. Es ist auch die einzige Antwort mit unterstützenden Daten, die zu erklären ist. Mehrere andere (mit einer hohen Anzahl von Stimmen) behaupten eindeutig, dass eine besser ist als die andere, ohne Referenzen oder Beweise ... Um klar zu sein, alle diese Antworten sind auch falsch . Weil "es kommt darauf an"
Arkaine55

2
Es ist auch eine gut geschriebene, gut referenzierte Antwort. Ernsthaft erstklassig.
Dan Williams

50

CTE hat seine Verwendung - wenn die Daten im CTE klein sind und sich die Lesbarkeit stark verbessert, wie dies bei rekursiven Tabellen der Fall ist. Die Leistung ist jedoch sicherlich nicht besser als bei Tabellenvariablen, und wenn es sich um sehr große Tabellen handelt, übertreffen temporäre Tabellen den CTE erheblich. Dies liegt daran, dass Sie keine Indizes für einen CTE definieren können und wenn Sie über eine große Datenmenge verfügen, für die eine Verknüpfung mit einer anderen Tabelle erforderlich ist (CTE ist einfach wie ein Makro). Wenn Sie mehrere Tabellen mit jeweils Millionen von Datensatzzeilen verbinden, ist die Leistung von CTE erheblich schlechter als bei temporären Tabellen.


9
Ich habe das aus eigener Erfahrung gesehen. CTEs arbeiten deutlich langsamer.
goku_da_master

7
CTEs arbeiten auch langsamer, da die Ergebnisse nicht zwischengespeichert werden. Jedes Mal, wenn Sie den CTE verwenden, werden die Abfrage, der Plan und alles erneut ausgeführt.
goku_da_master

1
Und die Datenbank-Engine kann sich dafür entscheiden, die Abfrage nicht nur für jede Referenz, sondern für jede Zeile der Verbraucherabfrage als korrelierte Unterabfrage erneut auszuführen. Sie müssen immer darauf achten, wenn dies nicht gewünscht wird.
Mike M

Die temporäre Tabelle wird in tempdb auf SQL Server gespeichert. Dies ist eine Festplatte, hat jedoch den Vorteil, dass sie indiziert wird, und das SQL-Optimierungsprogramm funktioniert in diesem Fall gut bei ausgewählten Abfragen. Sie sind sich nicht sicher, auf welcher Datenbank oder auf welchem ​​Festplattenbereich der CTE gespeichert ist (wenn er die Speichergröße überschreitet und für E / A-Paging in die Warteschlange gestellt wird), aber er wird mit dem großen Datenvolumen nie optimiert. Ich habe die Compiler-Option (mit Neukompilierung) manchmal verwendet, um sie schneller zu machen
rmehra76

33

Temp-Tabellen befinden sich immer auf der Festplatte. Solange Ihr CTE im Speicher gehalten werden kann, ist er höchstwahrscheinlich schneller (wie auch eine Tabellenvariable).

Wenn die Datenlast Ihres CTE (oder Ihrer temporären Tabellenvariablen) jedoch zu groß wird, wird sie auch auf der Festplatte gespeichert, sodass es keinen großen Vorteil gibt.

Im Allgemeinen bevorzuge ich einen CTE gegenüber einer temporären Tabelle, da er nach meiner Verwendung nicht mehr vorhanden ist. Ich muss nicht darüber nachdenken, es explizit fallen zu lassen oder so.

Also keine klare Antwort am Ende, aber persönlich würde ich CTE gegenüber temporären Tabellen bevorzugen.


2
Im Fall von SQLite und PostgreSQL, temporären Tabellen werden automatisch gelöscht ( in der Regel am Ende einer Sitzung). Ich weiß jedoch nichts über andere DBMS.
Serrano

1
CTE ist wie eine temporäre Ansicht. AFAIK-Daten werden nicht gespeichert, sodass nichts im Speicher oder auf der Festplatte gespeichert werden kann. Wichtiger Hinweis: Jedes Mal, wenn Sie den CTE verwenden, wird die Abfrage erneut ausgeführt.
Rob

1
Persönlich habe ich noch nie gesehen, dass ein CTE aus Geschwindigkeitsgründen besser funktioniert als ein Temp-Tisch. Und gut Debuggen ist viel einfacher mit temporären Tabelle
Mark Monforti

7

Die Abfrage, die mir zur Optimierung zugewiesen wurde, wurde mit zwei CTEs in SQL Server geschrieben. Es dauerte 28 Sekunden.

Ich habe zwei Minuten damit verbracht, sie in temporäre Tabellen zu konvertieren, und die Abfrage dauerte 3 Sekunden

Ich habe der temporären Tabelle auf dem Feld, auf dem sie verbunden werden soll, einen Index hinzugefügt und ihn auf 2 Sekunden reduziert

Drei Minuten Arbeit und jetzt läuft es 12x schneller, indem CTE entfernt wird. Ich persönlich werde keine CTEs verwenden, da diese auch schwieriger zu debuggen sind.

Das Verrückte ist, dass die CTEs beide nur einmal verwendet wurden und sich immer noch als 50% schneller erwiesen, wenn sie immer noch mit einem Index versehen wurden.


6

CTE nimmt keinen physischen Platz ein. Es ist nur eine Ergebnismenge, die wir mit join verwenden können.

Temp-Tabellen sind temporär. Wir können Indizes und Einschränkungen wie normale Tabellen erstellen, für die wir alle Variablen definieren müssen.

Der Bereich der temporären Tabelle nur innerhalb der Sitzung. EX: Öffnen Sie zwei SQL-Abfragefenster

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Führen Sie diese Abfrage im ersten Fenster aus, und führen Sie dann die folgende Abfrage im zweiten Fenster aus. Sie können den Unterschied feststellen.

select * from #temp

4
>> "Es ist nur eine Ergebnismenge, die wir mit join verwenden können." -> Das ist nicht korrekt. CTE ist keine "Ergebnismenge", sondern Inline-Code. Die SQL Server-Abfrage-Engine analysiert den CTE-Code als Teil des Abfragetextes und erstellt einen entsprechenden Ausführungsplan. Die Idee, dass CTE inline ist, ist der große Vorteil der Verwendung von CTE, da der Server damit einen "Ausführungsplan kombinieren" erstellen kann
Ronen Ariely,

4

Ich habe beide verwendet, aber in massiven komplexen Verfahren habe ich immer festgestellt, dass temporäre Tabellen besser zu bearbeiten und methodischer sind. CTEs haben ihre Verwendung, aber im Allgemeinen mit kleinen Datenmengen.

Zum Beispiel habe ich Sprocs erstellt, die in 15 Sekunden Ergebnisse großer Berechnungen liefern, diesen Code jedoch so konvertieren, dass er in einem CTE ausgeführt wird, und festgestellt haben, dass er länger als 8 Minuten ausgeführt wird, um dieselben Ergebnisse zu erzielen.


3

Spät zur Party, aber ...

Die Umgebung, in der ich arbeite, ist stark eingeschränkt, unterstützt einige Anbieterprodukte und bietet Mehrwertdienste wie Berichterstellung. Aufgrund von Richtlinien- und Vertragsbeschränkungen ist mir normalerweise nicht der Luxus eines separaten Tabellen- / Datenbereichs und / oder die Möglichkeit zum Erstellen von permanentem Code gestattet [es wird je nach Anwendung etwas besser].

IOW, ich kann nicht normalerweise keine gespeicherte Prozedur oder UDFs oder temporäre Tabellen usw. entwickeln. Ich muss so ziemlich alles über MEINE Anwendungsschnittstelle erledigen (Crystal Reports - Tabellen hinzufügen / verknüpfen, wo Klauseln von w / in CR festlegen usw.). ). Eine kleine Rettung ist, dass Crystal es mir ermöglicht, BEFEHLE (sowie SQL-Ausdrücke) zu verwenden. Einige Dinge, die durch die regulären Funktionen zum Hinzufügen / Verknüpfen von Tabellen nicht effizient sind, können durch Definieren eines SQL-Befehls ausgeführt werden. Ich benutze CTEs dadurch und habe "aus der Ferne" sehr gute Ergebnisse erzielt. CTEs helfen auch bei der Berichterstellung, ohne dass Code entwickelt und an einen DBA übergeben werden muss, um mehrstufige Tests zu kompilieren, zu verschlüsseln, zu übertragen, zu installieren und anschließend zu testen. Ich kann CTEs über die lokale Schnittstelle durchführen.

Der Nachteil bei der Verwendung von CTEs mit CR ist, dass jeder Bericht separat ist. Jeder CTE muss für jeden Bericht gepflegt werden. Wo ich SPs und UDFs ausführen kann, kann ich etwas entwickeln, das von mehreren Berichten verwendet werden kann, wobei nur eine Verknüpfung mit dem SP erforderlich ist und Parameter übergeben werden, als ob Sie an einer regulären Tabelle arbeiten würden. CR ist nicht wirklich gut darin, Parameter in SQL-Befehle zu verarbeiten, so dass dieser Aspekt des CR / CTE-Aspekts fehlen kann. In diesen Fällen versuche ich normalerweise, den CTE so zu definieren, dass genügend Daten (aber nicht ALLE Daten) zurückgegeben werden, und verwende dann die Datensatzauswahlfunktionen in CR, um diese zu schneiden und zu würfeln.

Also ... meine Stimme ist für CTEs (bis ich meinen Datenraum bekomme).


3

Eine Verwendung, bei der ich die hervorragende Leistung von CTE fand, war die, bei der ich eine relativ komplexe Abfrage mit einigen Tabellen verknüpfen musste, die jeweils einige Millionen Zeilen enthielten.

Ich habe den CTE verwendet, um zuerst die Teilmenge basierend auf den indizierten Spalten auszuwählen, um diese Tabellen zuerst auf jeweils einige tausend relevante Zeilen zu reduzieren, und dann den CTE mit meiner Hauptabfrage verbunden. Dies hat die Laufzeit meiner Abfrage exponentiell reduziert.

Obwohl die Ergebnisse für den CTE nicht zwischengespeichert werden und Tabellenvariablen möglicherweise die bessere Wahl waren, wollte ich sie unbedingt ausprobieren und fand heraus, dass sie zum obigen Szenario passen.


Außerdem denke ich, da ich den CTE nur im Join verwende, führe ich den CTE nur einmal in meiner Abfrage aus, so dass das Zwischenspeichern der Ergebnisse in dieser Hinsicht kein so großes Problem war
Käufe

1

Dies ist eine wirklich offene Frage, und alles hängt davon ab, wie sie verwendet wird und welche Art von temporärer Tabelle (Tabellenvariable oder traditionelle Tabelle).

Eine herkömmliche temporäre Tabelle speichert die Daten in der temporären Datenbank, wodurch die temporären Tabellen verlangsamt werden. Tabellenvariablen jedoch nicht.


1

Ich habe dies gerade getestet - sowohl CTE als auch Nicht-CTE (wobei die Abfrage für jede Union-Instanz abgetippt wurde) dauerten beide ~ 31 Sekunden. CTE hat den Code viel lesbarer gemacht - ihn von 241 auf 130 Zeilen reduziert, was sehr schön ist. Temp Table hingegen reduzierte es auf 132 Zeilen und brauchte FÜNF SEKUNDEN, um zu laufen. Kein Witz. Alle diese Tests wurden zwischengespeichert - die Abfragen wurden zuvor alle mehrmals ausgeführt.


1

Aufgrund meiner Erfahrung mit SQL Server habe ich eines der Szenarien gefunden, in denen CTE die Temp-Tabelle übertroffen hat

Ich musste ein DataSet (~ 100000) aus einer komplexen Abfrage nur EINMAL in meiner gespeicherten Prozedur verwenden.

  • Die temporäre Tabelle verursachte einen Overhead in SQL, wo meine Prozedur langsam ausgeführt wurde (da temporäre Tabellen echte materialisierte Tabellen sind, die in tempdb und Persist für die Lebensdauer meiner aktuellen Prozedur vorhanden sind).

  • Auf der anderen Seite bleibt CTE bei CTE nur so lange bestehen, bis die folgende Abfrage ausgeführt wird. CTE ist also eine praktische In-Memory-Struktur mit begrenztem Umfang. CTEs verwenden standardmäßig keine Tempdb.

Dies ist ein Szenario, in dem CTEs wirklich dazu beitragen können, Ihren Code zu vereinfachen und die temporäre Tabelle zu übertreffen. Ich hatte 2 CTEs verwendet, so etwas wie

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO

1
Ihre Antwort scheint sehr allgemein zu sein ... Wie messen Sie, dass "CTE die Temp-Tabelle übertroffen hat"? Hast du einige Zeitmessungen? Meiner Meinung nach sollten Sie Ihre Antwort bearbeiten und weitere Details hinzufügen.
Il Vic

Ja, ich habe Zeitmessungen und einen Ausführungsplan, um meine Aussage zu unterstützen.
Amardeep Kohli

Das Bild für den Ausführungsplan kann aufgrund eingeschränkter Berechtigungen nicht hinzugefügt werden. Aktualisierungen werden aktualisiert, sobald sie behoben sind
Amardeep Kohli
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.