PHP|Débutant :: Forums

Advertisement

Besoin d'aide ? N'hésitez pas, mais respectez les règles

Vous n'êtes pas identifié(e).

#1 26-06-2013 11:30:25

Mendoza
Membre
Inscription : 02-03-2011
Messages : 31

Requête sur plusieurs tables : je croyais avoir compris, mais non.

Bonjour !
Me voilà de retour sur ce forum pour implorer de l'aide. Je croyais avoir passé ce cap, mais en fait non...et ça me navre. J'essaye de me consoler en me disant que c'est quand même relativement avancé...
J'explique :
Je reprends mon premier site de cuisine (opérationnel). Je cherche à l'optimiser parce que maintenant qu'il y a un peu de trafic dessus, je tue le serveur. La page la plus vue est naturellement la page "recette.php". Cette page, à partir d'un identifiant recette_id obtenu à partir d'un GET, fait 5 ou 6 requêtes pour afficher toutes les infos utiles : les ingrédients, les ustensiles, les étapes, etc.
Je me dis que "tiens, j'ai du temps à perdre, maintenant que je sais faire, paf, je vais faire tout ça en une seule requête". Et là, c'est le drame : en fait, je ne sais toujours pas faire ça correctement.
Structure de la BDD :
- j'ai une table "principale" appelée recettes qui contient un recette_id et les infos uniques de chaque recette sur une ligne : l'auteur, le nom, la catégorie, etc.
- Toutes les autres tables concernées sont de type "1 à plusieurs" : elles ont toutes une colonne "recette_id" clé étrangère et un "nomDeLaTable_id" unique.
En une seule requête, je souhaite donc choper toutes les infos de toutes les tables concernées pour une recette donnée : les infos de la ligne de la table recettes, les X lignes d'accompagnements, les Y étapes, etc.

Quand je fais :


SELECT recettes.*, photos_recettes.*, recettes_ingredients.*, recettes_accompagnements.*, ustensiles.*, recettes_etapes.*, notes.*, commentaires.*
FROM recettes
LEFT JOIN photos_recettes ON photos_recettes.recette_id = recettes.recette_id
LEFT JOIN recettes_ingredients ON recettes_ingredients.recette_id = recettes.recette_i
LEFT JOIN recettes_accompagnements ON recettes_accompagnements.recette_id = recettes.recette_id
LEFT JOIN ustensiles ON ustensiles.recette_id = recettes.recette_id
LEFT JOIN recettes_etapes ON recettes_etapes.recette_id = recettes.recette_id
LEFT JOIN notes ON notes.recette_id = recettes.recette_id
LEFT JOIN commentaires ON commentaires.recette_id = recettes.recette_id
WHERE recettes.recette_id = '150'
ORDER BY photos_recettes.total_points DESC,
    recettes_ingredients.ingredient_position ASC,
    recettes_accompagnements.accompagnement_position ASC,
    ustensiles.ustensile_position ASC,
    recettes_etapes.etape_position ASC, commentaires.date DESC
 

Il me retourne 7920 résultats pour UNE recette. Ce qui me semble relativement absurde vu qu'il y a en gros une cinquantaine d'infos différentes pour chaque recette (en comptant les étapes, les ingrédients, etc.)
Bref : comment réécrire cette requête pour avoir un résultat cohérent et optimisé pour le serveur ?

Deuxième question :
Dans un monde idéal où je réussis à faire une requête correcte : comment utiliser au mieux le résultat lors de l'affichage de la page ? Comment à partir du résultat de la requête afficher les X ustensiles au bon endroit et les Y étapes à un autre ? Quelle est la technique la plus efficace ?

Question bonus :
Il m'est souvent arrivé de devoir utiliser plusieurs fois le résultat d'une requête sur une même page.  Quel est le meilleur moyen pour ce faire ? Une fois le résultat utilisé dans un while, on ne peut plus le réutiliser ailleurs. Faut-il mettre le résultat de la requête dans un premier tableau ? Ou faire un reset($requete) ? Ou utiliser mysql_data_seek() ? Ou quoi que ce soit d'autre ?

Voilà voilà.
Merci de ne pas me jeter de cailloux.

Edit : à l'attention de MK : oui je sais, il ne faut pas mettre de * dans une requête mais là c'est juste pour raccourcir un peu sinon il y a trop de champs...

Dernière modification par Mendoza (26-06-2013 11:31:40)

Hors ligne

#2 26-06-2013 12:36:44

Maljuna Kris
Infantimigulo
Lieu : Douarnenez 29100 Breizh Izel
Inscription : 08-05-2009
Messages : 2 453
Site Web

Re : Requête sur plusieurs tables : je croyais avoir compris, mais non.

Mendoza a écrit :

Je cherche à l'optimiser parce que maintenant qu'il y a un peu de trafic dessus, je tue le serveur.

Edit : à l'attention de MK : oui je sais, il ne faut pas mettre de * dans une requête mais là c'est juste pour raccourcir un peu sinon il y a trop de champs...

Tout faux, les tables n'ont pas de champs mais des colonnes, si ta requête comporte vraiment des étoiles sur le site c'est contre-productif pour soulager le serveur.

Mendoza a écrit :

Il me retourne 7920 résultats pour UNE recette. Ce qui me semble relativement absurde vu qu'il y a en gros une cinquantaine d'infos différentes pour chaque recette (en comptant les étapes, les ingrédients, etc.)
Bref : comment réécrire cette requête pour avoir un résultat cohérent et optimisé pour le serveur ?

En fait, vu que les jointures se font toutes vers la table recettes, il n'y a pas de raison qu'il te retourne plus de lignes que celles de la table qui comporte le plus de liaisons vers recette_id = 150, au passage, vu que recette_id est probablement numérique (j'espère même en auto-increment dans la table recettes) il n' y a pas lieu d'en encadrer la valeur avec des quotes ,'.
Cela dit, ça n'explique pas ce nombre conséquent de lignes retournées. Là on dirait qu'il fait un produit cartésien de toutes les jointures, bizarre.
Tu devrais essayer ta requête avec une seule jonture, puis deux, puis trois, etc. pour voir à partir d'où ça commence à déraper.

Mendoza a écrit :

Deuxième question :
Dans un monde idéal où je réussis à faire une requête correcte : comment utiliser au mieux le résultat lors de l'affichage de la page ? Comment à partir du résultat de la requête afficher les X ustensiles au bon endroit et les Y étapes à un autre ? Quelle est la technique la plus efficace ?

Peut-être qu'en l'occurrence le multi-requêtage n'était pas une si mauvaise idée que cela. L'information qu'il faut quand il faut  (remember, just keep it simple, stupid.

Mendoza a écrit :

Question bonus :
Il m'est souvent arrivé de devoir utiliser plusieurs fois le résultat d'une requête sur une même page.  Quel est le meilleur moyen pour ce faire ? Une fois le résultat utilisé dans un while, on ne peut plus le réutiliser ailleurs.

Où as-tu pêché cette ânerie ?

Bien entendu, toutes les colonnes visées par le ORDER BY sont indexées dans les tables, n'est-ce pas ?


Gloire à qui n'ayant pas d'idéal sacro-saint,
Se borne à ne pas trop emmerder ses voisins. G. Brassens Don Juan 1976.
Avĉjo MoKo kantas
La chaîne YouTube MoKo Papy

Hors ligne

#3 26-06-2013 13:42:49

Mendoza
Membre
Inscription : 02-03-2011
Messages : 31

Re : Requête sur plusieurs tables : je croyais avoir compris, mais non.

Maljuna Kris a écrit :

Tout faux, les tables n'ont pas de champs mais des colonnes, si ta requête comporte vraiment des étoiles sur le site c'est contre-productif pour soulager le serveur.

UI m'sieur, sur la page en vrai y a pas d'étoile. Et pardon pour l'erreur de langage.

Maljuna Kris a écrit :

il n' y a pas lieu d'en encadrer la valeur avec des quotes ,'.
Cela dit, ça n'explique pas ce nombre conséquent de lignes retournées. Là on dirait qu'il fait un produit cartésien de toutes les jointures, bizarre. Tu devrais essayer ta requête avec une seule jonture, puis deux, puis trois, etc. pour voir à partir d'où ça commence à déraper.

Je fais ça dans la foulée et viens poster l'évolution du résultat. Je suis presque soulagé de voir qu'en fait je dois pas être trop à côté de la plaque dans la première requête, cela dit.

EDIT :
En fait ça foire dès la deuxième jointure et il me fait bien un produit cartésien.


SELECT recettes.*, recettes_etapes.*, ustensiles.*
          FROM recettes        
          LEFT JOIN recettes_etapes ON recettes_etapes.recette_id = recettes.recette_id
          LEFT JOIN ustensiles ON ustensiles.recette_id = recettes.recette_id
          WHERE recettes.recette_id = '150'
          ORDER BY recettes_etapes.etape_position ASC
 

Rien que là j'ai 60 résultats. Que se passe-t-il ?


Maljuna Kris a écrit :

Peut-être qu'en l'occurrence le multi-requêtage n'était pas une si mauvaise idée que cela. L'information qu'il faut quand il faut  (remember, just keep it simple, stupid).

Ah. Comment je peux savoir ce qu'il vaut mieux faire, du coup ? Je pensais que diminuer le nombre de requêtes ne pouvait pas de toute façon être une mauvaise chose. Me trompé-je ?

Maljuna Kris a écrit :

Où as-tu pêché cette ânerie ?

Il me semblait l'avoir constaté les rares fois où j'ai eu besoin de le faire. Je dois confondre avec autre chose.

Maljuna Kris a écrit :

Bien entendu, toutes les colonnes visées par le ORDER BY sont indexées dans les tables, n'est-ce pas ?

Hum...Je vais vérifier de ce pas.

Edit :
Tant qu'à créer un index pour optimiser les requêtes, quelles colonnes choisir pour être le plus efficace possible ? Juste nomDeLaTable_position ou bien j'ai intérêt à le faire sur plusieurs colonnes ?

Dernière modification par Mendoza (26-06-2013 14:07:51)

Hors ligne

#4 26-06-2013 21:43:52

Jc
Membre
Lieu : Zillisheim - Alsace
Inscription : 15-04-2010
Messages : 1 629
Site Web

Re : Requête sur plusieurs tables : je croyais avoir compris, mais non.

Bonjour,

Si vous me permettez, pour commencer, voici ce que je dirais en introduction à ma réponse car vous m'en donnez l'occasion.

Je reprends mon premier site de cuisine (opérationnel). Je cherche à l'optimiser parce que maintenant qu'il y a un peu de trafic dessus, je tue le serveur.

Vous donnez la définition d'un site qui fonctionne mais qui n'est pas opérationnel. C'est la différence entre ceux qui sont professionnels et les amateurs qui pensent pouvoir faire un site tout seul. Mais dans ce contexte le plus souvent, tout fonctionne bien tant que vous êtes tout seul et votre famille à venir le contempler...

S'il est relativement simple de faire un site qui fonctionne (et encore cela dépends, cela reste très relatif), il en est tout autre de faire un site performant capable de supporter une certaine charge "sans broncher". Tout simplement car cela doit se prévoir le plus souvent dans l'architecture même du site en fonction de ses contraintes de production et ses spécificités métier.

Mais il faut que vous ayez conscience que professionnellement parlant, en ce qui me concerne, il m'est complètement impossible de vous répondre sans faire un audit détaillé de votre site. Voici quelques questions pour vous aider à comprendre pourquoi :

maintenant qu'il y a un peu de trafic dessus

De quel traffic parlez-vous quantitativement parlant? Quel est la charge que votre site doit être capable de supporter? Ce traffic correspond-t-il à un pic d'utilisation, et si oui quel est-il?

Structure de la BDD :
- j'ai une table "principale" appelée recettes qui contient un recette_id et les infos uniques de chaque recette sur une ligne : l'auteur, le nom, la catégorie, etc.
- Toutes les autres tables concernées sont de type "1 à plusieurs" : elles ont toutes une colonne "recette_id" clé étrangère et un "nomDeLaTable_id" unique.

C'est tout sauf une définition de structure de votre base de données, le résumé d'un aperçu serait plus juste.
Il nous faudrait un schéma détaillé de votre modèle physique de production. Avez-vous fait un MCD de votre projet?
Et le minimum syndical un show create table de chaque table de votre BD.

Quel SGBDR utilisez-vous et en quelle version? Avez-vous fait au préalable une étude à savoir si le SGBDR choisi est capable de répondre au cahier des charges de votre applicatif au moins en terme de charge?
Quel est le type et la configuration de votre hébergement?

Je tue le serveur

Qu'est ce que vous tuez dans votre serveur? Pour y répondre avez-vous consulté les logs apache? et ceux de votre SGBDR? (incluant le slow log queries)

Ensuite dans une requête lorsque l'on fait un FROM A LEFT JOIN B, on ecrit pas ensuite ON B.id=A.id mais ON A.id=B.id
Donc on doit ecrire :
FROM A LEFT JOIN B ON A.id=B.id
FROM A RIGHT JOIN B ON B.id=A.id

Pourquoi est-ce important? Car si vous inversez l'ordre, vous risquez de vous retrouver comme il vous arrive actuellement quand vous avez plusieurs lignes en réponse à votre jointure, de vous retrouver avec tous les ustensiles listés pour chaque étape de la recette. Ce qui peut être voulu dans une certaine mesure, mais je ne pense pas dans le cas présent.

Un petit rappel, lorsque l'on fait un WHERE sur une colonne de la table de droite dans une jointure à gauche cela revient à faire un INNER JOIN entre les deux tables.

Bonne continuation

Jc.

Dernière modification par Jc (29-12-2013 19:32:35)


POO PHP+Ajax en MVC avec PDO et Bases de données épaisses  : What else?

Hors ligne

#5 27-06-2013 08:54:06

Mendoza
Membre
Inscription : 02-03-2011
Messages : 31

Re : Requête sur plusieurs tables : je croyais avoir compris, mais non.

Merci JC pour votre réponse. J'avais oublié à quel point il fallait avoir le coeur bien accroché pour poster sur ce forum pour débutants...On a souvent l'impression d'être une grosse merde inutile. Bref.
Ce premier site de cuisine était - et est toujours - bien évidemment amateur. Il m'a servi à apprendre les bases de la programmation HTML / CSS / PHP / MySQL / javascript(jQuery). Je n'ai jamais eu la prétention d'aller concurrencer marmiton ou quoique ce soit d'autre. Le trafic augmente progressivement parce qu'il n'est pas trop mal référencé (enfin derrière marmiton et 750g bien sûr) et qu'il offre sérieusement quelques bonnes idées...A la question "Avez-vous fait au préalable une étude à savoir si le SGBDR choisi est capable de répondre au cahier des charges de votre applicatif au moins en terme de charge" la réponse est donc non, et je ne suis même pas sûr de la comprendre. Toutefois anticiper tout ce que vous dites dans la première partie de votre réponse m'intéresse énormément et pour l'avenir je vous serais reconnaissant de bien vouloir m'orienter vers des ouvrages, sites ou articles permettant de "prévoir [ ...] l'architecture même du site en fonction de ses contraintes de production et ses spécificités métier". Sérieusement, ça m'intéresse.

A part ça : tout a commencé quand j'ai reçu la semaine dernière un mail de mon hébergeur qui me prévenait que le trafic de ce site commence à excéder ce qui est prévu dans le contrat initial (quasi gratuit) et qu'ils m'encouragent à les contacter blablabla pour discuter d'un changement d'offre. Toutefois - disent-ils - ils m'encouragent aussi à vérifier l'efficacité dudit site en termes de sollicitation du serveur, taille des images, etc. Dans la foulée j'ai utilisé des outils comme YSlow sur Firefox et des sites comme GTmetrix qui me disent qu'en gros, l'affichage des pages est ralenti par différents facteurs, dont un peut-être trop grand nombre de requêtes serveur (et ces saletés de modules sociaux Facebook et Google+). Je me suis donc repenché sur différentes pages dont la fameuse recette.php et ses 6 requêtes. Il m'a naïvement semblé intelligent d'essayer de les regrouper toutes en une seule vu que c'est la page la plus utilisée sur le site et qu'au final, toutes ces requêtes concernent une même recette. Les causes de ma démarche s'arrêtent là. Débutant, quoi...

Donc si je comprends bien, on ne peut pas édicter une règle générale qui dirait "moins y a de requêtes, mieux c'est" ? Chercher à tout combiner en une requête est potentiellement inutile ? Quels sont les facteurs à regarder ? Comment tester ? Où puis-je apprendre cela ?

Merci pour la précision sur le sens des égalités, j'avoue avoir oublié ce détail. Je restais bêtement sur les bases mathématiques qui disent que peu importe le sens de l'égalité, vu que c'est une égalité.

Hors ligne

#6 27-06-2013 17:00:11

Jc
Membre
Lieu : Zillisheim - Alsace
Inscription : 15-04-2010
Messages : 1 629
Site Web

Re : Requête sur plusieurs tables : je croyais avoir compris, mais non.

Bonjour,

Merci également pour votre réponse j'apprécie également. Vous savez je ne suis pas du genre à vouloir décourager des personnes comme vous qui ont votre initiative et qui sont déjà capables d'aller où vous êtes allé, ça serait plutôt même le contraire. Donc très sincèrement continuez si cela vous botte et c'est un bon exercice pour les méninges wink donc cela ne peut vous faire de mal.
Ce qu'il faut comprendre à travers ma réponse c'est que si vous êtes la première personne intéressée par ma réponse, vous n'êtes pas le seul et c'est aussi pour les autres que je me suis permis aussi de répondre ainsi. Faire comprendre que cela ne s'improvise pas, que c'est un métier à part entière voir même plusieurs métiers réunis pour pouvoir répondre à toutes mes questions d'une manière professionnelle. Je tenais à ce que nos lecteurs, à travers votre problématique concrète, en prenne la mesure, tout simplement.

Ensuite, et toujours en toute amitié, si votre site se développe encore (ce que je vous souhaite), souhaitez-vous le gérer en tant que manager ou en tant que développeur? Ce que je veux dire par là, c'est qu'il va arriver à un moment où il vous sera très difficile de concilier les deux où alors au risque du devenir de l'activité même de votre site. Pensez-y, car j'ai fait dernièrement pas mal de validation de modèles économiques sur internet, et c'est malheureusement un problème récurrent pour développer les projets. C'est un peu comme quelqu'un qui fait 2 métiers en parallèle à 70h par semaine. Ca va un temps, mais arrive un moment où pour en faire bien un il faut faire un choix, sinon la machine ne suis plus, ou éventuellement on peut perdre les deux.

Bien que mon site ne soit plus du tout à jour (j'ai plus d'un an de retard dans sa mise à jour), sachez que je fais beaucoup de maîtrise d'ouvrage actuellement tant au niveau logicielle que base de données et je diminue progressivement ma part d'activité concernant la maîtrise d'œuvre, n'ayant plus 20 ans non plus. Donc s'il est vrai que je suis bien placé pour vous conseiller au mieux dans votre demande, faire les choses bien à ce niveau serait de vous apprendre le métier, mais je ne pense pas que cela soit ce que vous recherchez d'une part, ni ma vocation d'autre part bien qu'ayant eu par le passé un agrément éducation nationale en formation. Le sujet est très vaste vous savez, et je ne pense pas que vous trouverez des ouvrages de "synthèse" qui vous diront "pour tel cas de figure, le mieux c'est de faire cela", etc.. Surtout en base de données, l'administration c'est de l'artisanat, et la synthèse c'est l'artisan qui la fait, fort de ses connaissances et de son expérience.

Pour ce qui est des bases de données, je vous donnerai une très bonne référence à travers le blog de SQLpro qui vous donnera une idée de ce que l'on peut trouver sous le capot d'un SGBDR, et étant tout comme lui partisan des développement en SGBDR épais, vous comprendrez mieux aussi pourquoi en le lisant.
Pour ce qui concerne l'architecture logicielle, allez voir sur le forum développez.net rubrique ALM, vous aurez de la lecture également, et vous pourrez y poser des questions, peut être qu'à l'occasion je vous y répondrais aussi.

Pour ce qui est des requêtes je ne parlais pas forcément de requêtes SQL mais de requêtes HTTP sur votre serveur Apache. Vous avez de la marge quand on pense qu'avec certains sites construits avec des CMS que je ne citerai pas et atteints de "modulite aigüe", comme il me plaît à le dire, on peut atteindre parfois plus de 350 requêtes serveur pour afficher la page d'accueil, ce qui est insensé.
Si vous voulez connaître le nombre réel de requêtes serveur de votre site, ouvrez IE, faites F12, cliquez sur l'onglet "réseau", cliquez ensuite sur "démarrez capture" et faites un F5 sur la page d'accueil de votre site. Vous aurez une ligne par requête. Il vous suffira de les compter.

Pour ce qui est des égalités je vous rappellerai juste qu'il ne s'agit pas d'égalité au sens arithmétique du terme mais au sens relationnel. L'égalité représentant la jointure, il est donc normal qu'en faisant une jointure à gauche ou à droite, ce que vous placez à gauche ou à droite ait de l'importance, bien que parfois selon le contexte, cela ne peut avoir d'incidence (cas général des jointures INNER JOIN ou CROSS JOIN inclus).

Concernant le choix du serveur je travaille personnellement qu'avec des serveur VPS chez OVH pour mes solutions logicielles pour de très nombreuses raisons, libre à vous de choisir le vôtre.

En ce qui concerne "moins il y a de requêtes, mieux c'est", sauf cas particuliers, vous avez tout à fait raison.
La solution serait de passer par un GROUP_CONCAT pour agréger les résultats de vos requêtes dérivées avec un séparateur de chaîne bien choisi et réservé au sein de votre application, et de les récupérer en php via un explode().

Cordialement,

Jc.

Dernière modification par Jc (27-06-2013 17:18:59)


POO PHP+Ajax en MVC avec PDO et Bases de données épaisses  : What else?

Hors ligne

Pied de page des forums