web-dev-qa-db-fra.com

la requête SQL rejoint plusieurs tables - trop lente (8 tables)

j'essaye de joindre 8 tables en une afin de créer un index utilisé par une autre application, ma requête est comme: (ma compétence mysql est très amateur)

SELECT t1_id, t2_name, t3_name, t4_name, t5_name, 
       t6_name, t7_name, t8_name, t9_name 
FROM t1 
  LEFT JOIN t2 ON (t1_id = t2_id) 
  LEFT JOIN t3 ON (t3_id = t1_id) 
  LEFT JOIN t4 ON (t4_id = t1_id)
  LEFT JOIN t5 ON (t5_id = t1_id)
  LEFT JOIN t6 ON (t6_id = t1_id) 
  LEFT JOIN t7 ON (t7_id = t1_id)
  LEFT JOIN t8 ON (t8_id = t1_id)
  LEFT JOIN t9 ON (t9_id = t1_id)

je ne peux même pas voir les résultats de la requête lorsque je l'exécute, des moyens d'accélérer? :) tout type d'aide est apprécié, mais il vaut mieux ne faire qu'une seule requête (en dehors des règles d'application)

merci d'avance

33
lordlinier

J'ai eu un problème similaire avec plusieurs tables de recherche se joignant à une grande table avec tous les champs id indexés. Pour surveiller l'effet des jointures sur l'exécution du temps de requête, j'ai exécuté ma requête plusieurs fois (en limitant aux 100 premières lignes), en ajoutant à chaque fois une jointure à une table supplémentaire. Après avoir rejoint 12 tables, il n'y a eu aucun changement significatif dans le temps d'exécution des requêtes. Au moment où j'ai rejoint la 13e table, le temps d'exécution est passé à 1 seconde; 14e tableau 4 secondes, 15e tableau 20 s, 16e 90 secondes.

La suggestion de Keijro d'utiliser une sous-requête corrélée au lieu de jointures, par exemple.

SELECT t1_id, 
        (select t2_name from t2 where t1_id = t2_id), 
        (select t3_name from t3 where t1_id = t3_id), 
        (select t4_name from t4 where t1_id = t4_id), 
        (select t5_name from t5 where t1_id = t5_id), 
        (select t6_name from t6 where t1_id = t6_id), 
        (select t7_name from t7 where t1_id = t7_id), 
        (select t8_name from t8 where t1_id = t8_id), 
        (select t9_name from t9 where t1_id = t9_id)  FROM t1

amélioration considérable des performances des requêtes. En fait, les sous-requêtes ne semblaient pas allonger le temps d'exécution de la requête (la requête était presque instante).

Je suis un peu surpris car je pensais que les sous-requêtes corrélées étaient moins performantes que les jointures.

53
Andrew

Selon la quantité de données dans les tables, vous devrez peut-être placer des index sur les colonnes qui sont jointes. Souvent, la vitesse de requête lente se résume à l'absence d'index au bon endroit.

Aussi:

Les jointures gauches sont plus lentes que les jointures internes (bien que cela dépende de ce que vous faites exactement) - pouvez-vous accomplir ce que vous recherchez avec des jointures internes?

29
Nathan Ridley

Cela aiderait un peu si vous pouviez poster le plan d'explication de la requête.

Mais, tout d'abord, vous avez des index sur tous les champs utilisés dans la jointure? quelque chose comme CREATE INDEX ix_t2_id on t2 (t2_id, t2_name);

Au lieu des jointures, vous pourriez faire quelque chose comme

SELECT t1_id, 
    (select t2_name from t2 where t1_id = t2_id), 
    (select t3_name from t3 where t1_id = t3_id), 
    (select t4_name from t4 where t1_id = t4_id), 
    (select t5_name from t5 where t1_id = t5_id), 
    (select t6_name from t6 where t1_id = t6_id), 
    (select t7_name from t7 where t1_id = t7_id), 
    (select t8_name from t8 where t1_id = t8_id), 
    (select t9_name from t9 where t1_id = t9_id) 
FROM t1 

Mais, avec un bon planificateur de requêtes, cela ne devrait pas différer des jointures.

5
Jimmy Stenke

De combien de données parlons-nous? Il se peut que vous ayez beaucoup de données et que la clause where est exécutée à la fin du processus de requête, vous joignez d'énormes volumes de données avant de les filtrer.

Dans ce cas, il vaut mieux filtrer les données dès que possible, donc si vous pouvez restreindre les données de T1 dans la première sélection interne, toutes les autres jointures se joindront à un ensemble de données plus limité.

Select <your fields> from
(
Select * from t1 where t1_id = t1_value
) t1

Inner join t2
on t1.ID = t2.ID
...

si ce n'est pas des masses de données; vérifiez que vos index sont corrects puis vérifiez les choses de type serveur; fragmentation d'index; files d'attente de disques, etc.

5
u07ch

À partir de votre plan de requête, je peux conclure que les tables appelées s, n et q n'ont pas d'index sur le champ sur lequel elles sont jointes.

Puisqu'il y a beaucoup de lignes dans ces tableaux (environ 400,000 lignes dans leur produit cartésien) et MySQL est la seule façon de faire JOIN est d'utiliser NESTED LOOPS, cela prendra vraiment une éternité.

Créez un index sur ces tables ou définissez le champ joint comme PRIMARY KEY.

1
Quassnoi

Si vous avez besoin de toutes les lignes de t1 et que vous avez laissé join sur la clé primaire (je suppose que c'est aussi l'index cluster) des autres tables, il n'y a aucun moyen d'améliorer la vitesse de la requête.

Pour améliorer les performances, vous devez soit réduire l'ensemble de résultats, soit effectuer une mauvaise opération (par exemple, faire une copie dénormalisée des données).

1

Comme je peux le voir, la table t1 est celle qui est jointe à toutes les tables, au lieu de les mettre dans une seule requête avec autant de jointures, vous pouvez éventuellement essayer une Union de différentes requêtes quelque chose comme ça.

SELECT  t1_id, t2_name 
FROM    t1 LEFT JOIN t2 ON (t1_id = t2_id)
union 
SELECT  t1_id, t3_name 
FROM    t1 LEFT JOIN t3 ON (t1_id = t3_id)

cependant, dans ce cas, le résultat que vous obtiendrez n'aura pas 8 colonnes mais seulement 1 colonne. Je ne sais pas si c'est une option disponible avec vous.

une autre chose, que vous devez dans n'importe quelle solution que vous implémentez est - créer un index approprié sur toutes vos tables. la meilleure pratique des colonnes d'index est de la créer sur la colonne la plus fréquemment utilisée pour les jointures ou la clause where.

0
Vikram