Mysql
 sql >> Base de données >  >> RDS >> Mysql

L'ordre des champs dans une clause WHERE affecte-t-il les performances de MySQL ?

SQL a été conçu pour être un langage déclaratif et non procédural. L'optimiseur de requête ne doit donc pas considérez l'ordre des prédicats de la clause where pour déterminer comment les appliquer.

Je vais probablement trop simplifier la discussion suivante sur un optimiseur de requête SQL. J'ai écrit il y a un an, dans ce sens (c'était très amusant !). Si vous voulez vraiment approfondir l'optimisation des requêtes modernes, consultez le SQL Tuning , de O'Reilly.

Dans un simple optimiseur de requête SQL, l'instruction SQL est d'abord compilée dans un arbre d'algèbre relationnelle opérations. Ces opérations prennent chacune une ou plusieurs tables en entrée et produisent une autre table en sortie. Scanner est une analyse séquentielle qui lit une table à partir de la base de données. Trier produit un tableau trié. Sélectionner produit une table dont les lignes sont sélectionnées dans une autre table selon une condition de sélection. Projet produit une table avec seulement certaines colonnes d'une autre table. Produit croisé prend deux tables et produit une table de sortie composée de toutes les paires imaginables de leurs lignes.

De manière confuse, la clause SQL SELECT est compilée dans un projet d'algèbre relationnelle , tandis que la clause WHERE se transforme en une algèbre relationnelle Select . La clause FROM se transforme en un ou plusieurs Joins , chacun prenant deux tables et produisant une table. Il existe d'autres opérations d'algèbre relationnelle impliquant l'union d'ensembles, l'intersection, la différence et l'appartenance, mais restons simples.

Cet arbre a vraiment besoin d'être optimisé. Par exemple, si vous avez :

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

avec 5 000 employés dans 500 départements, l'exécution d'un arbre non optimisé produira aveuglément toutes les combinaisons possibles d'un employé et d'un département (un produit croisé ) puis Sélectionner juste la combinaison qui était nécessaire. Le Scan de l'employé produira un tableau de 5 000 enregistrements, le Scan du département produira un tableau de 500 enregistrements, le produit croisé de ces deux tables produira une table de 2 500 000 enregistrements, et la touche Sélectionner sur E.id prendra cette table de 2 500 000 enregistrements et en supprimera tous sauf un, l'enregistrement recherché.

[Les vrais processeurs de requêtes essaieront bien sûr de ne pas matérialiser toutes ces tables intermédiaires en mémoire.]

Ainsi, l'optimiseur de requête parcourt l'arborescence et applique diverses optimisations. L'une consiste à décomposer chaque Select dans une chaîne de Selects , un pour chacun des Sélectionner d'origine conditions de haut niveau, celles et-ed ensemble. (C'est ce qu'on appelle la "forme normale conjonctive".) Ensuite, le plus petit Selects individuel sont déplacés dans l'arbre et fusionnés avec d'autres opérations d'algèbre relationnelle pour en former des plus efficaces.

Dans l'exemple ci-dessus, l'optimiseur appuie d'abord sur Sélectionner sur E.id =123456 en dessous du coûteux Cross Product opération. Cela signifie le produit croisé produit juste 500 lignes (une pour chaque combinaison de cet employé et d'un département). Ensuite, le niveau supérieur Sélectionner for E.dept_id =D.dept_id filtre les 499 lignes indésirables. Pas mal.

S'il y a un index sur le champ id de l'employé, alors l'optimiseur peut combiner le Scan de l'employé avec le Sélectionner sur E.id =123456 pour former un index rapide Lookup . Cela signifie qu'une seule ligne Employee est lue en mémoire à partir du disque au lieu de 5 000. Les choses s'améliorent.

La dernière optimisation majeure consiste à prendre le Sélectionner sur E.dept_id =D.dept_id et combinez-le avec le Cross Product . Cela le transforme en une algèbre relationnelle Equijoin opération. Cela ne fait pas grand-chose en soi. Mais s'il existe un index sur Department.dept_id, alors le Scan séquentiel de niveau inférieur du Département alimentant l'Equijoin peut être transformé en un index très rapide Recherche du dossier de service de notre employé.

Les optimisations moindres impliquent de pousser Project opérations vers le bas. Si le niveau supérieur de votre requête nécessite uniquement E.name et D.name, et que les conditions nécessitent E.id, E.dept_id et D.dept_id, alors le Scan les opérations n'ont pas à créer de tables intermédiaires avec toutes les autres colonnes, ce qui permet d'économiser de l'espace lors de l'exécution de la requête. Nous avons transformé une requête horriblement lente en deux recherches d'index et rien d'autre.

Pour en savoir plus sur la question initiale, disons que vous avez :

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

L'arbre d'algèbre relationnelle non optimisé, une fois exécuté, analyserait les 5 000 employés et produirait, par exemple, les 126 employés du Delaware âgés de plus de 21 ans. L'optimiseur de requête a également une idée approximative des valeurs de la base de données. Il peut savoir que la colonne E.state contient les 14 états dans lesquels l'entreprise est implantée et quelque chose sur les distributions E.age. Donc, d'abord, il voit si l'un ou l'autre des champs est indexé. Si E.state l'est, il est logique d'utiliser cet index pour sélectionner uniquement le petit nombre d'employés que le processeur de requêtes soupçonne d'être dans le Delaware sur la base de ses dernières statistiques calculées. Si seul E.age l'est, le processeur de requêtes décide probablement que cela n'en vaut pas la peine, puisque 96 % de tous les employés ont 22 ans et plus. Donc, si E.state est indexé, notre processeur de requêtes casse le Select et fusionne le E.state ='Delaware' avec le Scan pour en faire un balayage d'index beaucoup plus efficace .

Disons dans cet exemple qu'il n'y a pas d'index sur E.state et E.age. Le combiné Select l'opération a lieu après le "Scan" séquentiel de l'Employé. Est-ce que cela fait une différence quelle condition dans le Sélectionner est fait en premier? Probablement pas grand-chose. Le processeur de requêtes peut les laisser dans l'ordre d'origine dans l'instruction SQL, ou il peut être un peu plus sophistiqué et examiner les dépenses prévues. D'après les statistiques, il trouverait à nouveau que la condition E.state ='Delaware' devrait être plus sélective, donc il inverserait les conditions et le ferait d'abord, de sorte qu'il n'y ait que 126 E.age> 21 comparaisons au lieu de 5 000 . Ou il peut se rendre compte que les comparaisons d'égalité de chaînes sont beaucoup plus coûteuses que les comparaisons d'entiers et laisser l'ordre tel quel.

Quoi qu'il en soit, tout cela est très complexe et il est très peu probable que votre ordre de condition syntaxique fasse une différence. Je ne m'en soucierais pas à moins que vous n'ayez un réel problème de performances et que votre fournisseur de base de données utilise l'ordre des conditions comme indice.