Requete avec jointures compliquée pour un newbe

Requete avec jointures compliquée pour un newbe - SQL/NoSQL - Programmation

Marsh Posté le 23-09-2013 à 22:55:41    

Salut tout le monde,
 
J'ai besoin de l'aide de connaisseurs pour modifier une requete SQL un peu trop compliquée pour moi. La voici :  
 
SELECT SQL_CALC_FOUND_ROWS *,f.nom AS nomfourn
FROM commandes AS c  
LEFT JOIN employes AS e ON c.Emetteur=e.id_employe  
LEFT JOIN fournisseurs AS f ON c.Fournisseur=f.id_fournisseur  
LEFT JOIN consommables AS con ON con.fournisseur=f.id_fournisseur
LEFT JOIN categories AS cat ON cat.id_cat=con.categorie  
WHERE 1
GROUP BY c.id_commande  
ORDER BY CONCAT(IFNULL(CASE WHEN status=0 THEN 1 ELSE 2 END,"" ),IFNULL(envoye,"" )),`date de livraison`
DESC LIMIT 0,100
 
Elle marche très bien.  
Voici ce que j'en ai compris :  
Elle va chercher toutes les commandes dans la table"commandes",
récupère le nom de l'émetteur pour chaque commande en cherchant dans la liste des employes,
récupère le nom du fournisseur dans la table des fournisseurs,
récupère la liste des consommable auquel un fournisseur a accès,
récupère les catégories de chaque consommable,
Elle les regroupe et les trie selon un ordre qui convient.
 
Mon problème est que l'employé n'a accès qu'à un nombre restreint de familles de produits.
J'aimerais que la requête ne retourne que les commandes dans lesquelles il y a au moins un consommable qui appartient à une des familles auxquelles l'utilisateur à accès.
Pour définir les familles auxquelles appartient un consommable la table "rel_familles_conso" contient les champs id_conso et famille avec une ligne par couple conso/famille.
De même la table "rel_familles_employe" contient les champs id_employe et famille avec une ligne par couple employe/famille.
 
J'ai beaucoup de mal avec les jointures, et je ne vois pas de moyen simple de faire, si l'un d'entre vous peut éclairer ma lanterne, il en récoltera toute mon admiration.
 
Bref, merci d'avance pour votre aide.

Reply

Marsh Posté le 23-09-2013 à 22:55:41   

Reply

Marsh Posté le 25-09-2013 à 16:24:24    

tu as juste à ajouter 2
inner join sur les 2 tables rel_familles_conso et rel_familles_employe
avec les jointures qui vont bien :
 
inner join rel_familles_employe as x
on x.id_employe = c.Emetteur  
(tu as peut-être un autre champ, à mettre en jointure ici, faut voir le détail de tes tables...)
 
inner join rel_familles_conso as y  
on  y.famille = x.famille and x.id_conso = y.id_conso
 
adaptes plus précisement les jointures...
 
Guillaume
 


Message édité par gpl73 le 25-09-2013 à 16:26:01
Reply

Marsh Posté le 30-09-2013 à 00:37:28    

Salut,
Désolé de répondre si tard, j'ai eu quelques soucis à régler sur le serveur entre temps.
 
Tout d'abord merci pour ta réponse. INNER JOIN me semble approprié en un certain sens.
 
J'ai quand même quelques problèmes :  
Je ne veux que les commandes qui contiennent un produit au moins qui appartient aux familles auxquelles un seul utilisateur a accès, j'ai donc fait la requête suivante :  
SELECT SQL_CALC_FOUND_ROWS *, f.nom AS nomfourn FROM commandes AS c  
LEFT JOIN employes AS e ON c.Emetteur=e.id_employe
LEFT JOIN fournisseurs AS f ON c.Fournisseur=f.id_fournisseur
LEFT JOIN consommables AS con ON con.fournisseur=f.id_fournisseur
LEFT JOIN categories AS cat ON cat.id_cat=con.categorie
INNER JOIN rel_familles_conso AS fc ON fc.id_conso=con.id_consommable
WHERE fc.famille IN (SELECT famille FROM rel_familles_employes WHERE user=1358) GROUP BY...
 
Mais elle ne marche pas :  
Il retourne toujours les commandes avec uniquement des produits ayant des familles auxquelles l'utilisateur n'a pas accès.
Il n'affiche plus les commandes sans fournisseur (même en remplaçant l'INNER JOIN par un LEFT JOIN).
 
Je suis un peu perdu pour cette requête. Merci d'avance pour ton aide.

Reply

Marsh Posté le 01-10-2013 à 14:46:21    

Au fait :)... tu as ton user , dans la table commandes...
c'est fini :) tu fais une selection sur ton user :
select ... from commandes as c  
where c.emmetteur= 1358 :)
Pour l'habillage, tu récuperes les info en faisant les jointures pour retrouver (left join) les fournisseurs (0.n)
inner join consommables (si tu peux directements sur la table commandes et non sur la table fornisseurs), pareil pour les  catégories et familles, en fonction des cardinalités de tes relations.
 
Guillaume

Reply

Marsh Posté le 01-10-2013 à 23:45:07    

Salut,
 
Ma requête doit afficher toutes les commandes qui contiennent au moins un produit appartenant à une des familles auxquelles l'utilisateur a accès.
 
L'utilisateur n'est pas forcément le demandeur. j'ai mis 1358 parce qu'il correspond à un compte user sur lequel je peux changer librement les droits sur les familles de produits.
 
en fait l'idée, c'est :
de parcourir toutes les commandes dans la table commande.
SELECT SQL_CALC_FOUND_ROWS *, f.nom AS nomfourn FROM commandes AS c  
Pour chaque commande, je vais chercher tous ses produits dans la table conso_histo
INNER JOIN conso_histo AS chisto ON chisto.provenance=c.id_commande
Puis les familles des consommables
LEFT JOIN rel_familles_conso AS fc ON fc.id_conso=chisto.consommable
puis je n'affiche que ceux qui correspondent à des familles que peut voir l'utilisateur
WHERE fc.famille IN (SELECT famille FROM rel_familles_employes WHERE user=1358)
 
Mais ça ne marche pas...
Est-ce que tu aurais une piste STP ?


Message édité par k'stor le 01-10-2013 à 23:45:31
Reply

Marsh Posté le 02-10-2013 à 00:33:39    

Re :  
J'ai bossé dessus et j'en suis arrivé à ça :
SELECT * FROM commandes  AS c
LEFT JOIN conso_histo AS chisto ON chisto.provenance=c.id_commande
LEFT JOIN employes AS e ON c.Emetteur=e.id_employe  
LEFT JOIN fournisseurs AS f ON c.Fournisseur=f.id_fournisseur  
LEFT JOIN consommables AS con ON con.fournisseur=f.id_fournisseur  
LEFT JOIN categories AS cat ON cat.id_cat=con.categorie
LEFT JOIN rel_familles_conso AS fc ON fc.id_conso=chisto.consommable
WHERE fc.famille IN (SELECT famille FROM rel_familles_employes WHERE user=1358)
 
Ça a l'air de marcher comme je veux, je te tiens au jus si j'ai une merde.
 
Merci pour ton aide.
 

Reply

Marsh Posté le 04-10-2013 à 13:52:59    

désolé, mais RTT lol
pourquoi fais tu une sous requete dans ton where?
et pas simplement un inner join?
Guillaume

Reply

Marsh Posté le 04-10-2013 à 23:24:53    

Ah, je savais meêm pas que c'était faisable.
 
Ca ressemble a quoi ?
SELECT * FROM commandes  AS c  
LEFT JOIN conso_histo AS chisto ON chisto.provenance=c.id_commande  
LEFT JOIN employes AS e ON c.Emetteur=e.id_employe  
LEFT JOIN fournisseurs AS f ON c.Fournisseur=f.id_fournisseur  
LEFT JOIN consommables AS con ON con.fournisseur=f.id_fournisseur  
LEFT JOIN categories AS cat ON cat.id_cat=con.categorie  
INNER JOIN rel_familles_conso AS fc ON fc.id_conso=chisto.consommable
INNER JOIN rel_familles_employes AS rfe ON fc.famille IN rfe.user=1358
 
Ca te semble bon ? (c'est un peu compliqué pour tester ça, je bosse directement sur la prod...)

Reply

Marsh Posté le 11-10-2013 à 16:19:07    

aieaie... :)
les jointures (des 3  types) se font sur égalités entre champs (pour joindre, relier, mais pas faire de selection).
il faut vraiement voir cela comme ça .
Le fait de faire un left , un inner , ou un exception join n'a rien à voir avec la maniere avec une sélection sur un critère précis...
tu réécris en fait les liens de ton MCD-MLD (je m'en souviens jamais lol)...
 
left outer : je prends ce qu'il y a dans la table principale et je remontes les info si il y en a dans la table jointes (et tu as null sinon)
inner : je ne prends que les enregistrements avec correspondances dans les 2 tables.
exception join: que ceux qui existent dans la principale et qui n'existent pas dans la seconde.
 
Ta selection se fait apres dans une clause where par exemple
 
select * from table1 as a inner ( ou exception ou inner) join table2 as b
on a.id1 = b.id1 (and a.id2 = b.id2 ... si tu as des clés composées).
where  
mes sélections
 
toi : cela donnera un truc du genre :
SELECT * FROM commandes  AS c  
LEFT JOIN conso_histo AS chisto ON chisto.provenance=c.id_commande  
LEFT JOIN employes AS e ON c.Emetteur=e.id_employe    
LEFT JOIN fournisseurs AS f ON c.Fournisseur=f.id_fournisseur    
LEFT JOIN consommables AS con ON con.fournisseur=f.id_fournisseur    
LEFT JOIN categories AS cat ON cat.id_cat=con.categorie  
INNER JOIN rel_familles_conso AS fc ON fc.id_conso=chisto.consommable  
______________________________________________________
INNER JOIN rel_familles_employes AS rfe ON fc.user = rfe.user
______________________________________________________
where  
rfe.user = 1358
 
c'est dans le where que tu mets rfe.user =1358
Si tu mets des valeurs (en dur) dans tes jointures... c'est pas "propre" (et tes perfs vont chutter salement, si tu as des gros tables) ou alors c'est vraiement voulu, par exemple dans une selection sur un left outer join tableN
et que tu veux que faire une "sous" selection de la tableN
car si tu mets dans ce cas des critères (champs de la table N) dans le where... tu n'as pas un left outer join mais un inner join...
 
Guillaume


Message édité par gpl73 le 11-10-2013 à 16:47:09
Reply

Marsh Posté le 11-10-2013 à 18:20:06    

Ouah !
Ça me parait très joli (et évident).
Bon j'avoue que j'ai du relire les explications deux fois mais la requête est limpide.
 
La requête est trèèèès lente (principalement à cause du *) seulement c'est pas moi qui ait écrit le code du coup j'ai peur d'enlever une valeur nécessaire en restreignant le nombre de champs en retour. J'ai déjà essayé en mettant "c.*" au lieu du "*", ça a l'air de marcher. Le code (PHP) est une grosse merde, pas indenté, il contient des $k, $tmp, $c... et un unique commentaire inutile pour 900 lignes.
 
Je vais essayer de faire tourner ta requête ce w.e.
 
Merci pour ton aide !

Reply

Marsh Posté le 11-10-2013 à 18:20:06   

Reply

Marsh Posté le 14-10-2013 à 14:34:20    

Bonjour :)
Il faut faire en effet attention au volume de ce que tu remontes surtout si tu es en web... car c'est plus la composante "web" qui va limité la faire la différence entre un * et qq champs....
la longueur (ou le volume) d'un record remontée n'est pas généralement pénalisant pour la partie SQL.
Cela sera en fait, plus un temps de transfert et de chargement et/ou d'affichage qui va faire que ta "requete" sera lente.
Mais le plus important: c'est de faire les bonnes jointures sur les bonnes clés (et/ou les bons index) de même pour la sélection.
Ces clés ou index (sl) doivent exister , sinon ton "SGBD" les recret pour ta requete... Ceci est d'autant plus vrai que tu travailles avec des clés composites, et non pas avec des jointures d'ID de tables....
 
Sur ta requete, on n'a parlé que de fichier physique, est-ce que tu as des vues ou logiques sur ceux-ci ? Si oui, tu peux les utliser... cela sera déjà plus rapide.
Il faut donc que tu vérifies si c'est le cas...Tu dois avoir dans ton requeteur SQL, un outil d'optimisation...non?
 
Si c'est le cas il faut créer les index qui vont bien... sinon il faut les créer "à la main".  
Attention, la création d'index n'est pas un truc que l'on fait à la légère... Cela peut aussi te mettre le binz dans ton appli, si ceux-ci sont pas adéquats.
trop d'index tue l'index lol
 
Guillaume
 
 
P.S. :
Un conseil de vieux lol , même si tu as des SQL faciles et rapides, ne remontes que ce tu as besoin et uniquement ce que tu as besoin... c'est pas bien plus long à coder, cela sera "forcement" plus rapide, surtout plus lisible et compréhensible et donc facile à maintenir.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed