Wenn ein CTE in einer Abfrage definiert ist und nie verwendet wird, gibt es dann einen Ton aus?

Antworten:

21

Dies scheint nicht der Fall zu sein, dies gilt jedoch nur für verschachtelte CTEs.

Erstellen Sie zwei temporäre Tabellen:

CREATE TABLE #t1 (id INT);
INSERT #t1 ( id )
VALUES ( 1 );

CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );

Abfrage 1:

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM your_mom;

Abfrage 2:

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM also_your_mom;

Abfragepläne:

NÜSSE

Es gibt einen Mehraufwand, aber der unnötige Teil der Abfrage wird sehr früh eliminiert (in diesem Fall während des Parsens; in komplexeren Fällen in der Vereinfachungsphase), sodass der zusätzliche Aufwand wirklich minimal ist und nicht zu potenziell teuren Kosten beiträgt Optimierung.

Erik Darling
quelle
28

+1 an Erik, wollte aber zwei Dinge hinzufügen (die in einem Kommentar nicht gut funktionierten):

  1. Sie müssen sich nicht einmal die Ausführungspläne ansehen, um festzustellen, ob sie ignoriert werden, wenn sie nicht verwendet werden. Folgendes sollte den Fehler "Teilen durch 0" verursachen, dies ist jedoch nicht der Fall, da überhaupt cte2keine Auswahl getroffen wurde:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
  2. CTEs können ignoriert werden, selbst wenn sie der einzige CTE sind, und selbst wenn sie ausgewählt werden, wenn logischerweise alle Zeilen sowieso ausgeschlossen würden. Im folgenden Fall weiß das Abfrageoptimierungsprogramm im Voraus, dass keine Zeilen vom CTE zurückgegeben werden konnten, sodass es sich nicht einmal darum kümmert, sie auszuführen:

    ;WITH cte AS
    (
      SELECT 1 / 0 AS [Bob]
    )
    SELECT TOP (1) [object_id]
    FROM   sys.objects
    UNION ALL
    SELECT cte.[Bob]
    FROM   cte
    WHERE  1 = 0;

In Bezug auf die Leistung wird der nicht verwendete CTE analysiert und kompiliert (oder zumindest im folgenden Fall kompiliert), sodass er nicht zu 100% ignoriert wird. Die Kosten müssten jedoch vernachlässigbar und nicht besorgniserregend sein.

Wenn nur geparst wird, gibt es keinen Fehler:

SET PARSEONLY ON;

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET PARSEONLY OFF;
GO

Wenn alles nur kurz vor der Ausführung ausgeführt wird, gibt es ein Problem:

GO
SET NOEXEC ON;
GO

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET NOEXEC OFF;
GO
/*
Msg 207, Level 16, State 1, Line XXXXX
Invalid column name 'NotHere'.
*/
Solomon Rutzky
quelle
Ich wünschte, ich könnte mehr als eine Antwort als richtig markieren, aber Erik hat dich beim Pistolenschießen besiegt. :) Aber deine Antwort ist sehr informativ und auch großartig, danke!
JD
Was passiert, wenn sich die CTEs in einer Ansicht befinden und die Ansicht mehr als dreimal verschachtelt ist? Gibt es nicht einen Punkt, an dem der Optimierer aufgibt und alles ausführt?
Zikato,
@Zikato Ich habe keine Ahnung, aber das ist eine tolle Frage. Sie sollten in der Lage sein, einen Test ohne großen Aufwand einzurichten, indem Sie eine Ansicht mit dem in den ersten beiden Beispielen gezeigten Trick zum Teilen durch Null erstellen. Bitte lassen Sie mich die Ergebnisse wissen, da ich jetzt sehr neugierig auf dieses Szenario bin :-).
Solomon Rutzky
@SolomonRutzky Um fair zu sein, ich habe es getestet, aber es war nicht schlüssig. Ich habe eine Ansicht aus Ihrem CTE-Beispiel erstellt und sie fünfmal verschachtelt, aber da es sich um eine konstante und nicht wirklich komplizierte Ansicht handelt, hat der Optimierer sie gut verarbeitet. Ich möchte es in Zukunft gründlicher testen und hinter komplexerer Logik verstecken. Ich lasse es dich wissen.
Zikato
@ Zikato Interessant. Ich bin mir nicht sicher, was als "komplex" angesehen wird, aber mein Beispiel ist sehr simpel. Wenn Sie "5-mal verschachtelt" sagen, meinen Sie dies in anderen Ansichten / Prozessen, die sich gegenseitig aufrufen und 5-fach waren, oder in Unterabfragen / CTEs? Ich denke, es besteht die Möglichkeit, dass das Verschachteln von genügend Ebenen es überspringt, aber nicht, weil es nicht referenziert wird, sondern weil eine höhere Verschachtelungsebene es nicht verwendet und dies für niedrigere Ebenen angenommen wird. Ich habe gesehen, wo der Trick des Einsetzens NEWID()einer Ansicht zur Verwendung in einer UDF den gleichen Wert aus mehreren Aufrufen zurückgeben kann, da das Optimierungsprogramm sie zwischenspeichert.
Solomon Rutzky