Filtrage d'une requête de type LEFT JOIN

Eléphanteau du PHP | 19 Messages

18 avr. 2006, 14:51

Bonjour,
J'ai 2 tables construites ainsi :
TABLE "fabrications" (qui contient toutes les fabrications de notre petite société artisanale) avec les champs suivant
ID_fab (int, not null, auto increment, clé primaire)
delais (date, not null)
client (varchar, not null)

et TABLE "etapes_fab" (qui contient toutes les étapes de fabrications pour chaque fabrication de la société) avec les champs suivant :
ID_etape (int, not null, auto increment, clé primaire)
fabricationID (int, not null)
atelier (varchar, not null)
operation (varchar; not null)
ordre (int, not null)

Le but est d'afficher un tableau dans lequel chaque ligne représente une fabrication avec ses étapes correspondantes donc le nombre de colonne change suivant la fabrication puisque que le nombre d'étapes n'est pas toujours le même.
Là,déjà j'ai lutté plusieurs jours avant de trouvé la solution qui est simple une fois qu'on la connais c'est à dire avec un LEFT JOIN dans la requête SQL et une rupture de séquence pour l'affichage dans le code PHP. Voici ma requête :

Code : Tout sélectionner

SELECT fabrications.ID_fab, fabrications.delais, fabrications.client, etapes_fab.ID_etape, etapes_fab.fabricationID, etapes_fab.atelier, etapes_fab.operation, etapes_fab.ordre FROM fabrications LEFT JOIN etapes_fab ON fabrications.ID_fab=fabricationID ORDER BY fabrications.delais ASC, fabrications.ID_fab, etapes_fab.ordre
LE PROBLEME que j'ai maintenant et où je sèche complètement après moulte essai et qui pourtant me parait tout bête est le suivant :
Je voudrais pouvoir afficher uniquement les fabrications avec toutes ses étapes correspondantes dont au moins une de ses étapes contient l'atelier x (collage par exemple).
Moi, j'avais tout simplement rajouter une clause :

Code : Tout sélectionner

WHERE etapes_fab.atelier="collage"
Mais bien entendu le résultat est qu'il m'affiche bien la fabrication qui contient au moins cette étape mais du coup il ne m'affiche que cette étape alors que je souhaiterais quand même afficher toutes les autres étapes de cette même fabrication puis la fabrication suivante avec toutes ses étapes qui répond également au critère de sélection...

Voilà, je sais pas si c'est suffisamment clair. Peut-être que je m'y suis mal pris :oops:
Merci beaucoup pour votre aide !
Babas

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

18 avr. 2006, 17:04

Le manuel de MySQL nous dit de le mettre dans la condition de jointure, donc dans le "ON".

Eléphanteau du PHP | 19 Messages

18 avr. 2006, 17:59

Le manuel de MySQL nous dit de le mettre dans la condition de jointure, donc dans le "ON".
Tout d'abord Merci pour ta réponse. Mais même si effectivement cette syntaxe est meilleure, le résultat est identique... c'est à dire qu'il n'affiche plus que les lignes où se trouve l'opération sélectionner.

Je m'explique avec un exemple :
Voici ce qui est retourné après la première requête cité plus haut sans la clause WHERE :
N°ligne - ID_fab - delais - client - ID_etape - fabricationID - atelier - operation - ordre
1 - 12 - 2006/01/15 - TOTO - 1 - 12 - maquette - bat - 1
2 - 12 - 2006/01/15 - TOTO - 2 - 12 - imprimante- imprss - 2
3 - 20 - 2006/04/20 - TATA - 5 - 20 - maquette - fichier- 1
4 - 20 - 2006/04/20 - TATA - 5 - 20 - montage - collage - 2
5 - 20 - 2006/04/20 - TATA - 6 - 20 - montage - finition - 3
6 - 20 - 2006/04/20 - TATA - 7 - 20 - expedition - control - 4

Et voila ce qu'il maffiche après ma requête avec la clause WHERE (ou ON spécifié) :
N°ligne - ID_fab - delais - client - ID_etape - fabricationID - atelier - operation - ordre
1 - 20 - 2006/04/20 - TATA - 5 - 20 - montage - collage - 2

Alors qu'il faudrait ça :
1 - 20 - 2006/04/20 - TATA - 5 - 20 - maquette - fichier- 1
2 - 20 - 2006/04/20 - TATA - 5 - 20 - montage - collage - 2
3 - 20 - 2006/04/20 - TATA - 6 - 20 - montage - finition - 3
4 - 20 - 2006/04/20 - TATA - 7 - 20 - expedition - control - 4

MERCI BEAUCOUP
Babas

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

18 avr. 2006, 19:29

Avant toute chose, je me permets de réécrire ta première requête afin de la rendre plus lisible. Pour cela, je donne des aliases aux noms des tables. (l'alias sera l'initiale du nom)

Code : Tout sélectionner

SELECT f.ID_fab, f.delais, f.client, e.ID_etape, e.fabricationID, e.atelier, e.operation, e.ordre FROM fabrications f LEFT JOIN etapes_fab e ON f.ID_fab = e.fabricationID ORDER BY f.delais ASC, f.ID_fab, e.ordre
Ensuite, puisque les champs ID_fab et fabricationID représente les mêmes données ils devraient porter le même nom:

Code : Tout sélectionner

ALTER TABLE etapes_fab CHANGE fabricationID ID_fab INT NOT NULL
Donc la requête devient:

Code : Tout sélectionner

SELECT f.ID_fab, f.delais, f.client, e.ID_etape, e.ID_fab, e.atelier, e.operation, e.ordre FROM fabrications f LEFT JOIN etapes_fab e ON e.ID_fab = f.ID_fab ORDER BY f.delais ASC, f.ID_fab, e.ordre
Maintenant que j'arrive à lire ta requête on peut s'attaquer aux choses sérieuses ;)

Comment savoir quelles fabrications sont passées par l'atelier collage ? c'est facile, tu l'as fait dans ton premier post, il faut regarder dans la table "etapes_fab". Donc on joint "fabrications" à "etapes_fab":

Code : Tout sélectionner

SELECT f.ID_fab FROM fabrications f JOIN etapes_fab e ON e.ID_fab = f.ID_fab AND e.atelier = 'collage'
Ok, maintenant on a les IDs des fabrications, mais comment avoir toutes les étapes ? il suffit de faire une nouvelle jointure sur la table etapes_fab ! Et pour s'assurer que les jointures se font aux bons endroits on mets des parenthèses pour mieux les délimiter:

Code : Tout sélectionner

SELECT f.ID_fab, e2.* FROM (fabrications f JOIN etapes_fab e ON e.ID_fab = f.ID_fab AND e.atelier = 'collage') JOIN etapes_fab e2 ON e2.ID_fab = f.ID_fab
Sauf erreur de ma part, cette dernière requête devrait produire le résultat escompté.

Eléphanteau du PHP | 19 Messages

18 avr. 2006, 21:04

MERCI pour ta réponse
Je test tout ça de suite...
Babas

Eléphanteau du PHP | 19 Messages

18 avr. 2006, 22:09

WOUAHHHHHH, ça fonctionne !!! sauf qu'il me double les réponses et le tri ne fonctionne plus ! Mais c'est déjà une très grande avancée.
Faut-il que je mette un distinct dans les e2 pour éviter les doublons ? Et le tri doit se faire maintenant sur e2.ordre pour la fin ?

Donc si j'ai bien compris on a effectuée une requête avec une 1ère jointure interne avec 2 conditions (ID_fab et e.atelier) puis une 2nd jointure en reprenant le résultat de la 1ère jointure.

Merci encore. J'essaye de supprimer les doublons
Babas

Eléphanteau du PHP | 19 Messages

18 avr. 2006, 22:35

Ca y est, c'est SUPER TOPPPPPPPPP. merci beaucoup de ton aide Hubert Roksor... T'es TROP FORT, merci, merci et merci. :D
Voilà la requête finale :

Code : Tout sélectionner

SELECT DISTINCT f.ID_fab, f.delais, f.client, e2.ID_etape, e2.atelier, e2.operation, e2.ordre FROM (fabrications f JOIN etapes_fab e ON f.ID_fab=e.ID_fab = AND e.atelier='collage') JOIN etapes_fab e2 ON e2.ID_fab=f.ID_fab ORDER BY f.delais ASC, f.ID_fab, e2.ordre
Encore Merci,
Basvic
Babas

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

18 avr. 2006, 22:45

le tri doit se faire maintenant sur e2.ordre pour la fin ?
Oui, tout à fait. J'avais oublié de l'intégrer.

Pour les doublons... n'utilise pas de DISTINCT ou GROUP BY au hasard, ça pourrait marcher mais d'après ce que je comprend de ton schema ce n'est pas la solution. (ça pourrait marcher un temps puis produire des résultats erronés sans que tu t'en aperçoives)

S'il y a des doublons, c'est que le schéma de la base n'est pas bon ou que la jointure ne correspond pas. En relisant ton premier post j'ai remarqué que "collage" était une opération, pas un atelier. (au fait, tu peux aligner les colonnes en utilisant la balise

Code : Tout sélectionner

) De plus, je soupçonne "operation" de dépendre de "atelier". Mon conseil: si on peut déduire l'atelier d'après l'opération effectuée alors "atelier" ne devrait pas être dans la table des opérations effectuées. Si chaque opération ne peut être effectuée qu'une fois par fabrication alors la table devrait comporter une clé UNIQUE sur ces deux champs. Si au contraire une fabrication peut se voire appliquer la même opération plusieurs fois (par exemple: finition, contrôle, finition) alors oui, il te faut grouper les résultats. Dans ce cas, la clause est simple à formuler. Que souhaites-tu grouper ? "pour chaque fabrication je veux chaque opération" => "GROUP BY e2.ID_fab, e2.operation" Si tu veux [url=http://fr.wikipedia.org/wiki/Formes_normales]normaliser tes données[/url] il te faut clairement définir tes besoins: [list][*]est-ce qu'une opération peut être effectuée dans différents ateliers ? (autrement dit: est-ce que l'atelier dépend de l'opération) [*]est-ce qu'une opération pourrait changer d'atelier dans le futur ? [*]est-ce qu'une opération peut être effectuée plusieurs fois sur une même fabrication ? [*]est-ce que les opérations sont entrées dans la base par ordre chronologique ? (dans ce cas, "ordre" est inutile) [*]est-ce que l'ordre des opérations est immuable ? (par exemple, le collage est toujours après l'impression)[/list]

Eléphanteau du PHP | 19 Messages

19 avr. 2006, 10:45


Si tu veux normaliser tes données il te faut clairement définir tes besoins:
Bonjour, et merci encore pour ces précieuses notions... Juste quelques précision car je vois que je me suis moi-même mélanger les pinceaux :oops: . Au final, je cherche effectivement à classer par atelier et non par opération (qui est juste là pour préciser ce que l'on fait dans un atelier x). Donc je répond aux questions dans l'ordre en les modifiant un peu du coup :
  • est-ce qu'une opération peut être effectuée dans différents ateliers ? (autrement dit: est-ce que l'atelier dépend de l'opération) Réponse : oui. Par exemple l'opération impression peut être attribué à l'atelier impression numérique ou à l'atelier de sérigraphie.
  • est-ce qu'une opération pourrait changer d'atelier dans le futur ? Réponse : ça va un peu avec la 1ère réponse oui puisqu'elle n'est pas unique à un atelier.
  • est-ce qu'un atelier peut être effectuée plusieurs fois sur une même fabrication ? Réponse : oui. On peut commencer par l'atelier maquette qui s'occupera de créer un fichier puis viendra l'atelier impression numérique puis il reviendra à la maquette pour un contre-collage.
  • est-ce que les opérations sont entrées dans la base par ordre chronologique ? (dans ce cas, "ordre" est inutile) - Réponse : oui elle sont entrées par ordre chronologique... J'avais créé ce champ dans le cas d'un rajout d'une étape entre deux autres par exemple. Je ne vois pas comment faire sinon juste avec l'id de l'étape.
  • est-ce que l'ordre des opérations et atelier est immuable ? (par exemple, le collage est toujours après l'impression) - Réponse : ba oui et non... il y a des execptions! Donc non.
Je ne devrais donc pas utiliser DISTINCT ?
Babas