Se un CTE è definito in una query e non viene mai utilizzato, emette un suono?


Risposte:


21

Non sembra che lo facciano, ma questo si applica solo ai CTE nidificati.

Crea due tabelle temporanee:

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

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

Query 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;

Query 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;

Piani di query:

NOCCIOLINE

C'è un sovraccarico, ma la parte non necessaria della query viene eliminata molto presto (durante l'analisi in questo caso; la fase di semplificazione in casi più complessi), quindi il lavoro aggiuntivo è veramente minimo e non contribuisce a costi potenzialmente costosi ottimizzazione.


28

+1 su Erik, ma volevo aggiungere due cose (che non ha funzionato bene in un commento):

  1. Non è nemmeno necessario esaminare i piani di esecuzione per vedere che vengono ignorati quando non utilizzati. Quanto segue dovrebbe produrre un errore "dividi per 0" ma non è dovuto al fatto che cte2non è stato selezionato affatto:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
  2. I CTE possono essere ignorati, anche se sono gli unici CTE e anche se sono selezionati, se logicamente tutte le righe sarebbero comunque escluse. Di seguito è riportato un caso in cui Query Optimizer sa in anticipo che nessuna riga può essere restituita dal CTE, quindi non si preoccupa nemmeno di eseguirla:

    ;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;

Per quanto riguarda le prestazioni, il CTE inutilizzato viene analizzato e compilato (o almeno compilato nel caso seguente), quindi non viene ignorato al 100%, ma il costo dovrebbe essere trascurabile e non vale la pena preoccuparsene.

Quando si esegue solo l'analisi, non si verifica alcun errore:

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

Quando si esegue tutto solo dopo l'esecuzione, c'è un problema:

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'.
*/

Vorrei poter contrassegnare più di una risposta come corretta, ma Erik ti ha battuto al tiro con la pistola. :) Ma la tua risposta è molto istruttiva e fantastica, grazie!
JD

Cosa succede se i CTE sono in una vista e la vista viene annidata più di 3 volte? Non c'è un punto in cui l'ottimizzatore si arrende e fa funzionare tutto?
Zikato,

@Zikato Non ne ho idea, ma questa è un'ottima domanda. Dovresti essere in grado di impostare un test senza troppi sforzi creando una vista usando il trucco diviso per zero che ho mostrato nei primi due esempi. Per favore fatemi sapere i risultati perché sono molto curioso ora su questo scenario :-).
Solomon Rutzky,

@SolomonRutzky Ad essere sinceri, l'ho provato, ma non è stato conclusivo. Ho creato una vista dal tuo esempio cte e l'ho annidato 5 volte, ma dato che è una scansione costante e non molto complicata, l'ottimizzatore ha gestito bene. Vorrei testarlo più approfonditamente in futuro e nasconderlo dietro una logica più complessa. Ti farò sapere.
Zikato,

@Zikato Interessante. Non sono sicuro di cosa sarebbe considerato "complesso", ma sì, il mio esempio è molto semplicistico. Quando dici "annidalo 5 volte", intendi in altre viste / processi che si chiamano a vicenda ed era profondo 5, o in sottoquery / CTE? Penso che ci sia la possibilità che l'annidamento di livelli sufficienti possa saltarlo, ma non a causa del fatto che non viene referenziato, ma piuttosto a causa di un livello di annidamento più elevato che non lo utilizza e che viene assunto per livelli inferiori. Ho visto dove il trucco di mettere NEWID()in una vista da usare in un UDF può restituire lo stesso valore da più chiamate a causa dell'ottimizzatore che lo memorizza nella cache.
Solomon Rutzky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.