Question

J'ai refactoré une section lente d'une application héritée d'une autre société afin d'utiliser une jointure interne au lieu d'une sous-requête telle que

.
where id in (select id from ... )

La requête refactorisée est environ 100 fois plus rapide. (~ 50 secondes à environ 0.3) Je m'attendais à une amélioration, mais est-ce que quelqu'un peut expliquer pourquoi elle est 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 autre chose?

Mettre à jour - Expliquer les résultats:

La différence se situe dans la deuxième partie de l'option "où id in ()". requête -

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
Était-ce utile?

La solution

Une "sous-requête corrélée" (c'est-à-dire, dans lequel 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 contenue) sera exécutée une fois au début. Le moteur SQL fait cette distinction automatiquement.

Mais oui, Expliquez-le-plan vous donnera les détails sales.

Autres conseils

Vous exécutez la sous-requête une fois pour chaque ligne alors que la jointure a lieu sur des index.

Voici un exemple illustrant comment les sous-requêtes sont évaluées dans MySQL 6.0 .

Le nouvel optimiseur convertira ce type de sous-requêtes en jointures.

Exécutez le plan explicit sur chaque version, il vous expliquera pourquoi.

Avant que les requêtes ne soient exécutées sur l'ensemble de données qui les a transmises via un optimiseur de requête, l'optimiseur 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 avoir vu 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 permettant de récupérer les données.

Habituellement, l’optimiseur ne peut déterminer que la sous-requête peut être exécutée en tant que jointure. Dans ce cas, il exécute la sous-requête pour chaque enregistrement de la table, puis joint la table de la sous-requête à la table que vous avez sélectionnée. interrogent. Certains des plus "entreprise" & la base de données est meilleure à cela, mais il leur manque encore parfois.

Cette question est un peu générale, alors voici une réponse générale:

En principe, les requêtes prennent plus de temps lorsque MySQL doit traiter des tonnes de lignes.

Faites ceci:

Exécutez une commande EXPLAIN sur chacune des requêtes (celle de JOIN, puis celle de la sous-requête), puis publiez 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 simplement exécuter une requête.

Examinez le plan de requête pour chaque requête.

Où dans et Rejoindre pouvez-vous généralement être implémenté à l'aide du même plan d'exécution, de sorte que typiquement , la valeur est nulle accélérez de changer entre eux.

L’optimiseur n’a pas fait du très bon travail. Habituellement, ils peuvent être transformés sans différence et l’optimiseur peut le faire.

La sous-requête était probablement en train d'exécuter un "scan de table complet". 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 courante.

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 pourra peut-être mieux comprendre 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 soient extrêmement rapides.

Extrait du manuel de référence ( 14.2.10.11 Réécriture des sous-requêtes comme Jointures ):

  

UN JOIN [OUTER] LEFT 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top