Perché il mio ORDER BY ordina due tabelle prima dell'EXCEPT (lento) e non dopo (veloce)?


12

Puzzle di Query Optimizer di SQL Server 2008 R2

Abbiamo due tabelle, entrambe contenenti 9 milioni di righe. 70.000 righe sono diverse, le altre sono uguali.

Questo è veloce, 13 secondi,

select * from bigtable1
except select * from similar_bigtable2

Questo ordina l'output ed è anche veloce, anche 13 secondi,

select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column

Mentre questo è enormemente lento:

;with q as (
    select * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

E anche un "trucco" che a volte uso per suggerire a SQL Server che deve precalcolare una determinata parte della query prima che passi, non funziona e risulta anche in una query lenta:

;with q as (
    select top 100 percent * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Osservando i piani di query non è difficile trovare il motivo:

Piano di query Piano di query con ORDER BY

SQL Server posiziona due tipi di 9 milioni di righe prima dell'hashatch, mentre preferirei che avesse aggiunto solo un tipo di 70.000 righe dopo l'hashatch.

Quindi la domanda: come posso istruire Query Optimizer a farlo?


3
Non ordina prima dell'hashatch, ordina e quindi fa un merge-join (non un hash-join). Forse c'è un suggerimento per forzare un hash-join (o impedire un merge-join)?
Thilo,

3
Sembra che l'ottimizzatore di query di SQL Server abbia determinato che l' ordinamento dei dati è stato utile in modo da poter utilizzare Merge Join molto più veloce (che funziona solo per i dati ordinati) invece del molto più lento Hash Match Join o Nested Loop Join ....
marc_s

9
Hai provato alternative a EXCEPT(ad esempio OUTER JOIN)? Mi rendo conto che la sintassi è meno conveniente, ma potresti essere in grado di giocare meglio con i suggerimenti di indice / join (o potresti non averne bisogno). L'alternativa che stai utilizzando ora (inserendo prima una tabella #temp) è un'ultima soluzione alternativa, ma in alcuni casi è l'unico modo per forzare l'ottimizzatore a separare completamente due parti di una query nel modo desiderato.
Aaron Bertrand

Risposte:


1

La differenza principale tra questi due piani di query è in realtà la differenza tra Hash Match e Merge Join. Hash Match è più efficiente e come puoi vedere la query viene eseguita più velocemente nell'opzione 1 (non usando CTE).

CTE è un ottimo strumento, ma sembra non essere efficace in due casi, Predicati complessi o Chiave genitore / figlio non univoca. Nel tuo caso non esiste una chiave univoca e SQL Server deve prima ordinare i set di dati per poter soddisfare le tue esigenze. Dai un'occhiata al link qui sotto che ti dice di più su questo problema: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx

Quindi sembra che tu debba accettare la sua lentezza o riscrivere la logica con il ciclo WHILE che può essere più efficiente.


0

Prova questo, meglio?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column

0

Questa non è una soluzione ideale ma se non si è in grado di strutturare il tsql per generare un piano efficiente, è possibile impostare una guida di piano per forzare il piano desiderato. Ciò significherebbe che se un piano più efficiente diventa disponibile SQL non lo prenderà in considerazione ma è un'opzione.

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.