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 telle que:
WHERE id IN (SELECT id FROM ...)
La requête refactorisée s'exécute environ 100 fois plus rapidement. (~ 50 secondes à ~ 0.3) Je m'attendais à une amélioration, mais quelqu'un peut-il expliquer pourquoi elle était si radicale? Les colonnes utilisées dans la clause where étaient toutes indexées. SQL exécute-t-il la requête dans la clause where une fois par ligne ou quelque chose d'autre?
Mise à jour - Expliquez les résultats:
La différence se trouve 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
Une "sous-requête corrélée" (c'est-à-dire une dans laquelle la condition where dépend des valeurs obtenues à partir des lignes de la requête contenant) sera exécutée une fois pour chaque ligne. Une sous-requête non corrélée (une dans laquelle la condition où est indépendante de la requête qui le contient) sera exécutée une fois au début. Le moteur SQL fait cette distinction automatiquement.
Mais, oui, explique-plan te donnera les détails sales.
Vous exécutez la sous-requête ne fois pour chaque ligne alors que la jointure a lieu sur des index.
Voici un exemple de la façon dont les sous-requêtes sont évaluées dans MySQL 6. .
Le nouvel optimiseur convertira ce type de sous-requêtes en jointures.
Lancez l'explique-plan sur chaque version, il vous dira pourquoi.
avant que les requêtes ne soient exécutées sur l'ensemble de données, elles sont soumises à un optimiseur de requête, ce dernier tente d'organiser la requête de manière à pouvoir supprimer autant de tuples (lignes) du jeu de résultats aussi rapidement que possible. Souvent, lorsque vous utilisez des sous-requêtes (en particulier des mauvaises), les n-uplets ne peuvent pas être supprimés du jeu de résultats tant que la requête externe n'a pas commencé à s'exécuter.
Sans voir la requête, il est difficile de dire ce qui était si mauvais à propos de l'original, mais je suppose que c'est quelque chose que l'optimiseur ne pourrait tout simplement pas améliorer. Si vous lancez 'explique', vous découvrirez la méthode d'optimisation pour récupérer les données.
Cette question est quelque peu générale, alors voici une réponse générale:
Fondamentalement, les requêtes prennent plus de temps lorsque MySQL doit traiter des tonnes de lignes.
Faire ceci:
Exécutez un EXPLAIN sur chacune des requêtes (celle qui a été jointe puis celle qui a été soumise), et affichez les résultats ici.
Je pense que voir la différence dans l'interprétation de ces requêtes par MySQL serait une expérience d'apprentissage pour tout le monde.
La sous-requête où doit exécuter 1 requête pour chaque ligne renvoyée. La jointure interne doit juste exécuter 1 requête.
Examinez le plan de requête pour chaque requête.
Où dans et Joindre peut généralement être mis en œuvre en utilisant le même plan d’exécution, donc généralement il n’y a aucune accélération entre les changements.
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.
C'est généralement parce que l'optimiseur ne peut pas déterminer 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 rejoindre la table de la sous-requête par rapport à la table que vous interrogez. Certaines des bases de données les plus "entreprises" sont meilleures à cet égard, mais elles la manquent encore parfois.
La sous-requête était probablement en train d'exécuter une "analyse complète de la table". En d'autres termes, ne pas utiliser l'index et renvoyer trop de lignes que la requête Where de la requête principale avait besoin de filtrer.
Juste une supposition sans détails bien sûr mais c'est la situation commune.
Tiré du Manuel de référence ( 14.2.10.11 Réécriture de sous-requêtes en jointures ):
Une jointure gauche [OUTER] JOIN peut être plus rapide qu'une sous-requête équivalente car le serveur pourrait mieux l'optimiser, ce qui n'est pas spécifique au serveur MySQL.
Les sous-requêtes peuvent donc être plus lentes que LEFT [OUTER] JOINS.
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 une 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 vous pouvez désormais joindre les résultats, et rejoindre les relations est ce à quoi une base de données est censée être bonne. Par exemple, l'optimiseur peut peut-être savoir comment tirer meilleur parti d'un index maintenant.
Ce n'est pas tant la sous-requête que la clause IN, bien que les jointures soient au moins le fondement du moteur SQL d'Oracle et s'exécutent extrêmement rapidement.