Comment faire en sorte que MySQL utilise un index pour une requête de vue ? La réponse courte, fournissez un index que MySQL peut utiliser.
Dans ce cas, l'indice optimal est probablement un indice "couvrant" :
... ON highscores (player, happened_in, score)
Il est probable que MySQL utilisera cet index, et EXPLAIN affichera :"Using index"
à cause du WHERE player = 24
(un prédicat d'égalité sur la première colonne de l'index. Le GROUP BY happened_id
(la deuxième colonne de l'index), peut permettre à MySQL d'optimiser cela en utilisant l'index pour éviter une opération de tri. Y compris le score
colonne dans l'index permettra à la requête d'être entièrement satisfaite à partir de l'index, sans avoir à visiter (rechercher) les pages de données référencées par l'index.
C'est la réponse rapide. La réponse la plus longue est qu'il est très peu probable que MySQL utilise un index avec une colonne principale de happened_id
pour la requête de vue.
Pourquoi la vue cause un problème de performances
L'un des problèmes que vous rencontrez avec la vue MySQL est que MySQL ne "pousse" pas le prédicat de la requête externe vers la requête de vue.
Votre requête externe spécifie WHERE happened_in = 2006
. L'optimiseur MySQL ne considère pas le prédicat lorsqu'il exécute la "requête de vue" interne. Cette requête pour la vue est exécutée séparément, avant la requête externe. Le jeu de résultats de l'exécution de cette requête est "matérialisé" ; c'est-à-dire que les résultats sont stockés dans une table MyISAM intermédiaire. (MySQL l'appelle une "table dérivée", et ce nom qu'ils utilisent a du sens, quand vous comprenez les opérations que MysQL effectue.)
L'essentiel est que l'index que vous avez défini sur happened_in
n'est pas utilisé par MySQL lorsqu'il exécute la requête qui forme la définition de la vue.
Une fois la "table dérivée" intermédiaire créée, ALORS la requête externe est exécutée, en utilisant cette "table dérivée" comme source de ligne. C'est lorsque cette requête externe s'exécute que le happened_in = 2006
le prédicat est évalué.
Notez que toutes les lignes de la requête de vue sont stockées, ce qui (dans votre cas) est une ligne pour CHAQUE valeur de happened_in
, pas seulement celui pour lequel vous spécifiez un prédicat d'égalité dans la requête externe.
La façon dont les requêtes de vue sont traitées peut être "inattendue" pour certains, et c'est l'une des raisons pour lesquelles l'utilisation de "vues" dans MySQL peut entraîner des problèmes de performances, par rapport à la manière dont les requêtes de vue sont traitées par d'autres bases de données relationnelles.
Améliorer les performances de la requête de vue avec un index de couverture approprié
Compte tenu de votre définition de vue et de votre requête, le mieux que vous obtiendrez serait une méthode d'accès "Utilisation de l'index" pour la requête de vue. Pour obtenir cela, vous auriez besoin d'un index de couverture, par exemple
... ON highscores (player, happened_in, score).
Il s'agit probablement de l'index le plus avantageux (en termes de performances) pour votre définition de vue existante et votre requête existante. Le player
colonne est la colonne principale car vous avez un prédicat d'égalité sur cette colonne dans la requête de vue. Le happened_in
colonne est la suivante, car vous avez une opération GROUP BY sur cette colonne, et MySQL va pouvoir utiliser cet index pour optimiser l'opération GROUP BY. Nous incluons également le score
colonne, car il s'agit de la seule autre colonne référencée dans votre requête. Cela fait de l'index un index "de couverture", car MySQL peut répondre à cette requête directement à partir des pages d'index, sans avoir besoin de visiter les pages de la table sous-jacente. Et c'est aussi bon que nous allons sortir de ce plan de requête :"Utiliser l'index" sans "Utiliser le tri de fichiers".
Comparer les performances d'une requête autonome sans table dérivée
Vous pouvez comparer le plan d'exécution de votre requête à la vue par rapport à une requête autonome équivalente :
SELECT player
, MAX(score) AS highest_score
, happened_in
FROM highscores
WHERE player = 24
AND happened_in = 2006
GROUP
BY player
, happened_in
La requête autonome peut également utiliser un index de couverture, par ex.
... ON highscores (player, happened_in, score)
mais sans avoir besoin de matérialiser une table MyISAM intermédiaire.
Je ne suis pas sûr que l'un des éléments précédents fournisse une réponse directe à la question que vous posiez.
Q :Comment puis-je faire en sorte que MySQL utilise un INDEX pour la requête de vue ?
A :Définissez un INDEX approprié que la requête de vue peut utiliser.
La réponse courte est de fournir un "index de couverture" (l'index inclut toutes les colonnes référencées dans la requête de vue). Les colonnes principales de cet index doivent être les colonnes référencées avec des prédicats d'égalité (dans votre cas, la colonne player
serait une colonne principale parce que vous avez un player = 24
prédicat dans la requête. De plus, les colonnes référencées dans GROUP BY doivent être des colonnes de tête dans l'index, ce qui permet à MySQL d'optimiser le GROUP BY
opération, en utilisant l'index plutôt que d'utiliser une opération de tri.
Le point clé ici est que la requête de vue est essentiellement une requête autonome; les résultats de cette requête sont stockés dans une table "dérivée" intermédiaire (une table MyISAM qui est créée lorsqu'une requête sur la vue est exécutée.
L'utilisation de vues dans MySQL n'est pas nécessairement une "mauvaise idée", mais je voudrais fortement avertir ceux qui choisissent d'utiliser des vues dans MySQL d'être conscients de la façon dont MySQL traite les requêtes qui font référence à ces vues. Et la façon dont MySQL traite les requêtes de vue diffère (considérablement) de la façon dont les requêtes de vue sont gérées par d'autres bases de données (par exemple, Oracle, SQL Server).