Existe-t-il une différence d'exécution entre une condition JOIN et une condition WHERE?

17

Existe-t-il une différence de performances entre ces deux exemples de requêtes?

Requête 1:

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y'

Requête 2;

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
   and b.tag = 'Y'

Notez que la seule différence est le placement de la condition supplémentaire; le premier utilise une WHEREclause et le second ajoute la condition à la ONclause.

Lorsque j'exécute ces requêtes sur mon système Teradata, les plans d'explication sont identiques et l'étape JOIN affiche la condition supplémentaire dans chaque cas. Cependant, sur cette question SO concernant MySQL, l'une des réponses a suggéré que le deuxième style est préféré car le WHEREtraitement se produit après les jointures.

Y a-t-il une règle générale à suivre lors du codage de requêtes comme celle-ci? Je suppose que cela doit dépendre de la plate-forme car cela ne fait évidemment aucune différence sur ma base de données, mais c'est peut-être juste une fonctionnalité de Teradata. Et si cela dépend de la plate - forme, j'aimerais beaucoup obtenir quelques références de documentation; Je ne sais vraiment pas quoi chercher.

BellevueBob
la source
9
Cela dépend de la plateforme, car cela dépend de la façon dont l'optimiseur RDBMSes traite l'analyse et l'optimisation.
Philᵀᴹ
8
Et cette réponse dans la question liée mérite plusieurs downvotes. Même l'optimiseur primitif de MySQL comprendrait que ces requêtes simples sont équivalentes et que "la clause WHERE est évaluée après que toutes les jointures ont été effectuées" n'est vraie qu'à un niveau logique, pas en cours d'exécution.
ypercubeᵀᴹ
1
Pas vraiment un doublon; cette question et les réponses comparaient la syntaxe JOIN "implicite" et "explicite". Je pose des questions spécifiques sur les conditions d'adhésion supplémentaires.
BellevueBob
Je n'oserai pas poster de réponse comme je l'ai essayé auparavant et j'ai obtenu beaucoup de votes négatifs. Quand il y a beaucoup de jointures, j'ai des cas d'expérience de remontée de la condition dans la jointure qui ont abouti à un meilleur plan de requête (il a été filtré tôt). Toujours les mêmes résultats.
paparazzo

Réponses:

14

Selon le chapitre 9 (analyseur et optimiseur), page 172 du livre Comprendre les composants internes de MySQL par Sasha Pachev

Comprendre les internes de MySQL

voici la répartition de l'évaluation d'une requête selon les tâches suivantes:

  • Déterminez quelles clés peuvent être utilisées pour récupérer les enregistrements des tables et choisissez la meilleure pour chaque table.
  • Pour chaque table, décidez si une analyse de table est meilleure que la lecture sur une clé. S'il y a beaucoup d'enregistrements qui correspondent à la valeur de la clé, les avantages de la clé sont réduits et l'analyse de la table devient plus rapide.
  • Déterminez l'ordre dans lequel les tables doivent être jointes lorsque plusieurs tables sont présentes dans la requête.
  • Réécrivez les clauses WHERE pour éliminer le code mort, en réduisant les calculs inutiles et en changeant les contraintes autant que possible pour ouvrir la voie à l'utilisation des clés.
  • Éliminez les tables inutilisées de la jointure.
  • Déterminez si les clés peuvent être utilisées pour ORDER BYet GROUP BY.
  • Essayez de simplifier les sous-requêtes et déterminez dans quelle mesure leurs résultats peuvent être mis en cache.
  • Fusionner les vues (développer la référence de vue en tant que macro)

Sur cette même page, il dit ce qui suit:

Dans la terminologie de l'optimiseur MySQL, chaque requête est un ensemble de jointures. Le terme jointure est utilisé ici plus largement que dans les commandes SQL. Une requête sur une seule table est une jointure dégénérée. Bien que nous ne pensions normalement pas lire les enregistrements d'une table comme une jointure, les mêmes structures et algorithmes utilisés avec les jointures conventionnelles fonctionnent parfaitement pour résoudre la requête avec une seule table.

ÉPILOGUE

En raison des clés présentes, de la quantité de données et de l'expression de la requête, les jointures MySQL peuvent parfois faire des choses pour notre propre bien (ou pour nous revenir) et produire des résultats que nous ne nous attendions pas et ne pouvons pas expliquer rapidement.

J'ai écrit sur cette bizarrerie avant

parce que MySQL Query Optimizer pourrait faire ignorer certaines clés lors de l'évaluation de la requête.

Le commentaire de @ Phil m'aide à voir comment publier cette réponse (+1 pour le commentaire de @ Phil)

Le commentaire de @ ypercube (+1 pour celui-ci aussi) est une version compacte de mon article car l'optimiseur de requêtes de MySQL est primitif. Malheureusement, cela doit être le cas puisqu'il s'agit de moteurs de stockage extérieurs.

CONCLUSION

En ce qui concerne votre question réelle, l'optimiseur de requêtes MySQL déterminerait les mesures de performances de chaque requête lorsqu'elle sera effectuée

  • compter les lignes
  • sélection des clés
  • masser les jeux de résultats intermittents
  • Oh ouais, faire le REJOINDRE réel

Vous devrez probablement contraindre l'ordre d'exécution en réécrivant (refactorisant) la requête

Voici la première requête que vous avez donnée

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y';

Essayez de le réécrire pour évaluer le WHERE en premier

select count(*)
from   table1 a
join   (select key_col from table2 where tag='Y') b
on     b.key_col=a.key_col;

Cela modifierait certainement le plan EXPLAIN. Cela pourrait produire des résultats meilleurs ou pires.

J'ai déjà répondu à une question dans StackOverflow où j'ai appliqué cette technique. L'EXPLAIN était horrible mais la performance était de la dynamite. Cela n'a fonctionné qu'en raison de la présence des index corrects et de l'utilisation de LIMIT dans une sous-requête .

Comme pour les cours des actions, lorsqu'il s'agit de requêtes et d'essayer de les exprimer, des restrictions s'appliquent, les résultats peuvent varier et les performances passées ne sont pas indicatives des résultats futurs.

RolandoMySQLDBA
la source
2
+1 pour les informations détaillées spécifiques à MySQL et surtout pour m'avoir incité à apprendre la différence entre "Epilogue" et "Conclusion"!
BellevueBob
Dans mon article, l'épilogue est une sous-conclusion.
RolandoMySQLDBA
6
@Rolando: Vous pouvez ajouter un Aftermath sur les améliorations des optimiseurs dans les dernières versions de MariaDB (5.3 et 5.5) et dans la version principale de MySQL (5.6) récemment publiée. Ce qui peut rendre la réécriture inutile.
ypercubeᵀᴹ
1

Pour Oracle, étant donné que mySQL avait une longue description, nous avons deux façons de tirer parti de l'optimiseur.

Le premier est l'optimisation basée sur les règles (ou RBO). Oracle dispose de 15 règles prédéfinies que chaque requête qu'il analyse tente de suivre dans un ordre défini. S'il ne peut pas générer de requête optimisée à partir de la règle 1, il avancera vers la règle 2 et ensuite jusqu'à ce qu'il atteigne la règle 15.

pour plus d'informations: https://docs.oracle.com/cd/B10500_01/server.920/a96533/rbo.htm

Ceux-ci affectent les noyaux Oracle RDBMS à partir de 11.1 et inférieurs qui n'ont pas été convertis en Cost Based Optimizer (aka CBO). Oracle 11.2 et les versions ultérieures nécessitent l'optimiseur CBO, mais peuvent forcer des ID SQL spécifiques à optimiser dans l'ancienne méthode RBO si l'utilisateur le souhaite.

Le CBO pour Oracle 11.1+ effectue à la place plusieurs plans d'exécution pour le même ID SQL et exécute celui dont le coût global est le moins attendu. Il exploite une grande partie de la logique de RBO, mais analyse les statistiques des tableaux pour créer des coûts de plan d'exécution dynamiques pour chaque opération que la base de données doit faire pour fournir à l'utilisateur final ses données. L'exécution d'analyses de table complètes sur de très grandes tables est très coûteuse; exécuter des analyses de table complètes sur une table à 10 lignes est bon marché. En RBO, ces opérations étaient considérées comme des opérations égales.

pour plus d'informations: https://oracle-base.com/articles/misc/cost-based-optimizer-and-database-statistics

Pour votre exemple de requête spécifique: Oracle analyserait probablement les informations pour faire différents plans d'exécution et donc l'un sera techniquement meilleur que l'autre. Cependant, cela peut être une différence minime. À l'œil nu, Oracle RBO et CBO souhaiteraient la requête 1 de plus, car elle s'exécute sur une jointure dans moins de conditions, puis filtre une colonne spécifique de la table temporaire créée à partir de la jointure.

JB-Learner
la source
1

Si vous avez deux requêtes et que vous pensez qu'elles sont équivalentes, les événements suivants peuvent se produire:

  1. Les deux requêtes ont le même plan d'exécution. C'est bien et c'est ce que nous attendons. Espérons qu'il s'agit du plan d'exécution optimal pour la requête.
  2. il existe différents plans d'exécution. Nous avons deux sous-cas ici.

    2.1 Les requêtes ont des plans d'exécution différents mais les deux plans fonctionnent tout aussi bien. C'est bien aussi. Il n'est pas nécessaire que pour des requêtes équivalentes, le même plan soit généré. Mais les performances doivent être égales. Et encore une fois, nous espérons que c'est le meilleur possible.

    2.2 Les requêtes ont des plans d'exécution différents et un plan est meilleur que l'autre. Encore une fois, nous avons des sous-cas:

    2.2.1 Les plans sont différents car les requêtes ne sont pas équivalentes. Vérifiez donc attentivement si elles sont vraiment équivalentes. Dans votre cas, ils sont vraiment équivalents.

    2.2.2 Les plans sont différents mais les requêtes sont équivalentes. Cela signifie que l'optimiseur n'est pas suffisamment mûr. Dans un monde parfait avec des optimiseurs parfaits, cela ne devrait pas se produire. Alors oui, cela dépend de la plateforme et vous devez étudier des documents spécifiques à la plateforme pour savoir pourquoi cela se produit.

    2.2.3 Les plans sont différents, les requêtes sont équivalentes, le logiciel de base de données a un bug.

miracle173
la source