2013-09-01 4 views
3

Cette requête s'exécute plus de 12 secondes, même si toutes les tables sont relativement petites (environ 2 000 lignes).Comment optimiser la jointure qui entraîne une performance très lente

SELECT attr_73206_ AS attr_73270_ 
FROM object_73130_ f1 
LEFT OUTER JOIN (
    SELECT id_field, attr_73206_ FROM (
    SELECT m.id_field, t0.attr_73102_ AS attr_73206_ FROM object_73200_ o 
    INNER JOIN master_slave m ON (m.id_object = 73130 OR m.id_object = 73290) AND (m.id_master = 73200 OR m.id_master = 73354) AND m.id_slave_field = o.id 
    INNER JOIN object_73101_ t0 ON t0.id = o.attr_73206_ 
    ORDER BY o.id_order 
    ) AS o GROUP BY o.id_field 
) AS o ON f1.id = o.id_field 

Les deux tables ont des champs id comme clés primaires. En outre, id_field, id_order, attr_73206_ et tous les champs de master_slave sont indexés. Quant à la logique de cette requête, elle est globalement de type master-detail. Le tableau object_73130_ est une table principale, la table object_73200_ est une table de détail. Ils sont liés par une table master_slave. object_73101_ est une table ad hoc utilisée pour obtenir une valeur réelle pour le champ attr_73206_ par son identifiant. Pour chaque ligne de la table principale, la requête renvoie un champ de la toute première ligne de sa table de détail. Tout d'abord, la requête avait un autre aspect, mais ici, chez stackoverflow, il m'a été conseillé d'utiliser cette structure plus optimisée (au lieu d'une sous-requête utilisée auparavant, et, par ailleurs, la requête a démarré beaucoup plus vite). J'observe que la sous-requête dans le premier bloc JOIN s'exécute très rapidement mais renvoie un nombre de lignes comparable au nombre de lignes dans la table principale principale. De toute façon, je ne sais pas comment l'optimiser. Je me demande simplement pourquoi une simple jointure rapide cause beaucoup de problèmes. Oh, l'observation principale est que si je supprime un object_73101_ ad-hoc de la requête pour retourner juste un ID, mais pas une valeur réelle, alors la requête s'exécute aussi rapidement qu'un flash. Donc, toute l'attention devrait être concentrée sur cette partie de la requête

INNER JOIN object_73101_ t0 ON t0.id = o.attr_73206_ 

Pourquoi ralentit-il si terriblement toute la requête?

EDIT

De cette façon, il fonctionne super rapide

SELECT t0.attr_73102_ AS attr_73270_ 
FROM object_73130_ f1 
LEFT OUTER JOIN (
SELECT id_field, attr_73206_ FROM (
    SELECT m.id_field, attr_73206_ FROM object_73200_ o 
    INNER JOIN master_slave m ON (m.id_object = 73130 OR m.id_object = 73290) AND (m.id_master = 73200 OR m.id_master = 73354) AND m.id_slave_field = o.id 
    ORDER BY o.id_order 
) AS o GROUP BY o.id_field 
) AS o ON f1.id = o.id_field 
LEFT JOIN object_73101_ t0 ON t0.id = o.attr_73206_ 

Ainsi, vous pouvez voir, que je viens de mettre le add-hoc se joindre à l'extérieur de la sous-requête. Mais, le problème est, cette sous-requête est automatiquement créée et j'ai un accès à cette partie de l'algo qui la crée et je peux modifier cet algo, et je n'ai pas accès à la partie d'algo qui construit la requête entière, donc le La seule chose que je peux faire est juste de réparer la sous-requête en quelque sorte. Quoi qu'il en soit, je n'arrive toujours pas à comprendre pourquoi INNER JOIN dans une sous-requête peut ralentir l'ensemble de la requête des centaines de fois.

EDIT

Une nouvelle version de requête avec différents alias pour chaque table. Cela n'a aucun effet sur les performances:

SELECT attr_73206_ AS attr_73270_ 
FROM object_73130_ f1 
LEFT OUTER JOIN (
SELECT id_field, attr_73206_ FROM (
    SELECT m.id_field, t0.attr_73102_ AS attr_73206_ FROM object_73200_ a 
    INNER JOIN master_slave m ON (m.id_object = 73130 OR m.id_object = 73290) AND (m.id_master = 73200 OR m.id_master = 73354) AND m.id_slave_field = a.id 
    INNER JOIN object_73101_ t0 ON t0.id = a.attr_73206_ 
    ORDER BY a.id_order 
) AS b GROUP BY b.id_field 
) AS c ON f1.id = c.id_field 

EDIT

Ceci est le résultat de EXPLAIN commande:

| id | select_type | TABLE | TYPE | possible_keys   |  KEY  | key_len | ROWS | Extra       | 
| 1 | PRIMARY  | f1 | INDEX | NULL     | PRIMARY | 4  | 1570 | USING INDEX 
| 1 | PRIMARY  | derived2| ALL | NULL     | NULL  | NULL | 1564 | 
| 2 | DERIVED  | derived3| ALL | NULL     | NULL  | NULL | 1575 | USING TEMPORARY; USING filesort 
| 3 | DERIVED  | m  | RANGE | id_object,id_master,..| id_object | 4  | 1356 | USING WHERE; USING TEMPORARY; USING filesort 
| 3 | DERIVED  | a  | eq_ref | PRIMARY,attr_73206_ | PRIMARY | 4  | 1 | 
| 3 | DERIVED  | t0  | eq_ref | PRIMARY    | PRIMARY | 4  | 1 | 

Quel est le problème avec ça?

EDIT

Voici le résultat de la commande EXPLAIN pour la "super-rapide" requête

| id | select_type | TABLE | TYPE | possible_keys  |  KEY  | key_len | ROWS | Extra       
| 1 | PRIMARY  | f1 | INDEX | NULL     | PRIMARY | 4  | 1570 | USING INDEX 
| 1 | PRIMARY  | derived2| ALL | NULL     | NULL  | NULL | 1570 | 
| 1 | PRIMARY  | t0  | eq_ref| PRIMARY    | PRIMARY | 4  | 1  | 
| 2 | DERIVED  | derived3| ALL | NULL    | NULL  | NULL | 1581 | USING TEMPORARY; USING filesort 
| 3 | DERIVED  | m  | RANGE | id_object,id_master,| id_bject | 4  | 1356 | USING WHERE; USING TEMPORARY; USING filesort 
| 3 | DERIVED  | a  | eq_ref | PRIMARY    | PRIMARY | 4  | 1 | 

FERME

je vais utiliser mon propre "ultra-rapide" requête, que j'ai présenté ci-dessus. Je pense qu'il est impossible de l'optimiser plus.

+1

. . Votre utilisation du 'order by' dans la sous-requête avant la jointure est hautement suspecte. MySQL ne garantit pas explicitement quelles valeurs seront retournées lorsque vous utiliserez un champ non mentionné dans la clause 'group by' (voir http://dev.mysql.com/doc/refman/5.5/fr/group-by-extensions). html). –

+2

Avez-vous utilisé la fonctionnalité [** EXPLAIN **] (http://dev.mysql.com/doc/refman/5.7/fr/using-explain.html)? –

+0

Si j'enlève ORDER BY a.id_order, cela n'a toujours aucun effet. Comme je l'ai dit, tout le problème est dans l'INNER JOIN object_73101_ etc Événement bien qu'il ne ralentisse pas la sous-requête elle-même, il ralentit terriblement toute la requête – Jacobian

Répondre

1

Sans connaître la nature exacte des données/requête, il y a deux choses que je vois:

  1. MySQL est notoirement mauvaise à la manipulation sous-requêtes, car elle nécessite la création de dérivés les tables. En fait, certaines versions de MySQL ignorent également les index lors de l'utilisation de sous-sélections. Généralement, il est préférable d'utiliser JOINs à la place des sous-sélections, mais si vous avez besoin d'utiliser des sous-sélections, il est préférable de faire en sorte que ce sous-select soit aussi léger que possible. Sauf si vous avez une raison très spécifique de placer ORDER BY dans la sous-sélection, il peut être judicieux de la déplacer dans la partie de requête "principale", car le jeu de résultats peut être plus petit (ce qui permet de tri).

Donc, tout cela étant dit, j'ai essayé de réécrire votre requête en utilisant la logique JOIN, mais je me demandais quelle table la valeur finale (attr_73102_) vient? Est-ce le résultat du sous-select, ou provient-il de la table object_73130_? S'il provient de la sous-sélection, je ne vois pas pourquoi vous dérangez avec LEFT JOIN d'origine, car vous ne retournerez la liste des valeurs que de la sous-sélection, et NULL pour les lignes non-assorties à partir de object_73130_.

Peu importe, ne sachant pas cette réponse, je pense que la requête ci-dessous peuvent être syntaxiquement équivalentes:

SELECT t0.attr_73102_ AS attr_73270_ 
FROM object_73130_ f1 
LEFT JOIN (object_73200_ o 
    INNER JOIN master_slave m ON m.id_slave_field = o.id 
    INNER JOIN object_73101_ t0 ON t0.id = o.attr_73206_) 
ON f1.id = o.id_field 
WHERE m.id_object IN (73130,73290) 
AND m.id_master IN (73200,73354) 
GROUP BY o.id_field 
ORDER BY o.id_order; 
+0

En plus de cela (si je comprends bien votre commentaire initial correctement), vous voulez ajouter les indices suivants (couvrant) si elles n'existent déjà: Sur master_slave: (id_objet, id_master, slave_field), sur object_73200_: (attr_73206_, id_field, id_order) – Chosun

+0

Je ne suis même pas convaincu le 'ORDER BY' dans la sous-sélection va appliquer effectivement la commande, il est certainement préférable de le déplacer à l'extérieur. –

+0

D'accord. Eh bien, je crois qu'il appliquera la commande tant que la table d'origine (object_73130_) n'ajoute pas de nouvelles lignes (dont il va très probablement faire, car il est une jointure gauche). Tant que les données sous-sélectionné est transmis à la LEFT JOIN dans un ordre précis, je crois que le jeu de résultats gardera dans cet ordre, mais je pourrais être tout à fait tort. En fait, il ne me surprendrait pas si elle a l'ordre du principal des données de la table sur le disque, et a effectué la LEFT JOIN après la ORDER BY. De toute façon, je suis entièrement d'accord avec vous que c'est très douteux. – Chosun