Remonter plusieurs niveaux de tables MySql

Fermé
PunkarA Messages postés 4 Date d'inscription lundi 7 mars 2005 Statut Membre Dernière intervention 29 avril 2015 - 1 oct. 2012 à 01:12
 PunkarA - 3 oct. 2012 à 13:24
Bonjour,

J'ai un code qui fonctionne bien, mais je cherche simplement à l'optimiser !
J'ai différentes tables liées qui sont :
- CHANTIER
- DEVIS
- LIGNES
- OUVRAGES

Chaque ouvrage est lié à une ligne qui est liée à un devis qui est lié à un chantier...
Cette liaison s'effectue à l'aide d'ID propres et d'ID de référence :
- ouvrage.ligne = ligne.ID
- lignes.devis = devis.ID
- devis.chantier = chantiers.ID

Maintenant, supposons que, pour la ligne n°53, je veuille récupérer la liste des ouvrages correspondant au même chantier... Je procède comme suit :
- je récupère le devis correspondant à cette ligne
- je récupère le chantier correspondant à ce devis : j'ai mon chantier !
ensuite :
- je récupère la liste des devis correspondant à ce chantier
- je récupère la liste des lignes correspondant à ces devis
- je récupère la liste des ouvrages correspondant à ces lignes

Ouf, c'était long ! Trop long, en fait... A l'aide de table liées (INNER JOIN), j'aurais pu me faciliter la tache. Mais sur plusieurs niveaux, comment faire ?!

Voici mon code, il fonctionne très bien mais est un poil trop long...
Il permet de faire cette manip sur plusieurs lignes fournies (sélection multiple).

$lID  = $_GET['ligne_ID']; // liste des lignes car sélection multiple

  $liste_chantiers = array();
  $ls = mysql_query("SELECT * FROM ligne WHERE ID IN ($lID)") or die(mysql_error());	// on selectionne les lignes concernées
  while ($l = mysql_fetch_array($ls))
    {
	  $dID = $l['devis'];
	  $ds = mysql_query("SELECT * FROM devis WHERE ID='$dID'");
          // on sélectionne le devis correspondant (ligne par ligne)
	  while ($d = mysql_fetch_array($ds))
	    {
		  $cID = $d['chantier'];	
		  $cs = mysql_query("SELECT * FROM chantiers WHERE ID='$cID'");
                  // on sélectionne le chantier correspondant (devis par devis)
		  while ($c = mysql_fetch_array($cs))
		    {
			  if (!in_array($c['ID'], $liste_chantiers)) $liste_chantiers[] = $c['ID'];
                          // on ajoute (ou pas) le chantier à la liste;
		     }
	    }
    }
	
  // Là, on a fini de lister les chantiers !
  // Alors on va s'attaquer à la suite : lister les devis et lignes correspondantes !
  
  $liste_chantiers_str = join(',', $liste_chantiers);
  $liste_devis  = array();
  $liste_lignes = array();
  
  
  $ds = mysql_query("SELECT * FROM devis WHERE chantier IN ($liste_chantiers_str)") or die(mysql_error());
  while ($d = mysql_fetch_array($ds)) $liste_devis[] = $d['ID'];
  
  $liste_devis_str = join(',', $liste_devis);
  
  
  $ls = mysql_query("SELECT * FROM ligne WHERE devis IN ($liste_devis_str)");
  while ($l = mysql_fetch_array($ls)) $liste_lignes[] = $l['ID'];
  
  $liste_lignes_str = join(',', $liste_lignes);

  $os = mysql_query("SELECT * FROM ouvrages WHERE ligne in ($liste_lignes)");
  // $os : ça y est, j'ai mon array que je traiterai par la suite !  


Une idée pour épurer un peu ce bordel ?!

PS : je reconnais que le choix des noms n'est pas optimal (un champ a le même nom qu'une table par exemple) mais ce détail sera réglé plus tard, là, c'est pas trop le sujet...

Merci d'avance pour vos réponses !

3 réponses

Reivax962 Messages postés 3672 Date d'inscription jeudi 16 juin 2005 Statut Membre Dernière intervention 11 février 2021 1 011
1 oct. 2012 à 11:00
Bonjour,

Je n'ai pas testé, mais je pense que tu peux faire une requête de ce genre :

SELECT o.*
FROM ouvrages o
INNER JOIN lignes l1 ON l1.id = o.ligne
INNER JOIN devis d1 ON d1.id = l1.devis
INNER JOIN devis d2 ON d2.chantier = d1.chantier
INNER JOIN ligne l2 ON l2.devis = d2.id
WHERE l2.id = 53

À vue d'oeil, tu auras peut-être des doublons à gérer par un DISTINCT.

Xavier
1
Super, merci pour ta réponse aussi rapide !

Je me demandais justement s'il était possible de procéder à plusieurs jointures INNER JOIN dans une même requête. Je n'ai jamais trouvé ce cas-là dans mes différentes recherches (toujours une jointure simple, donc pas très intéressante dans mon cas).

Je teste ça et je te tiens au jus ;)

Je connaissais pas DISTINCT, je me documente là-dessus et si tout roule, je balancerai un code de principe pour aider les autres personnes qui seraient susceptibles de rencontrer le même problème.

Merci beaucoup en tout cas !
0
Hello !

Finalement, ta requête fonctionne très bien dans le principe, je vais simplement me contenter de compléter les différentes requêtes possibles pour ceux qui, comme moi, ont (eu) un peu de mal avec les jointures sur plus de 2 tables :


- comment obtenir, en une seule requête, toutes les infos relatives à l'ouvrage $ID :

SELECT o.*, l.nb AS lig, d.nb AS dev, c.name AS cha 
FROM ouvrages AS o 
LEFT JOIN ligne AS l ON l.ID=o.ligne
LEFT JOIN devis AS d ON d.ID=l.devis
LEFT JOIN chantiers AS c ON c.ID=d.chantier
WHERE o.ID=$ID


on peut ainsi récupérer :
- le n° de la ligne via $['lig']
- le n° du devis via $['dev']
- le nom du chantier via $['cha']

A noter qu'on aurait pu vouloir récupérer d'autres infos, dans quel cas il faudra les rajouter sur la 1ere ligne de ce code (ex : c.adresse AS adr, c.prix_total AS prix, etc.). Evitez les l.*, d.*, c.*, qui peuvent générer une ambiguïté dans MySql !

L'utilisation de LEFT JOIN au lieu d'INNER JOIN est utile pour récupérer les ouvrages dont le champ 'ligne' ne correspond à rien dans la table 'ligne' (et autres correspondances dans les autres tables) !
C'est très utile car la ligne à laquelle se réfère l'ouvrage a très bien pu être supprimée du devis...

- comment récupérer les ouvrages du chantier "TOUR EIFFEL" ?

SELECT * FROM ouvrages AS o 
INNER JOIN ligne AS l ON o.ligne=l.ID
INNER JOIN devis AS d ON l.devis=d.ID
INNER JOIN chantiers AS c ON d.chantier=c.ID
WHERE c.name="TOUR EIFFEL"


- comment récupérer les ouvrages du devis "D2012-00128/A" ?

SELECT * FROM ouvrages AS o 
INNER JOIN ligne AS l ON o.ligne=l.ID
INNER JOIN devis AS d ON l.devis=d.ID
WHERE d.nb="D2012-00128/A"


- comment récupérer tous les devis des chantiers comprenant des "parpaings" ?

SELECT * FROM devis AS d1 
INNER JOIN chantiers AS c1 ON d1.chantier=c1.ID
INNER JOIN devis AS d2 ON d2.chantier=c1.ID
INNER JOIN ligne AS l2 ON l2.devis=d2.ID
WHERE l2.description LIKE "%parpaing%"



En espérant en avoir aidé quelques uns ;)

Merci encore à Xavier (Reivax962) pour son aide précieuse !
0