J'ai créé une commande SQL qui utilise INNER JOIN sur 9 tables, de toute façon cette commande prend très longtemps (plus de cinq minutes). Donc mes gens m'ont suggéré de changer INNER JOIN en LEFT JOIN parce que les performances de LEFT JOIN sont meilleures, malgré ce que je sais. Après l'avoir changé, la vitesse de la requête s'est considérablement améliorée.
Je voudrais savoir pourquoi LEFT JOIN est plus rapide que INNER JOIN?
Ma commande SQL ressemble à ci-dessous:
SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D
et ainsi de suite
Mise à jour: Ceci est bref de mon schéma.
FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
ON a.CompanyCd = b.CompanyCd
AND a.SPRNo = b.SPRNo
AND a.SuffixNo = b.SuffixNo
AND a.dnno = b.dnno
INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
ON a.CompanyCd = h.CompanyCd
AND a.sprno = h.AcctSPRNo
INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
ON c.CompanyCd = h.CompanyCd
AND c.FSlipNo = h.FSlipNo
AND c.FSlipSuffix = h.FSlipSuffix
INNER JOIN coMappingExpParty d -- NO PK AND FK
ON c.CompanyCd = d.CompanyCd
AND c.CountryCd = d.CountryCd
INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
ON b.CompanyCd = e.CompanyCd
AND b.ProductSalesCd = e.ProductSalesCd
LEFT JOIN coUOM i -- PK = UOMId
ON h.UOMId = i.UOMId
INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
ON a.CompanyCd = j.CompanyCd
AND b.BFStatus = j.BFStatus
AND b.ProductSalesCd = j.ProductSalesCd
INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
ON e.ProductGroup1Cd = g1.ProductGroup1Cd
INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
ON e.ProductGroup1Cd = g2.ProductGroup1Cd
sql
sql-server
performance
Anonyme
la source
la source
coUOM
? Sinon, vous pourrez peut-être utiliser une semi-jointure. Si oui, vous pourriez l'utiliserUNION
comme alternative. Publier uniquement votreFROM
clause est une information inadéquate ici.Réponses:
A
LEFT JOIN
n'est absolument pas plus rapide que anINNER JOIN
. En fait, c'est plus lent; par définition, une jointure externe (LEFT JOIN
ouRIGHT JOIN
) doit faire tout le travail d'unINNER JOIN
plus le travail supplémentaire d'extension nulle des résultats. Il devrait également renvoyer plus de lignes, augmentant encore le temps d'exécution total simplement en raison de la plus grande taille de l'ensemble de résultats.(Et même si a
LEFT JOIN
était plus rapide dans des situations spécifiques en raison d'une confluence de facteurs difficile à imaginer, il n'est pas fonctionnellement équivalent à unINNER JOIN
, vous ne pouvez donc pas simplement remplacer toutes les instances de l'une par l'autre!)Vos problèmes de performances se situent probablement ailleurs, comme le fait de ne pas avoir une clé candidate ou une clé étrangère indexée correctement. 9 tables, c'est beaucoup de choses à rejoindre, donc le ralentissement pourrait littéralement être presque n'importe où. Si vous publiez votre schéma, nous pourrons peut-être vous fournir plus de détails.
Éditer:
En réfléchissant davantage à cela, je pourrais penser à une circonstance dans laquelle un
LEFT JOIN
pourrait être plus rapide qu'unINNER JOIN
, et c'est quand:Considérez cet exemple:
Si vous l'exécutez et affichez le plan d'exécution, vous verrez que la
INNER JOIN
requête coûte en effet plus cher que leLEFT JOIN
, car elle satisfait les deux critères ci-dessus. C'est parce que SQL Server veut faire une correspondance de hachage pour leINNER JOIN
, mais fait des boucles imbriquées pour leLEFT JOIN
; le premier est normalement beaucoup plus rapide, mais comme le nombre de lignes est si petit et qu'il n'y a pas d'index à utiliser, l'opération de hachage s'avère être la partie la plus coûteuse de la requête.Vous pouvez voir le même effet en écrivant un programme dans votre langage de programmation préféré pour effectuer un grand nombre de recherches sur une liste à 5 éléments, contre une table de hachage à 5 éléments. En raison de la taille, la version de la table de hachage est en fait plus lente. Mais augmentez-le à 50 éléments, ou 5000 éléments, et la version de liste ralentit à une analyse, car c'est O (N) vs O (1) pour la table de hachage.
Mais changez cette requête pour qu'elle soit sur la
ID
colonne au lieu deName
et vous verrez une histoire très différente. Dans ce cas, il fait des boucles imbriquées pour les deux requêtes, mais laINNER JOIN
version est capable de remplacer l'une des analyses d'index cluster avec une recherche - ce qui signifie que ce sera littéralement un ordre de grandeur plus rapide avec un grand nombre de lignes.La conclusion est donc plus ou moins celle que j'ai mentionnée plusieurs paragraphes ci-dessus; il s'agit presque certainement d'un problème d'indexation ou de couverture d'index, éventuellement associé à une ou plusieurs très petites tables. Ce sont les seules circonstances dans lesquelles SQL Server peut parfois choisir un plan d'exécution pire pour un
INNER JOIN
que pour unLEFT JOIN
.la source
Il existe un scénario important qui peut conduire à une jointure externe plus rapide qu'une jointure interne qui n'a pas encore été discutée.
Lors de l'utilisation d'une jointure externe, l'optimiseur est toujours libre de supprimer la table jointe externe du plan d'exécution si les colonnes de jointure sont le PK de la table externe et qu'aucune des colonnes de table externe n'est référencée en dehors de la jointure externe elle-même. Par exemple,
SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY
et B.KEY est le PK pour B. Oracle (je crois que j'utilisais la version 10) et Sql Server (j'ai utilisé 2008 R2) élaguent la table B du plan d'exécution.La même chose n'est pas nécessairement vraie pour une jointure interne:
SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEY
peut ou peut ne pas nécessiter B dans le plan d'exécution en fonction des contraintes existantes.Si A.KEY est une clé étrangère nullable faisant référence à B.KEY, l'optimiseur ne peut pas supprimer B du plan car il doit confirmer qu'une ligne B existe pour chaque ligne A.
Si A.KEY est une clé étrangère obligatoire référençant B.KEY, l'optimiseur est libre de supprimer B du plan car les contraintes garantissent l'existence de la ligne. Mais ce n'est pas parce que l'optimiseur peut supprimer la table du plan. SQL Server 2008 R2 ne supprime PAS B du plan. Oracle 10 supprime B du plan. Il est facile de voir comment la jointure externe surpassera la jointure interne sur SQL Server dans ce cas.
Il s'agit d'un exemple trivial et non pratique pour une requête autonome. Pourquoi rejoindre une table si vous n'en avez pas besoin?
Mais cela pourrait être une considération de conception très importante lors de la conception de vues. Souvent, une vue «tout faire» est créée qui joint tout ce dont un utilisateur peut avoir besoin en rapport avec une table centrale. (Surtout s'il y a des utilisateurs naïfs effectuant des requêtes ad hoc qui ne comprennent pas le modèle relationnel) La vue peut inclure toutes les colonnes pertinentes de nombreuses tables. Mais les utilisateurs finaux peuvent uniquement accéder aux colonnes d'un sous-ensemble des tables dans la vue. Si les tables sont jointes avec des jointures externes, l'optimiseur peut (et supprime) les tables inutiles du plan.
Il est essentiel de s'assurer que la vue utilisant des jointures externes donne les résultats corrects. Comme Aaronaught l'a dit - vous ne pouvez pas substituer aveuglément OUTER JOIN à INNER JOIN et vous attendre aux mêmes résultats. Mais il peut arriver que cela soit utile pour des raisons de performances lors de l'utilisation des vues.
Une dernière note - je n'ai pas testé l'impact sur les performances à la lumière de ce qui précède, mais en théorie, il semble que vous devriez pouvoir remplacer en toute sécurité un INNER JOIN par un OUTER JOIN si vous ajoutez également la condition <FOREIGN_KEY> IS NOT NULL à la clause where.
la source
Si tout fonctionne comme il le devrait, MAIS nous savons tous que tout ne fonctionne pas comme il le devrait, en particulier en ce qui concerne l'optimiseur de requêtes, la mise en cache du plan de requête et les statistiques.
Tout d'abord, je suggérerais de reconstruire l'index et les statistiques, puis de vider le cache du plan de requête juste pour vous assurer que cela ne gâche rien. Cependant, j'ai rencontré des problèmes même lorsque cela est fait.
J'ai rencontré des cas où une jointure gauche a été plus rapide qu'une jointure interne.
La raison sous-jacente est la suivante: si vous avez deux tables et que vous vous joignez à une colonne avec un index (sur les deux tables). La jointure interne produira le même résultat, peu importe si vous bouclez sur les entrées de l'index sur la table un et faites correspondre avec l'index sur la table deux comme si vous faisiez l'inverse: Bouclez les entrées dans l'index sur la table deux et faites correspondre avec l'index dans le tableau un. Le problème est que lorsque vous avez des statistiques trompeuses, l'optimiseur de requête utilisera les statistiques de l'index pour trouver la table avec les entrées les moins correspondantes (en fonction de vos autres critères). Si vous avez deux tableaux avec 1 million chacun, dans le tableau un, vous avez 10 lignes correspondant et dans le tableau deux, vous avez 100000 lignes correspondant. La meilleure façon serait de faire un balayage d'index sur la table un et de faire correspondre 10 fois dans la table deux. L'inverse serait un balayage d'index qui boucle sur 100 000 lignes et essaie de correspondre 100 000 fois et seulement 10 réussissent. Donc, si les statistiques ne sont pas correctes, l'optimiseur peut choisir la mauvaise table et l'index à boucler.
Si l'optimiseur choisit d'optimiser la jointure gauche dans l'ordre d'écriture, il fonctionnera mieux que la jointure interne.
MAIS, l'optimiseur peut également optimiser une jointure gauche de manière sous-optimale en tant que semi-jointure gauche. Pour le faire, choisissez celui que vous voulez, vous pouvez utiliser l'indicateur d'ordre de force.
la source
Essayez les deux requêtes (celle avec jointure interne et gauche) avec
OPTION (FORCE ORDER)
à la fin et publiez les résultats.OPTION (FORCE ORDER)
est un indice de requête qui force l'optimiseur à créer le plan d'exécution avec l'ordre de jointure que vous avez fourni dans la requête.Si
INNER JOIN
commence à jouer aussi vite que possibleLEFT JOIN
, c'est parce que:INNER JOIN
s, l'ordre de jointure n'a pas d'importance. L'optimiseur de requêtes peut ainsi ordonner les jointures comme bon lui semble, de sorte que le problème peut dépendre de l'optimiseur.LEFT JOIN
, ce n'est pas le cas, car la modification de l'ordre de jointure modifiera les résultats de la requête. Cela signifie que le moteur doit suivre l'ordre de jointure que vous avez fourni dans la requête, qui pourrait être meilleur que celui optimisé.Je ne sais pas si cela répond à votre question, mais j'étais une fois dans un projet qui comportait des requêtes très complexes faisant des calculs, ce qui a complètement gâché l'optimiseur. Nous avons eu des cas où un
FORCE ORDER
réduirait le temps d'exécution d'une requête de 5 minutes à 10 secondes.la source
Ont fait un certain nombre de comparaisons entre les jointures externes et internes gauches et n'ont pas été en mesure de trouver une différence consisten. Il existe de nombreuses variables. Je travaille sur une base de données de rapports avec des milliers de tables dont beaucoup avec un grand nombre de champs, de nombreux changements au fil du temps (versions des fournisseurs et workflow local). Il n'est pas possible de créer toutes les combinaisons d'index de couverture pour répondre aux besoins d'une si grande variété de requêtes et gérer les données historiques. Des requêtes internes ont tué les performances du serveur car deux grandes tables (des millions à des dizaines de millions de lignes) sont jointes en interne, tirant toutes les deux un grand nombre de champs et aucun index de couverture n'existe.
Le plus gros problème, cependant, ne semble pas apparaître dans les discussions ci-dessus. Peut-être que votre base de données est bien conçue avec des déclencheurs et un traitement des transactions bien conçu pour garantir de bonnes données. La mienne a souvent des valeurs NULL là où elles ne sont pas attendues. Oui, les définitions de table pourraient appliquer des valeurs Null, mais ce n'est pas une option dans mon environnement.
La question est donc ... concevez-vous votre requête uniquement pour la vitesse, une priorité plus élevée pour le traitement des transactions qui exécute le même code des milliers de fois par minute. Ou optez-vous pour la précision qu'une jointure externe gauche fournira. N'oubliez pas que les jointures internes doivent trouver des correspondances des deux côtés, de sorte qu'un NULL inattendu supprimera non seulement les données des deux tables mais également des lignes d'informations entières. Et ça se passe si bien, pas de messages d'erreur.
Vous pouvez être très rapide car obtenir 90% des données nécessaires et ne pas découvrir que les jointures internes ont supprimé silencieusement les informations. Parfois, les jointures internes peuvent être plus rapides, mais je ne crois pas que quiconque fasse cette hypothèse à moins d'avoir examiné le plan d'exécution. La vitesse est importante, mais la précision est plus importante.
la source
Vos problèmes de performances sont plus susceptibles d'être dus au nombre de jointures que vous effectuez et si les colonnes sur lesquelles vous vous joignez ont des index ou non.
Dans le pire des cas, vous pourriez facilement effectuer 9 analyses de table entières pour chaque jointure.
la source
Les jointures externes peuvent offrir des performances supérieures lorsqu'elles sont utilisées dans les vues.
Supposons que vous ayez une requête qui implique une vue, et que cette vue se compose de 10 tables réunies. Supposons que votre requête n'utilise que des colonnes de 3 de ces 10 tables.
Si ces 10 tables avaient été jointes ensemble, l'optimiseur de requête devrait les joindre toutes, même si votre requête elle-même n'a pas besoin de 7 sur 10 des tables. En effet, les jointures internes elles-mêmes peuvent filtrer les données, ce qui les rend essentielles pour le calcul.
Si ces 10 tables avaient été jointes à l'extérieur à la place, l'optimiseur de requête ne rejoindrait en fait que celles qui étaient nécessaires: 3 sur 10 dans ce cas. En effet, les jointures elles-mêmes ne filtrent plus les données et les jointures inutilisées peuvent donc être ignorées.
Source: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/
la source
J'ai trouvé quelque chose d'intéressant dans SQL Server en vérifiant si les jointures internes sont plus rapides que les jointures gauches.
Si vous n'incluez pas les éléments de la table jointe gauche, dans l'instruction select, la jointure gauche sera plus rapide que la même requête avec jointure interne.
Si vous incluez la table jointe gauche dans l'instruction select, la jointure interne avec la même requête était égale ou plus rapide que la jointure gauche.
la source
D'après mes comparaisons, je trouve qu'ils ont exactement le même plan d'exécution. Il existe trois scénarios:
Si et quand ils retournent les mêmes résultats, ils ont la même vitesse. Cependant, nous devons garder à l'esprit qu'il ne s'agit pas des mêmes requêtes, et que LEFT JOIN renverra éventuellement plus de résultats (lorsque certaines conditions ON ne sont pas remplies) --- c'est pourquoi c'est généralement plus lent.
Lorsque la table principale (la première non constante dans le plan d'exécution) a une condition restrictive (WHERE id =?) Et que la condition ON correspondante est sur une valeur NULL, la table "droite" n'est pas jointe --- c'est quand LEFT JOIN est plus rapide.
Comme indiqué au point 1, INNER JOIN est généralement plus restrictif et renvoie moins de résultats et est donc plus rapide.
Les deux utilisent (les mêmes) indices.
la source