Pourquoi mon ORDER BY trie-t-il deux tables avant EXCEPT (lent) et non après (rapide)?

12

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:

Plan de requête Plan de requête avec ORDER BY

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?

thomaspaulb
la source
3
Il ne trie pas avant le hashmatch, il trie puis effectue une fusion-jointure (pas une hachage-jointure). Peut-être existe-t-il un indice pour forcer une jointure par hachage (ou empêcher une fusion-jointure)?
Thilo
3
Il semble que l'optimiseur de requêtes SQL Server ait déterminé que le tri des données était bénéfique afin qu'il puisse utiliser la jointure de fusion beaucoup plus rapide (qui ne fonctionne que pour les données triées) au lieu de la jointure de correspondance de hachage ou de la jointure de boucle imbriquée beaucoup plus lente ....
marc_s
9
Avez-vous essayé des alternatives à EXCEPT(par exemple OUTER 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.
Aaron Bertrand

Réponses:

1

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.

Ciel
la source
0

Essayez ceci, mieux?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column
Gordon Bell
la source
0

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.

cfradenburg
la source