OVH Cloud OVH Cloud

Foreach sous condition where? (long)

4 réponses
Avatar
Julien Delvat
Bonjour à tous,

J'ai plusieurs tables sous MySQL qui sont construites comme suit:

Membre: ID, login, ...
Company: ID, type, name, resp_pers (lien vers membre->ID), ...
Contact: ID, company (lien vers company->ID), first_name, last_name,
resp_pers (lien vers membre->ID), ...
Request: ID, produit (lien vers product->ID), contact (lien vers
contact->ID), resp_pers (lien vers membre->ID), quantity, cost, ...
Product: ID, manufacturer, reference, name

Ces tables representent une liste de clients (company). Pour chaque client,
un responsable est défini à l'intérieur de l'entreprise.
Chaque client peut avoir 1 à n contacts (contact). Pour chaque contact, un
responsable est défini (le meme que celui du client ou un autre).
Chaque contact peut avoir 0 à n requete ou devis (request). Pour chaque
requete, un responsable est défini (le meme que celui du client ou celui du
contact ou un autre). Chaque requete ne porte que sur un seul produit.

Mon but aujourd'hui est de faire un arbre avec des layers masquables pour
representer cette hierarchie:
x Racine
\- Company 1 / Type / Name / Resp. Pers.
\- Contact 1 / Last Name, First Name / Resp. Pers.
\- Request 1 / Product Name / Quantity / Cost / Resp. Pers.
\- Request 2 / Product Name / Quantity / Cost / Resp. Pers.
\- Contact 2 / Last Name, First Name / Resp. Pers.
\- Request 3 / Product Name / Quantity / Cost / Resp. Pers.
\- Request 4 / Product Name / Quantity / Cost / Resp. Pers.
\- Company 2 / Type / Name / Resp. Pers.
\- Contact 3 / Last Name, First Name / Resp. Pers.
\- Request 5 / Product Name / Quantity / Cost / Resp. Pers.
\- Request 6 / Product Name / Quantity / Cost / Resp. Pers.

Mon problème est de structurer mes requetes pour conserver des performances
correctes.
J'ai envisagé plusieurs approches:
- une requete enorme qui me recupere tout d'un coup, mais j'aurais une
redondance enorme de données
- plusieurs requetes imbriquees. evidemment, c'est le plus facile, mais
bonjour le nombre de requetes totales
- 3 requetes principales (1 pour company, 1 pour contact, 1 pour request) ->
c'est là ou j'en suis pour l'instant

Apres chacune de mes requetes, j'enregistre le resultat dans 3 tables
internes. Ensuite, je voudrais faire 3 boucles avec foreach comme suit:
foreach( $company )
{
// en-tete pour la company
foreach( $contact)
{
// en-tete pour contact
foreach( $request)
{
// donnees pour request
}
}
}

Mon probleme est qu'à chaque boucle de contact ou requete, je lis tous les
contacts et toutes les requetes.
Existe-t-il une syntaxe qui permette d'ajouter une condition where comme ca:
foreach( company )
{
// en-tete pour la company
foreach( $contact where $contact[company_ID] = company[ID] )
{
// en-tete pour contact
foreach( $request where $request[$contact_ID] = $contact[ID] )
{
// donnees pour request
}
}
}

Merci de votre aide,

Julien

4 réponses

Avatar
Bruno Desthuilliers
Julien Delvat wrote:
Bonjour à tous,

J'ai plusieurs tables sous MySQL qui sont construites comme suit:

(snip)


Mon but aujourd'hui est de faire un arbre avec des layers masquables pour
representer cette hierarchie:


(snip)

Mon problème est de structurer mes requetes pour conserver des performances
correctes.
J'ai envisagé plusieurs approches:
- une requete enorme qui me recupere tout d'un coup, mais j'aurais une
redondance enorme de données


Est-ce vraiment un problème ? Ces données ne circuleront guère qu'entre
le SGBDR et le serveur Web, et on peut espérer que la tuyauterie entre
les deux est à même de supporter ça...

AMHA, il y a de bonne chances qu'un test prouve que cette solution est
de loin la plus rapide, car elle ne nécessite qu'une seule requête, et
utilise au mieux les perf du SGBDR pour les sélections et tris.

- plusieurs requetes imbriquees. evidemment, c'est le plus facile, mais
bonjour le nombre de requetes totales
- 3 requetes principales (1 pour company, 1 pour contact, 1 pour request) ->
c'est là ou j'en suis pour l'instant




Mon probleme est qu'à chaque boucle de contact ou requete, je lis tous les
contacts et toutes les requetes.
Existe-t-il une syntaxe qui permette d'ajouter une condition where comme ca:
foreach( company )
{
// en-tete pour la company
foreach( $contact where $contact[company_ID] = company[ID] )


(snip)
Tu peux soit filtrer directement dans la boucle, soit utiliser
array_filter(), mais je doute que les perf soient au rendez-vous. Si ton
SGBDR n'est pas plus efficace que du code PHP pour les sélections et
tris, change de SGBDR !-)

mes 2 centimes
Bruno

Avatar
pdv
"Julien Delvat" a écrit dans le message de
news:3f7f449a$0$27594$

- une requete enorme qui me recupere tout d'un coup, mais j'aurais une
redondance enorme de données
- plusieurs requetes imbriquees. evidemment, c'est le plus facile, mais
bonjour le nombre de requetes totales
- 3 requetes principales (1 pour company, 1 pour contact, 1 pour
request) ->

c'est là ou j'en suis pour l'instant


7 tables ce n'est pas énorme dans une requête, tu as vérifié que la première
solution posait de réels problèmes de perfs ?

Avatar
P'tit Marcel
Julien Delvat écrivit:

J'ai plusieurs tables sous MySQL qui sont construites comme suit:
Membre: ID, login, ...
Company: ID, type, name, resp_pers (lien vers membre->ID), ...
Contact: ID, company (lien vers company->ID), first_name, last_name,
resp_pers (lien vers membre->ID), ...
Request: ID, produit (lien vers product->ID), contact (lien vers
contact->ID), resp_pers (lien vers membre->ID), quantity, cost, ...
Product: ID, manufacturer, reference, name


(snip affichage de la hiérarchie)

x Racine
- Company 1 / Type / Name / Resp. Pers.
- Contact 1 / Last Name, First Name / Resp. Pers.
- Request 1 / Product Name / Quantity / Cost / Resp. Pers.



Mon problème est de structurer mes requetes pour conserver des
performances correctes.

3 requetes principales (1 pour company, 1 pour contact, 1 pour
request) -> c'est là ou j'en suis pour l'instant

Apres chacune de mes requetes, j'enregistre le resultat dans 3 tables
internes. Ensuite, je voudrais faire 3 boucles avec foreach comme
suit: foreach( $company )

Mon probleme est qu'à chaque boucle de contact ou requete, je lis tous
les contacts et toutes les requetes.
Existe-t-il une syntaxe qui permette d'ajouter une condition where
comme ca: foreach( company )
foreach( $contact where $contact[company_ID] = company[ID] )



La solution dépend notamment de savoir :
- si la plupart des lignes de la table "Company" doivent être affichées ou
bien seules quelques unes donne lieu à "Requete"
- idem pour la table Contact

En effet, dans le premier cas de figure, il est rentable de récupérer la
totalité des tables dans un tableau avant de lire la table Requete.

Dans le second, il faudrait lire les données de Company et Contact avec des
jointures ce qui est plus coûteux. On peut aussi alors décider de lire
d'acord les données Requetes et les stocker dans un tableau, mais là il ne
faut pas avoir trop de requêtes...

En supposant qu'on est dans le 1er cas, plus simple, cela donne :


# récupérer les données Contact
$sql='select ID, company, first_name, last_name, resp_pers from Contact';
$res=mysql_query($sql) or die(mysql_error()."<br>$sql");
$contact=array();
$comp=array();
while($table=mysq_fetch_assoc($res)) {
foreach ($table as $cle => $valeur) {
if (cle=="ID") continue;
if (cle=="company") {
if (!isset($company[$valeur]))
$comp[$valeur]=1;
}
else $contact[$table["ID"]][$cle]=$valeur;
}
}
# récupérer les données Company pour les Contacts
$company=array();
if(count($comp)>0) {
$sql='select ID, type, name, resp_pers from company where ID in(".
implode(',' , array_keys($comp)) . ")";
$res=mysql_query($sql) or die(mysql_error()."<br>$sql");
unset($comp);
while($table=mysq_fetch_assoc($res)) {
foreach ($table as $cle => $valeur) {
if (cle=="ID") continue;
$company[$table["ID"]][$cle]=$valeur;
}
}
}
# lire les données détaillées et afficher le tout
$sql="select p.name as name, r.quantity as quantity, r.cost as cost,
r.resp_pers as resp_pers, r.contact as contact, c.company as company from
requete as r inner join produit as p on r.produit=p.ID inner join contact
as c on r.contact= c.ID order by c.company, r.contact";
$res=mysql_query($sql) or die(mysql_error()."<br>$sql");
$comp0="";
$cont0="";
while($table=mysq_fetch_assoc($res)) {
if($table["company"]<>$comp0) { # nouvelle société
$comp0=$table["company"];
if(isset($company[$comp0]))
print $comp0 . '/' . implode(' / ', $company[$comp0]) . '<br>';
else print $comp0 . '/ INCONNUE<br>';
}
if($table["contact"]<>$cont0) {# nouveau contact
$cont0=$table["contact"];
if(isset($contact[$cont0]))
print ".." .$cont0.'/'.implode(' / ',$contact[$cont0]).'<br>';
else print ".." .$cont0 . '/ INCONNU<br>';
}
print "..." . $table["name"] . '/' . $table["quantity"] . '/' .
$table["cost] . '/' . $table["resp_pers"] . '<br>';
}


(non testé)

eça
--
P'tit Marcel
statistiques sur les forums modérés : http://www.centrale-lyon.org/ng/

Avatar
tyoup
Julien Delvat wrote:
Bonjour à tous,

J'ai plusieurs tables sous MySQL qui sont construites comme suit:

[blabla]

Mon but aujourd'hui est de faire un arbre avec des layers masquables pour
representer cette hierarchie:

[blabla]

Mon problème est de structurer mes requetes pour conserver des performances
correctes.
J'ai envisagé plusieurs approches:
- une requete enorme qui me recupere tout d'un coup, mais j'aurais une
redondance enorme de données
- plusieurs requetes imbriquees. evidemment, c'est le plus facile, mais
bonjour le nombre de requetes totales
- 3 requetes principales (1 pour company, 1 pour contact, 1 pour request) ->
c'est là ou j'en suis pour l'instant


il me semble que la dernière solution soit la bonne

je propose la démarche suivante :

trier les clients
trier les contacts, les grouper et les trier par client
trier les requêtes, les grouper et les trier par contact

ainsi on peut parcourir la hiérarchie en ne parcourant qu'une seule fois
chacune des trois tables.

Apres chacune de mes requetes, j'enregistre le resultat dans 3 tables
internes. Ensuite, je voudrais faire 3 boucles avec foreach comme suit:
foreach( $company )
[blabla]


en profitant du tri évoqué précédemment le parcours de la hiérarchie
devient trivial. prendre un client, lire tous ses contacts (en toute
logique ce sont les premiers que l'on trouve puisqu'ils sont triés) et
pour chaque contact, lire les requêtes (pareil elles devraient être au
début). recommencer pour le client suivant. on peut ainsi utiliser une
boucle for ou une boucle while : on évite une copie "coûteuse" de
l'ensemble des données et le "coût" de la boucle foreach. et on gagne en
plaisir d'avoir bien codé son application =).

Merci de votre aide,


de rien

--
Tyoup