Puzzle d'optimiseur de requête SQL Server 2008 R2
Nous avons deux tableaux, contenant chacun 9 millions de lignes. 70 000 lignes sont différentes, les autres sont les mêmes.
C'est rapide, 13 secondes,
select * from bigtable1
except select * from similar_bigtable2
Cela trie la sortie et est également rapide, 13 secondes également,
select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column
Bien que cela soit extrêmement lent:
;with q as (
select * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
Et même une "astuce" que j'utilise parfois pour indiquer à SQL Server qu'il doit précalculer une certaine partie de la requête avant de continuer, ne fonctionne pas et entraîne également une requête lente:
;with q as (
select top 100 percent * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
En regardant les plans de requête, la raison n'est pas difficile à trouver:
SQL Server place deux sortes de 9 millions de lignes avant le hashmatch, alors que je préférerais qu'il n'ait ajouté qu'une seule sorte de 70 000 lignes après le hashmatch.
Donc, la question: comment puis-je demander à l'optimiseur de requêtes de le faire?
la source
EXCEPT
(par exempleOUTER JOIN
)? Je me rends compte que la syntaxe est moins pratique, mais vous pourrez peut-être mieux jouer avec les indices d'indexation / jointure (ou vous n'en aurez peut-être pas besoin). L'alternative que vous utilisez maintenant (tout d'abord dans une table #temp) est une solution de dernier recours, mais dans certains cas, c'est la seule façon de forcer l'optimiseur à séparer complètement deux parties d'une requête de la manière que vous souhaitez.Réponses:
La principale différence entre ces deux plans de requête réside en fait dans la différence entre Hash Match et Merge Join. Hash Match est plus efficace et comme vous pouvez le voir, la requête s'exécute plus rapidement dans l'option 1 (sans utiliser CTE).
CTE est un excellent outil, mais il ne semble pas efficace dans deux cas, les prédicats complexes ou la clé parent / enfant non unique. Dans votre cas, il n'y a pas de clé unique et SQL Server doit d'abord trier les ensembles de données pour pouvoir répondre à vos besoins. Jetez un œil au lien ci-dessous qui vous en dit plus sur ce problème: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx
Il semble donc que vous deviez accepter sa lenteur ou réécrire la logique avec la boucle WHILE qui peut être plus efficace.
la source
Essayez ceci, mieux?
la source
Ce n'est pas une solution idéale, mais si vous n'êtes pas en mesure de structurer le tsql pour générer un plan efficace, vous pouvez définir un guide de plan pour forcer le plan que vous souhaitez. Faire cela signifierait que si un plan plus efficace devient disponible, SQL ne le considérera pas mais c'est une option.
la source