12
votes

Pourquoi l'ordre des clauses d'inscription affecte-t-il le plan de requête dans SQL Server?

Je construis une vue sur SQL Server 2000 (et 2005) et j'ai remarqué que l'ordre des instructions de jointure affecte considérablement le plan d'exécution et la vitesse de la requête.

select sr.WTSASessionRangeID    // + lots of columns

from WTSAVW_UserSessionRange us
inner join WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRangeID
left outer join WTSA_SessionRangeTutor srt on srt.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeClass src on src.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeEnrolmentPeriod srep on srep.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeStudent stsd on stsd.WTSASessionRangeID = sr.WTSASessionRangeID

// SessionRangeStream is a many-to-many mapping table between SessionRange and MO_Stream
left outer join (
    WTSA_SessionRangeStream srs
    inner join MO_Stream ms on ms.MOStreamID = srs.MOStreamID
) on srs.WTSASessionRangeID = sr.WTSASessionRangeID

// SessionRanges MAY have Subranges and Subranges MAY have Rooms
left outer join (
    WTSA_SessionSubrange ssr    
    left outer join WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID
) on ssr.WTSASessionRangeID = sr.WTSASessionRangeID


0 commentaires

7 Réponses :


2
votes

Évidemment, l'optimiseur SQL Server 2005 est beaucoup mieux que le SQL Server 2000 un.

Cependant, il y a beaucoup de vérité dans votre question. Les jointures extérieures entraîneront une exécution pour varier sauvagement sur la commande (les jointures intérieures ont tendance à être optimisées à la voie la plus efficace, mais encore une fois, la commande compte). Si vous y réfléchissez, lorsque vous construisez des jointures à gauche, vous devez déterminer ce que le diable est à gauche. En tant que tel, chaque jointure doit être calculée avant que tous les autres jointures puissent être effectuées. Il devient séquentiel et non parallèle. Maintenant, évidemment, il y a des choses que vous pouvez faire pour combattre ceci (tels que des index, des vues, etc.). Mais, le point se tient: la table doit savoir ce qui est à gauche avant qu'il ne puisse faire une jointure extérieure gauche. Et si vous continuez à ajouter des jointures, vous obtenez de plus en plus d'abstraction à ce qui est à gauche (surtout si vous utilisez des tables jointes comme table de gauche!).

Avec des jointures intérieures, vous pouvez toutefois les paralléliser un peu, il y a donc moins de différence dramatique en ce qui concerne l'ordre concerné.


2 commentaires

Merci pour l'entrée Eric - je vais faire plus de réorganisation de nouveau pour voir si je peux avoir le coût supplémentaire. Il y a des index sur toutes les colonnes utilisées pour rejoindre. Malheureusement, la nature des données signifie que je dois utiliser des jointures extérieures pour celui-ci.


"Évidemment, l'optimiseur SQL Server 2005 est beaucoup mieux que le SQL Server 2000 un." C'est un euphémisme. Non seulement le SQL Server 2000 Query Optimizer Optimizer (avec le reste de l'édition 2000, à mon avis), il semble souvent trop optimiser, en faisant des hypothèses fausses qui entraînent des données hilarieusement incorrectes. Une union de vues, ou un point de vue contenant une union, ou une vue contenant une vue (!) Peut déclencher cette idiotie une fois que vous traversez un seuil de complexité. Je n'ai jamais pu identifier la nature du problème exactement, mais j'ai souvent été mordu par elle.



1
votes

Cela dépend de laquelle des champs de jointure sont indexés - s'il doit analyser le premier champ, mais utilisez un index sur la seconde, c'est lent. Si votre premier champ de jointure est un index, ce sera plus rapide. Je suppose que c'est que 2005 l'optimise mieux en déterminant les champs indexés et effectuant ces premiers


0 commentaires

2
votes

Une stratégie générale pour optimiser les requêtes contenant des jointures consiste à examiner votre modèle de données et les données et essayer de déterminer quelles jointures réduiront le nombre d'enregistrements qui doivent être considérés comme le plus rapidement. Moins d'enregistrements à prendre en compte, plus la requête sera plus rapide. Le serveur produira généralement un meilleur plan de requête aussi.

Avec l'optimisation ci-dessus, assurez-vous que tous les champs utilisés dans les jointures sont indexés


0 commentaires

1
votes

chez DevConnections Il y a quelques années, une session sur SQL Server Performance a déclaré que (a) l'ordre des jointures extérieures compose, et (b) lorsqu'une requête a beaucoup de jointures, elle ne les regardera pas avant de faire une détermination sur un plan. Si vous savez que vous avez des jointures qui vous aideront à accélérer une requête, elles devraient être tôt dans la liste de la liste (si vous le pouvez).


0 commentaires

8
votes

Je dois être en désaccord avec toutes les réponses précédentes, et la raison est simple: si vous modifiez l'ordre de votre jointure gauche, vos requêtes sont logiquement différentes et comme telles, elles produisent différents ensembles de résultats. Voir pour vous-même:

SELECT 1 AS a INTO #t1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4;

SELECT 1 AS b INTO #t2
UNION ALL SELECT 2;

SELECT 1 AS c INTO #t3
UNION ALL SELECT 3;

SELECT a, b, c 
FROM #t1 LEFT JOIN #t2 ON #t1.a=#t2.b
  LEFT JOIN #t3 ON #t2.b=#t3.c
ORDER BY a;

SELECT a, b, c 
FROM #t1 LEFT JOIN #t3 ON #t1.a=#t3.c
  LEFT JOIN #t2 ON #t3.c=#t2.b
ORDER BY a;

a           b           c
----------- ----------- -----------
1           1           1
2           2           NULL
3           NULL        NULL
4           NULL        NULL

(4 row(s) affected)

a           b           c
----------- ----------- -----------
1           1           1
2           NULL        NULL
3           NULL        3
4           NULL        NULL


2 commentaires

Cela dépend de la structure des tables. Vous êtes correct pour le boîtier T1-T2, T2-T3. Dans ma situation, c'est T1-T2, T1-T3.


@geofftnz: Voir ma réponse. Vous êtes pas T1-T2, T1-T3



2
votes

vous requis est probablement faux de toute façon. Alex est correct. Eric peut être correct aussi, mais la requête est fausse.

permet de faire ce sous-ensemble: xxx

Vous rejoignez WTSA_SESSSSITIONSUBRANDOOMPER. Vous n'avez peut-être pas de lignes de WTSA_SESSSITIONUNUGRANGE.

La jointure doit être ceci: xxx

C'est pourquoi la commande de jointure affecte les résultats car c'est un Différente requête , déclara-parler.

Vous devez également modifier le mo_stream et wtsa_sessrangeRangestream rejoindre aussi.


4 commentaires

Quelles sont les situations de ces deux requêtes qui renvoient des résultats différents?


Mais dans la situation T1-T2 T2-T3, s'il y avait une relation clé étrangère entre T2 et T3 de sorte qu'une rangée de T3 ne peut pas exister sans référencer une ligne en T2, cela compte-t-il encore?


Oui. C'est en raison de la situation de type Null-Null-Null-Value-Null.


(Sensation d'amorçage horrible sur le code existant) J'ai reformaté ma requête et l'a ajouté à la question. Est-ce logiquement la même chose que votre sous-requête?



3
votes

La commande de jointure fait une différence dans la requête résultante. Ceci est documenté dans Bol dans la docs pour de :

est un ensemble de résultats qui est le produit de deux tables ou plus. Pour plusieurs jointures, utilisez des parenthèses pour changer l'ordre naturel des jointures .

Vous pouvez modifier la commande de jointure à l'aide de parenthèses autour des jointures (BOL le montre dans la syntaxe en haut de la documentation, mais il est facile à manquer).

Ceci est connu sous le nom de comportement chiastique. Vous pouvez également utiliser l'option de requête (commande de force) pour forcer une commande de jointure spécifique, mais cela peut entraîner ce qu'on appelle les "plans touffus" qui peuvent ne sont pas le la plus optimale pour la requête étant exécutée.


0 commentaires