Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Was ist performanter, CTE oder temporäre Tabellen?

Es kommt darauf an.

Zunächst einmal

Was ist ein gemeinsamer 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 Inline-Tabellenwertfunktionen. Beachten Sie, dass BOL zwar sagt, dass ein CTE "als temporäre Ergebnismenge angesehen werden kann", dies jedoch eine rein logische Beschreibung ist. Meistens ist 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 werden und Spaltenstatistiken haben.

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

Beachten Sie, dass im obigen Plan CTE1 nicht erwähnt wird. Es greift nur 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 einer temporären Zwischentabelle hier wäre massiv kontraproduktiv.

Materialisierung der CTE-Definition von

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

Würde das Kopieren von etwa 8 GB an Daten in eine temporäre Tabelle beinhalten, dann wäre auch noch der Aufwand für die Auswahl daraus vorhanden.

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 Rechner etwa 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 finden.

Dies wäre ein guter Kandidat, um das Zwischenergebnis zu materialisieren. Das entsprechende Neuschreiben der 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 

Die zwischenzeitliche Materialisierung eines Teils einer Abfrage in einer temporären Tabelle kann manchmal nützlich sein, selbst wenn sie nur einmal ausgewertet wird – wenn sie es ermöglicht, den Rest der Abfrage neu zu kompilieren, indem Statistiken über das materialisierte Ergebnis genutzt werden. Ein Beispiel für diesen Ansatz finden Sie im SQL Cat-Artikel When To Break Down Complex Queries.

Unter bestimmten Umständen verwendet SQL Server einen Spool, um ein Zwischenergebnis zwischenzuspeichern, z. eines CTE und vermeiden, dass dieser Teilbaum neu bewertet werden muss. Dies wird im (migrierten) Connect-Element erörtert. Geben Sie einen Hinweis, um die Zwischenmaterialisierung von CTEs oder abgeleiteten Tabellen zu erzwingen. Es werden jedoch keine Statistiken darüber erstellt, und selbst wenn die Anzahl der gespoolten Zeilen stark von der geschätzten abweichen sollte, ist es dem in Bearbeitung befindlichen Ausführungsplan nicht möglich, sich dynamisch als Reaktion darauf anzupassen (zumindest in aktuellen Versionen. Adaptive Abfragepläne können in möglich werden die Zukunft).