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

SELECT avec des variables de requête n'utilisant pas d'INDEX

La raison réside dans l'utilisation de OU conditions dans clause.

Pour illustrer, essayez d'exécuter à nouveau la requête, cette fois avec uniquement le id = 5 condition, et obtenez (EXPLAIN output):

+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| id | select_type | table      | type   | possible_keys      | key     | key_len | ref   | rows | Extra          |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL               | NULL    | NULL    | NULL  |    1 |                |
|  1 | PRIMARY     | tree       | const  | PRIMARY,index_both | PRIMARY | 4       | const |    1 |                |
|  2 | DERIVED     | NULL       | NULL   | NULL               | NULL    | NULL    | NULL  | NULL | No tables used |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+

Et encore une fois, cette fois avec seulement le parent_id = @last_id OR parent_id = 5 condition, et obtenez :

+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| id | select_type | table      | type   | possible_keys   | key  | key_len | ref  | rows | Extra          |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL            | NULL | NULL    | NULL |    1 |                |
|  1 | PRIMARY     | tree       | ALL    | index_parent_id | NULL | NULL    | NULL |   10 | Using where    |
|  2 | DERIVED     | NULL       | NULL   | NULL            | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+

MySQL n'est pas trop doué pour gérer plusieurs index dans la même requête. Les choses sont légèrement meilleures avec les conditions ET ; on est plus susceptible de voir un index_merge optimisation qu'une union d'index optimisation.

Les choses s'améliorent au fur et à mesure que les versions avancent, mais j'ai testé votre requête sur la version 5.5 , qui est à la dernière version de production actuelle, et les résultats sont tels que vous les décrivez.

Pour expliquer pourquoi c'est difficile, considérez :deux index différents répondront pour deux conditions différentes de la requête. On répondra pour id = 5 , l'autre pour parent_id = @last_id OR parent_id = 5 (BTW pas de problème avec le OR à l'intérieur de ce dernier, puisque les deux termes sont gérés à partir du même index).

Il n'y a pas d'index unique qui puisse répondre aux deux, et donc le FORCE INDEX l'instruction est ignorée. Voir, FORCE INDEX dit que MySQL doit utiliser un index sur un parcours de table. Cela n'implique pas qu'il doive utiliser plus d'un index sur un parcours de table.

MySQL suit donc les règles de la documentation ici. Mais pourquoi est-ce si compliqué ? Parce que pour répondre en utilisant les deux index, MySQL doit rassembler les résultats des deux, stocker celui de côté dans un tampon temporaire tout en gérant le second. Ensuite, il doit parcourir ce tampon pour filtrer les lignes identiques (il est possible qu'une ligne corresponde à toutes les conditions). Et ensuite d'analyser ce tampon afin de renvoyer les résultats.

Mais attendez, ce tampon n'est pas indexé en lui-même. Filtrer les doublons n'est pas une tâche évidente. MySQL préfère donc travailler sur la table d'origine et y faire l'analyse, et éviter tout ce gâchis.

Bien sûr, cela est résoluble. Les ingénieurs d'Oracle peuvent encore améliorer cela (récemment, ils ont travaillé dur pour améliorer les plans d'exécution des requêtes), mais je ne sais pas si cela fait partie de la tâche TODO ou s'il a une priorité élevée.