Perché queste query simili utilizzano fasi di ottimizzazione diverse (elaborazione delle transazioni e piano rapido)?


12

Il codice di esempio in questo elemento di connessione

Mostra un bug dove

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

Restituisce i risultati corretti. Ma quanto segue restituisce risultati errati (nel 2014 utilizzando il nuovo stimatore della cardinalità)

SELECT
    (SELECT COUNT(*)
    FROM dbo.my_splitter_1('2') L1
     INNER JOIN dbo.my_splitter_1('') L2
        ON L1.csv_item = L2.csv_item)

Poiché carica erroneamente i risultati per L2 in uno spool di sottoespressione comune, quindi riproduce il risultato per quello L1.

Ero curioso di sapere perché la differenza di comportamento tra le due query. Trace Flag 8675 mostra che entra in funzione quello che funziona search(0) - transaction processinge quello che non funziona search(1) - quick plan.

Quindi presumo che la disponibilità di ulteriori regole di trasformazione sia alla base della differenza di comportamento (la disabilitazione di BuildGbApply o GenGbApplySimple sembra risolverla, ad esempio).

Ma perché i due piani per queste query molto simili incontrano diverse fasi di ottimizzazione? Da quello che ho letto search (0)richiede almeno tre tabelle e quella condizione certamente non è soddisfatta nel primo esempio.

Risposte:


7

Ogni fase ha condizioni di ingresso. "Avere almeno tre riferimenti di tabella" è una delle condizioni di ingresso di cui parliamo quando si forniscono esempi semplici, ma non è l'unica.

In generale, sono ammessi solo i join e i sindacati di base per accedere alla ricerca 0; subquery scalari, semi join ecc. impediscono l'accesso alla ricerca 0. Questa fase è in realtà per le forme di query di tipo OLTP molto comuni. Le regole necessarie per esplorare le cose meno comuni non sono abilitate. La tua query di esempio ha una subquery scalare, quindi non riesce a inserire.

Dipende anche da come si contano i riferimenti alla tabella. Non ho mai approfondito questo aspetto con le funzioni, ma è possibile che la logica stia contando le funzioni con valori di tabella e le variabili di tabella che producono. Potrebbe anche contare il riferimento alla tabella all'interno della funzione stessa - non ne sono sicuro; anche se so che le funzioni sono solo un duro lavoro a tutto tondo.

Il bug con GenGbApplySimpleè brutto. Questa forma del piano è sempre stata una possibilità, ma è stata respinta per motivi di costo fino a quando la modifica in 100 righe ha assunto la cardinalità variabile della tabella. È possibile forzare la forma problematica del piano sul CE pre-2014 con un USE PLANsuggerimento, ad esempio.

Hai ragione sul fatto che il nuovo elemento Connect sia lo stesso problema segnalato in precedenza .

Per fornire un esempio, la seguente query si qualifica per la ricerca 0:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1) 
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;

Apportare una piccola modifica per includere una subquery scalare significa che va direttamente alla ricerca 1:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;
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.