IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes MySQL Discussion :

Optimisation d'une requete avec jointure


Sujet :

Requêtes MySQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre à l'essai
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 6
    Par défaut Optimisation d'une requete avec jointure
    Bonjour,

    la situation :
    j'ai 2 tables qui enregistre des produits et leurs propriétés : cat_product(id_product,libelle) et cat_product_additionnal_properties(id_product,id_champs,value,lang)
    chaque produit à environ 10 à 20 propriétés additionnelles.
    La table cat_product_famille enregistre 1 produit dans 1 famille.

    La table cat_product contient 20 000 lignes et la table cat_product_additionnal_properties contient 320 000 lignes.

    Je souhaite afficher sur une page tous les produits d'une famille avec leurs propriétés, le tout listé dans un tableau, mais je me heurte à des temps d'execution de ma requete trop élevé lorsqu'il y a plus de 400 produits dans une famille.

    Et maintenant voici un exemple de requête, cet exemple est particulier car pour cette famille (qui contient 12 000 produits) je dois grouper mes résultats par marque et modèle, qui sont 2 propriétés additionnelles du produit, pour éviter d'afficher 12 000 produits (le group by réduit a 800)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT cat_product.id_product, i18n.traduction_fr, addpropMarque.value AS marque,addpropModele.value AS modele 
    FROM cat_product_famille 
    LEFT JOIN cat_product ON cat_product_famille.id_product=cat_product.id_product 
    LEFT JOIN i18n ON i18n.shortcut=cat_product.libelle 
    LEFT JOIN cat_product_additionnal_properties addpropMarque ON addpropMarque.id_product=cat_product.id_product 
    LEFT JOIN cat_product_additionnal_properties addpropModele ON addpropModele.id_product=cat_product.id_product 
    WHERE addpropMarque.id_champs=133 AND 
    addpropMarque.lang="fr" AND 
    addpropModele.id_champs=59 AND 
    addpropModele.lang="fr" AND 
    cat_product_famille.id_famille=156 
    GROUP BY marque,modele
    Actuellement cette requete dépasse le délai d'execution. Je cherche donc un moyen d'optimiser cette requete. Est-ce que la logique de ma requête est mauvaise ? Est-ce qu'il y a un moyen d'eviter les jointures ?...
    Merci par avance pour votre aide.

    (Remarque: je bosse sur un CMS payant, la structure de la bdd n'est pas modifiable)

  2. #2
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Par défaut
    Bonjour,


    Déjà votre group by est mal écrit, en effet vous n'avez que 2 colonnes sur les 4 du select qui sont spécifiés dans la clause group by.

    Ensuite si vous n'utilisez pas de fonction d'agrégation (count, min, etc) c'est un distinct qu'il faut utiliser.


    Point suivant, vous avez 3 left outer join qui vont être transformés en inner join car vous mettez dans la clause where des restrictions sur ces tables : utilité des left outer join ?


    Mise à part ça, il faudrait vérifier que vous avez des index sur toutes les clauses de jointure.

    Quand tout ceci sera vérifier et si c'est toujours lent il faudra sans doute présenter l'explain de la requête pour qu'une personne puisse l'analiser et proposer une solution (ou pas c'est peut-être le temps de fetch du curseur qui est lent)

  3. #3
    Expert éminent
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 818
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 818
    Billets dans le blog
    14
    Par défaut
    1) GROUP BY s'utilise avec des fonctions de regroupement (COUNT, SUM, AVG, MIN, MAX).
    Du fait de ton GROUP BY, id_product et traduction_fr seront aléatoires.

    2) Mettre une condition de restriction dans le WHERE sur une table externe d'une jointure externe revient à faire une jointure interne.
    Voir mon blog pour plus d'explication.

    En l'occurence, je crois que des jointures internes seraient ici suffisantes non ?

    3) Si ta requête est longue, commence par vérifier si les tables sont bien indexées, notamment sur les colonnes figurant dans les conditions de jointure. La plupart sont des id donc en principe des clés primaires et étrangères et il y a de fortes chances qu'elles soient indexées.
    Par contre, la table i18n est plus inquiétante ! Une jointure sur un libellé est contre performant.

    4) L'utilisation systématique d'alias courts rend la lecture de la requête plus facile.

    Dis-nous plus précisément ce que tu cherches à obtenir parce que ton GROUP BY est incohérent.

    EDIT : Grilled by Punkoff !
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole, en retraite... mais toujours Autoentrepreneur à l'occasion.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  4. #4
    Membre à l'essai
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 6
    Par défaut
    bonjour,

    merci à vous 2 pour vos réponses rapides, je reviens sur mon problèmes (après un passage sur un autre projet + les vacances qui sont passées par là).

    J'ai pris note de vos précieuses remarques sur le group by et le left join.
    J'ai refait quelques tests aujourd'hui en simplifiant ma requête

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT cat_product.id_product, addpropMarque.value AS marque, addpropModele.value AS modele, addpropVue.value AS vue
    FROM cat_product_famille 
    INNER JOIN cat_product ON cat_product_famille.id_product=cat_product.id_product 
    INNER JOIN i18n ON i18n.shortcut=cat_product.libelle 
    INNER JOIN cat_product_additionnal_properties addpropMarque ON addpropMarque.id_product=cat_product.id_product 
    INNER JOIN cat_product_additionnal_properties addpropModele ON addpropModele.id_product=cat_product.id_product 
    INNER JOIN cat_product_additionnal_properties addpropVue ON addpropVue.id_product=cat_product.id_product 
    WHERE 
    addpropMarque.id_champs=133 AND addpropMarque.lang="fr" 
    AND addpropModele.id_champs=59 AND addpropModele.lang="fr" 
    AND addpropVue.id_champs=132 AND addpropVue.lang="fr" 
    AND cat_product_famille.id_famille=156
    cette requete doit me resortir 14 976 lignes mais le délai d'execution est beaucoup trop long (si je fais qu'une seule jointure sur la table cat_product_additionnal_properties il n'y a pas de pb)

    Les index sont bien créé sur les différents champs de jointure (même sur i18n.shortcut)

    voici le explain de la requete :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    id 	select_type 	table 	type 	possible_keys 	key 	key_len 	ref 	rows 	Extra
    1 	SIMPLE 	addpropMarque 	ALL 	NULL 	NULL 	NULL 	NULL 	320206 	Using where
    1 	SIMPLE 	cat_product 	eq_ref 	PRIMARY,id_product 	PRIMARY 	4 	dbsymtapiecess3030com.addpropMarque.id_product 	1 	 
    1 	SIMPLE 	i18n 	ref 	shortcut 	shortcut 	767 	dbsymtapiecess3030com.cat_product.libelle 	1 	Using index
    1 	SIMPLE 	cat_product_famille 	ref 	id_product,id_famille 	id_product 	4 	dbsymtapiecess3030com.addpropMarque.id_product 	1 	Using where
    1 	SIMPLE 	addpropModele 	ALL 	NULL 	NULL 	NULL 	NULL 	320206 	Using where
    1 	SIMPLE 	addpropVue 	ALL 	NULL 	NULL 	NULL 	NULL 	320206 	Using where

  5. #5
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Par défaut
    Que veut dire long ?

    Sinon que donne cette requête ?

    edit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    SELECT cat_product.id_product, 
    max(case when addprop.id_champs = 133 then addprop.value end) AS marque, 
    max(case when addprop.id_champs = 59 then addprop.value end) AS modele, 
    max(case when addprop.id_champs = 132 then addprop.value end) AS vue
    FROM cat_product_famille 
    INNER JOIN cat_product ON cat_product_famille.id_product=cat_product.id_product 
    INNER JOIN i18n ON i18n.shortcut=cat_product.libelle 
    INNER JOIN cat_product_additionnal_properties addprop ON addprop.id_product=cat_product.id_product 
    WHERE 
    addprop.id_champs in (133, 59, 132) AND addprop.lang="fr" 
    AND cat_product_famille.id_famille = 156 
    group by cat_product.id_product
    having count(distinct addprop.id_champs) = 3
    edit2 : Sinon essayez aussi avec un de ces deux indexs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    create index idx_addprop_1 on cat_product_additionnal_properties (lang, id_champs, id_product);
    create index idx_addprop_2 on cat_product_additionnal_properties (id_champs, lang, id_product);

  6. #6
    Membre à l'essai
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 6
    Par défaut
    long ça veut dire que la requête dépasse le délai d’exécution.

    ta requête est très efficace, délai de 5 à 6 secondes. Je n'aurais jamais pensé l'écrire de cette manière !

    par contre je ne comprends pas le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    HAVING count(DISTINCT addprop.id_champs) = 3
    peux-tu m'expliquer ?

    merci

  7. #7
    Expert éminent
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 818
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 818
    Billets dans le blog
    14
    Par défaut
    Si on ne met pas le HAVING, du fait de WHERE addprop.id_champs IN (133, 59, 132), les lignes ayant l'un des trois critères seraient pris en compte. Le HAVING permet de dire qu'on ne prend que les cat_product.id_product ayant 3 adddrop.id_champ différents, c'est à dire les 3 qui sont dans le IN.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole, en retraite... mais toujours Autoentrepreneur à l'occasion.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Optimisation d'une requete avec jointure
    Par yann123456 dans le forum Requêtes
    Réponses: 2
    Dernier message: 16/09/2010, 11h02
  2. Réponses: 5
    Dernier message: 06/02/2007, 13h46
  3. Pb sur une requete avec jointure droite
    Par crashyear dans le forum Langage SQL
    Réponses: 3
    Dernier message: 06/02/2007, 08h06
  4. [MySQL] Erreur dans une requête avec jointures
    Par bobic dans le forum Langage SQL
    Réponses: 17
    Dernier message: 03/08/2006, 13h04
  5. optimisation d'une requête avec jointure
    Par champijulie dans le forum PostgreSQL
    Réponses: 8
    Dernier message: 07/07/2005, 09h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo