La vue MySQL effectue-t-elle toujours une analyse complète de la table?
-
10-07-2019 - |
Question
J'essaie d'optimiser une requête qui utilise une vue dans MySQL 5.1. Il semble que même si je sélectionne 1 colonne dans la vue, une analyse complète du tableau est effectuée. Est-ce le comportement attendu?
La vue est simplement un élément SELECT "Toutes les colonnes de ces tables - NON *". pour les tables que j'ai spécifiées dans la première requête ci-dessous.
C’est ce que j’explique à partir de la sélection de la colonne indexée PromotionID de la requête qui constitue la vue. Comme vous pouvez le constater, le résultat est très différent de celui affiché dans la vue.
EXPLAIN SELECT pb.PromotionID FROM PromotionBase pb INNER JOIN PromotionCart pct ON pb.PromotionID = pct.PromotionID INNER JOIN PromotionCode pc ON pb.PromotionID = pc.PromotionID WHERE pc.PromotionCode = '5TAFF312C0NT'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pc
type: const
possible_keys: PRIMARY,fk_pc_pb
key: PRIMARY
key_len: 302
ref: const
rows: 1
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: pb
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: pct
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
Extra: Using index
3 rows in set (0.00 sec)
La sortie lorsque je sélectionne la même chose mais dans la vue
EXPLAIN SELECT vpc.PromotionID FROM vw_PromotionCode vpc WHERE vpc.PromotionCode = '5TAFF312C0NT'\G;
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5830
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: pcart
type: index
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 33
Extra: Using index
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pcart.PromotionID
rows: 1
Extra:
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 5. row ***************************
id: 3
select_type: UNION
table: pp
type: index
possible_keys: PRIMARY
key: pp_p
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 6. row ***************************
id: 3
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pp.PromotionID
rows: 1
Extra:
*************************** 7. row ***************************
id: 3
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 8. row ***************************
id: 4
select_type: UNION
table: pcp
type: index
possible_keys: PRIMARY
key: pcp_cp
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 9. row ***************************
id: 4
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pcp.PromotionID
rows: 1
Extra:
*************************** 10. row ***************************
id: 4
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 11. row ***************************
id: 5
select_type: UNION
table: ppc
type: index
possible_keys: PRIMARY
key: ppc_pc
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 12. row ***************************
id: 5
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.ppc.PromotionID
rows: 1
Extra:
*************************** 13. row ***************************
id: 5
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 14. row ***************************
id: 6
select_type: UNION
table: ppt
type: index
possible_keys: PRIMARY
key: ppt_pt
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 15. row ***************************
id: 6
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.ppt.PromotionID
rows: 1
Extra:
*************************** 16. row ***************************
id: 6
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 17. row ***************************
id: NULL
select_type: UNION RESULT
table: <union2,3,4,5,6>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra:
17 rows in set (0.18 sec)
La solution
Les vues dans MySQL n'étant pas indexées, elles nécessitent par conséquent une analyse complète à chaque accès. En règle générale, cela n’est utile pour Views que dans les cas où vous avez une requête statique assez complexe qui renvoie un petit jeu de résultats et que vous prévoyez d’obtenir le jeu complet à chaque fois.
Éditer: Bien sûr, View utilisera les index des tables sous-jacentes pour optimiser la vue (sinon ils n'auraient aucun sens d'utilisation), mais comme il n'y a pas d'index. Sur une vue, il n'est pas possible d'optimiser une requête WHERE sur la vue.
La construction d’index pour les vues serait coûteuse de toute façon car, bien que je n’ai essayé de profiler aucune vue, je suis à peu près certain qu’une table temporaire est construite en arrière-plan puis que le jeu de résultats est renvoyé. Il faut déjà beaucoup de temps pour construire la table temporaire, je ne voudrais pas d'une vue qui tente également de deviner quels index sont nécessaires. Le deuxième point est le fait que MySQL ne propose pas actuellement de méthode pour spécifier les index à utiliser pour une vue. Comment sait-il quels champs doivent être indexés? Devine-t-il en fonction de votre requête?
Vous pouvez envisager d'utiliser une table temporaire , car vous pouvez ensuite spécifier des index sur les champs de la table temporaire. Cependant, d’expérience, cela a tendance à être très lent.
Si tout ce que cette vue contient est un SELECT ALL FROM table1, table2, table3; alors je devrais demander pourquoi cette requête doit être dans une vue du tout? Si, pour une raison quelconque, cela est absolument nécessaire, vous pouvez utiliser une procédure stockée pour encapsuler la requête. Vous pourrez ainsi optimiser les performances tout en conservant l'avantage d'un appel plus simple à la base de données pour le jeu de résultats.
Autres conseils
J'ai approfondi la question et j'ai manqué un point d'information clé :( Ma requête de vue a en fait une union avec une autre table. Cela oblige la vue à utiliser l'algorithme TEMPORARY TABLE au lieu de l'algorithme MERGE.
L’algorithme TEMPORARY TABLE ne permet pas l’utilisation d’index dans les tables sous-jacentes.
Cela semble être un bogue dans MySQL et cela a été rapporté en 2006, mais cela ne semble pas avoir été résolu en 2009! http://forums.mysql.com/read.php?100,56681, 56681
On dirait que je vais devoir réécrire la requête en tant que jointure externe.