J'ai refactoré une section lente d'une application héritée d'une autre société pour utiliser une jointure interne au lieu d'une sous-requête comme:
WHERE id IN (SELECT id FROM ...)
La requête refactorisée s'exécute environ 100 fois plus vite. (~ 50 secondes à ~ 0,3) Je m'attendais à une amélioration, mais est-ce que quelqu'un peut expliquer pourquoi c'était si radical? Les colonnes utilisées dans la clause where ont toutes été indexées. SQL exécute-t-il la requête dans la clause where une fois par ligne ou quelque chose?
Mise à jour - Expliquez les résultats:
La différence se situe dans la deuxième partie de la requête "where id in ()" -
2 DEPENDENT SUBQUERY submission_tags ref st_tag_id st_tag_id 4 const 2966 Using where
vs 1 ligne indexée avec la jointure:
SIMPLE s eq_ref PRIMARY PRIMARY 4 newsladder_production.st.submission_id 1 Using index
sql
mysql
performance
database-design
join
Palmsey
la source
la source
Réponses:
Une "sous-requête corrélée" (c'est-à-dire une dans laquelle la condition où dépend des valeurs obtenues à partir des lignes de la requête contenant) s'exécutera une fois pour chaque ligne. Une sous-requête non corrélée (une dans laquelle la condition where est indépendante de la requête contenant) s'exécutera une fois au début. Le moteur SQL fait cette distinction automatiquement.
Mais, ouais, expliquer-plan vous donnera les détails sales.
la source
DEPENDENT SUBQUERY
signifie exactement la même chose que "sous-requête corrélée".Vous exécutez la sous-requête une fois pour chaque ligne alors que la jointure se produit sur les index.
la source
EXPLAIN
ditDEPENDENT SUBQUERY
, ce qui est l'indicateur le plus clair de ce comportement.Voici un exemple de la façon dont les sous-requêtes sont évaluées dans MySQL 6.0 .
Le nouvel optimiseur convertira ce type de sous-requêtes en jointures.
la source
Exécutez le plan d'explication sur chaque version, il vous dira pourquoi.
la source
avant que les requêtes ne soient exécutées sur l'ensemble de données, elles sont soumises à un optimiseur de requête, l'optimiseur tente d'organiser la requête de telle sorte qu'il puisse supprimer autant de tuples (lignes) de l'ensemble de résultats aussi rapidement que possible. Souvent, lorsque vous utilisez des sous-requêtes (en particulier les mauvaises), les tuples ne peuvent pas être supprimés du jeu de résultats tant que la requête externe ne démarre pas.
Sans voir la requête, il est difficile de dire ce qui était si mauvais à propos de l'original, mais je suppose que c'était quelque chose que l'optimiseur ne pouvait tout simplement pas faire beaucoup mieux. L'exécution de «Expliquer» vous montrera la méthode des optimiseurs pour récupérer les données.
la source
Regardez le plan de requête pour chaque requête.
Where in et Join peuvent généralement être implémentés en utilisant le même plan d'exécution, il n'y a donc généralement aucune accélération de changement entre eux.
la source
Optimizer n'a pas fait un très bon travail. Habituellement, ils peuvent être transformés sans aucune différence et l'optimiseur peut le faire.
la source
Habituellement, c'est le résultat de l'incapacité de l'optimiseur à comprendre que la sous-requête peut être exécutée en tant que jointure, auquel cas il exécute la sous-requête pour chaque enregistrement de la table plutôt que de joindre la table dans la sous-requête par rapport à la table que vous interrogez. Certaines des bases de données les plus «d'entreprise» sont meilleures dans ce domaine, mais elles la manquent encore parfois.
la source
Cette question est quelque peu générale, voici donc une réponse générale:
Fondamentalement, les requêtes prennent plus de temps lorsque MySQL a des tonnes de lignes à trier.
Faites ceci:
Exécutez un EXPLAIN sur chacune des requêtes (celle jointe, puis la sous-requête) et publiez les résultats ici.
Je pense que voir la différence dans l'interprétation de MySQL de ces requêtes serait une expérience d'apprentissage pour tout le monde.
la source
La sous-requête where doit exécuter 1 requête pour chaque ligne renvoyée. La jointure interne doit simplement exécuter 1 requête.
la source
La sous-requête exécutait probablement une "analyse complète de la table". En d'autres termes, ne pas utiliser l'index et renvoyer beaucoup trop de lignes que le lieu de la requête principale avait besoin de filtrer.
Juste une supposition sans détails bien sûr mais c'est la situation courante.
la source
Avec une sous-requête, vous devez réexécuter le 2nd SELECT pour chaque résultat, et chaque exécution renvoie généralement 1 ligne.
Avec une jointure, le 2nd SELECT renvoie beaucoup plus de lignes, mais vous ne devez l'exécuter qu'une seule fois. L'avantage est que maintenant vous pouvez vous joindre aux résultats, et joindre des relations est ce à quoi une base de données est censée être bonne. Par exemple, l'optimiseur peut peut-être trouver comment tirer le meilleur parti d'un index maintenant.
la source
Ce n'est pas tant la sous-requête que la clause IN, bien que les jointures soient au moins à la base du moteur SQL d'Oracle et s'exécutent extrêmement rapidement.
la source
Extrait du Manuel de référence ( 14.2.10.11 Réécriture des sous-requêtes en jointures ):
Ainsi, les sous-requêtes peuvent être plus lentes que LEFT [OUTER] JOINS.
la source