Perché il riferimento a una variabile in un predicato del join forza i cicli annidati?


16

Di recente ho riscontrato questo problema e non sono riuscito a trovarne alcuna discussione online.

La query qui sotto

DECLARE @S VARCHAR(1) = '';

WITH T
     AS (SELECT name + @S AS name2,
                *
         FROM   master..spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2;

Ottiene sempre un piano di cicli nidificati

inserisci qui la descrizione dell'immagine

Il tentativo di forzare il problema con INNER HASH JOINo INNER MERGE JOINsuggerimenti produce il seguente errore.

Il processore di query non è stato in grado di produrre un piano di query a causa dei suggerimenti definiti in questa query. Reinvia la query senza specificare alcun suggerimento e senza utilizzare SET FORCEPLAN.

Ho trovato una soluzione alternativa che consente di utilizzare hash o merge join - avvolgendo la variabile in un aggregato. Il piano generato ha costi significativamente inferiori (19.2025 vs 0.261987)

DECLARE @S2 VARCHAR(1) = '';

WITH T
     AS (SELECT name + (SELECT MAX(@S2)) AS name2,
                *
         FROM   spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2; 

inserisci qui la descrizione dell'immagine

Qual è la ragione di questo comportamento? e c'è una soluzione migliore di quella che ho trovato? (che forse non richiede i rami del piano di esecuzione extra)

Risposte:


13

Ho provato la tua query su un'istanza di SQL 2012 e il flag di traccia 4199 sembra risolvere il problema. Con esso abilitato ottengo un merge join per un costo totale di 0,24 e nessuna delle filiali extra.

L'articolo KB specifico per questo problema è che si verificano problemi di prestazioni quando il predicato di join nella query presenta colonne di riferimento esterne in SQL Server 2005 o in SQL Server 2008

inserisci qui la descrizione dell'immagine

Per qualificarsi ulteriormente, TF 4199 abilita tutte le correzioni dell'ottimizzatore. Vedi questo link per maggiori informazioni. Abilitare tutto in una volta può avere strani effetti collaterali, quindi se riesci a trovare una correzione specifica potrebbe essere meglio abilitare la correzione da sola.

È possibile abilitare un flag di traccia in base alla query utilizzando OPTION (QUERYTRACEON 4199);


0

Vecchia domanda, ma vedendo la risposta non era super definitiva, ho pensato di pubblicare una soluzione alternativa che ho trovato. Non sono sicuro del motivo per cui Query Optimizer si oppone a HASH, ma penso che non gli piaccia MERGEperché non ha un input ordinato. Nel 2012/14,

DECLARE @S VARCHAR(1) = '';

    WITH T
        AS (SELECT TOP (2147483647)
                name + @S AS name2,
                *
            FROM   master..spt_values
            ORDER BY name + @S)
    SELECT *
    FROM   T T1
           INNER JOIN T T2
             ON T1.name2 = T2.name2;

produce il seguente piano:

inserisci qui la descrizione dell'immagine

Forcing TOPe ORDER BYin the cte sembrano fornire all'ottimizzatore una conoscenza sufficiente del set di dati per eseguire il MERGE JOIN.

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.