Page 1 sur 2

Script PHP pour déterminer une catégorie

Posté : 22 nov. 2005, 01:04
par Sebe
Bonjour,
Je suis dans l'édition d'un site de course à pied et j'aimerai éditer une page où il y a une liste des sociétaires du club avec leur catégorie. Jusque maintenant,j'ai utilisé la facilité (Dreamweaver)mais je ne trouve pas la fonction qui me permetrait de faire ceci http://60gp.ovh.net/~kainbuye/autres_pa ... etaire.php qui sont en faite toutes des entrées de ma Base de Données (chez OVH). L'inconveignant est que celles-ci change chaque année en fonction des ages!

Voici les différentes catégories :
ED = femme (F) =<19 ans
EH = homme (H) =<19 ans
DA = femme (F) >= 20 ans & <35 ans
A1 = femme (F) >= 35 ans & <45 ans
A2 = femme (F) >= 45 ans & <55 ans
A3 = femme (F) >= 65 ans & <75 ans
A4 = femme (F) >= 75 ans & <85 ans
A5 = femme (F) >= 85 ans
SE = homme (H) >= 20 ans & <40 ans
V1 = homme (H) >= 40 ans & <50 ans
V2 = homme (H) >= 50 ans & <60 ans
V3 = homme (H) >= 60 ans & <70 ans
V4 = homme (H) >= 70 ans & <80 ans
V5 = homme (H) >= 80 ans

Variable dans les Champs de la Base de Données :
Champ ‘H_ouF’ : rep. possibles ‘H’ ou ‘F’
Champ ‘naissance’: par défaut ‘0000-00-00’ a noter que je n’ai besoin que l’année !

Préambule :
Déterminer l’année courante : ‘$anneecourante’
Déterminer l’année de naissance : ‘$anneenaissance’
Déterminer l’âge de cette année : ‘$agecetteannee’
Pour info: $agecetteannee = $anneecourante - $anneenaissance

Voici le code PHP
<?php require_once('../Connections/db_kainb.php'); ?>
<?php
mysql_select_db($database_db_kainb, $db_kainb);
$query_homme = "SELECT * FROM societaires WHERE H_ou_F = 'h' AND societaires.naissance <= '1986-01-01' ORDER BY nom ASC";
$homme = mysql_query($query_homme, $db_kainb) or die(mysql_error());
$row_homme = mysql_fetch_assoc($homme);
$totalRows_homme = mysql_num_rows($homme);

mysql_select_db($database_db_kainb, $db_kainb);
$query_dame = "SELECT * FROM societaires WHERE H_ou_F = 'f' AND societaires.naissance <= '1986-01-01' ORDER BY nom ASC";
$dame = mysql_query($query_dame, $db_kainb) or die(mysql_error());
$row_dame = mysql_fetch_assoc($dame);
$totalRows_dame = mysql_num_rows($dame);

mysql_select_db($database_db_kainb, $db_kainb);
$query_espoirs = "SELECT * FROM societaires WHERE naissance >= '1986-01-01' ORDER BY nom ASC";
$espoirs = mysql_query($query_espoirs, $db_kainb) or die(mysql_error());
$row_espoirs = mysql_fetch_assoc($espoirs);
$totalRows_espoirs = mysql_num_rows($espoirs);
?>
1 Suis-je dans le bon?
2 Qu'est-ce que je dois utiliser comme fonction?

Posté : 22 nov. 2005, 02:12
par ouckileou
Salut,

moi ce que je ferais, c'est une petite table "Categories", qui ressemblerait à ça :

Code : Tout sélectionner

Categories ---------- id_categorie nom_categorie age_min age_max sexe
Ensuite, je récupèrerais les infos utiles de chaque personne avec quelque chose comme ceci :

Code : Tout sélectionner

SELECT id_personne, nom, prenom, YEAR(NOW()) - YEAR(naissance) AS age, sexe FROM personnes
YEAR(date) : renvoie la partie année d'une date (AAAA-MM-JJ)
NOW() : renvoie un datetime correspondant à la date et heure courante
=> Fonctions de date et d'heure MySQL
Tu connais donc maintenant : le sexe et l'âge de la personne. Tu peux donc traiter en PHP, mais tu peux peut-être essayer de tout récupérer d'un coup :

Code : Tout sélectionner

SELECT id_personne, nom, prenom, YEAR(NOW()) - YEAR(naissance) AS age, sexe, nom_categorie FROM personnes p INNER JOIN categories c ON p.sexe = c.sexe AND YEAR(NOW()) - YEAR(naissance) BETWEEN c.age_min AND c.age_max
--> on sélectionne quelques infos dans la table personnes
--> on relie ces infos àla table catégorie par le sexe
--> et par l'âge : la catégorie dont l'age mini et l'age maxi encadre l'âge de la personne (à voir si on peut mettre un Between dans une jointure...)

A tester bien sûr parceque moi je ne peux pas mais comme ça ça a l'air possible ;)

Comme ça, la catégorie de la personne n'est pas physiquement représentée, elle est dynamiquement affichée en foncion de son age
Et tu peux facilement mettre à jour dans la table catégories : en rajouter, changer les noms etc...

Parceque ton système est actuellement basée sur des dates en dur, tu pourrais les intégrer à tes requêtes dynamiquement avec un petit calcul en PHP, mais cela te fait 1 requête par catégorie !
Autant faire tout ça avec MySQL, ce sera plus léger en code et plus rapide ;)

J'espère que ça t'aidera, en espérant que tout fonctionne, n'hésite pas à demander de l'aide si ça ne marche pas

++

Posté : 23 nov. 2005, 01:09
par Sebe
J'essayerai dès demain et je dirais si c'est bon.

Merci beaucoup pour tes conseilles.

Posté : 23 nov. 2005, 13:02
par Sebe
ouckileou,

J'ai une connaissance qui s'est penché sur la question et qui me conseille de travailler comme ceci:
Je verrais l'algorithme de lecture du genre

Tant que (AgePersonne>age)
lecture age
categoriePersonne=Descriptif
fin tantque
#ici à la sortie de la boucle, categoriePersonne contiendrait la categorie
de la personne en toutes lettres...

La récupération du recordset de la table des catégorie se ferait avec une
requête sql :
"Select * from CatgorieTable where sexe=:sx order by age" ### :sx
est une variable correspondant au sexe de la personne

Si la table dans la DB s'appellerait CategorieTable, composé des champs
age ,sexe et descriptif...
Il m'a posté son idée un peu en même temps que toi ... quels sont les avantages et les inconvénients de chaques méthodes?

Merci

Posté : 23 nov. 2005, 13:26
par ouckileou
Salut,

l'inconvénient de cette méthode sera à mon avis le temps de traitement.
Même si cela ne sera peut-être pas si significatif, tu as ici 2 requêtes SQL (liste des personnes + liste des catégories) et 2 belles boucles imbriquées :
- boucle sur la liste des personnes
- pour chaque personne, boucle sur la liste des catégories

Personnellement, je suis partisan d'en coller un maxium dans le SQL, c'est plus rapide, et tu as des résultats quasiment prêts à l'emploi :)

Posté : 23 nov. 2005, 18:44
par Sebe
Comme ma connaissance ne poste pas et m'envoit que des e-mails ... je le fais parler:
J'admet...

Il a raison... Il faudrait tout faire faire par SQL...
Maintenant, je connais mieux le SQL Oracle.. Hors tu as une DB MySQL...
J'imagine quand-même que tu pourrais récupérer directement le descriptif
avec la requête :


Select description from CatgorieTable where sexe=:sx and age<:ag and
rownum=1

rownum existe en Oracle... Je ne sais pas sous MySQL... En tous cas, tu
peux aussi récupéré le recordset de la requête précédente, et au niveau
php, récupérer directement le dernier enregistrement...
Avec des instructions du genre mysql_num_rows et ...
Faudrait que je me replonge dedans...
Donc, j'opte pour la méthode ouckileou.

Une petite question pour ma BD est-ce le type que j'ai choisi est bon?

Code : Tout sélectionner

id_categorie: [int(11)] + auto_increment nom_categorie: [char(3)] sexe: [char(1)] age_min: [int(11)] age_max: [int(11)]
Dois-je ajouter quelque chose dans les autres colonnes?

Merci

Posté : 23 nov. 2005, 19:40
par Sebe
Après avoir écrit ma BD, je me suis lancé dans le code
SELECT id_societaire, nom, prenom, sexe, YEAR(NOW()) - YEAR(naissance) AS age
FROM societaires p INNER JOIN soc_categories c  ON p.sexe = c.sexe AND YEAR(NOW()) - YEAR(naissance) BETWEEN c.age_min AND c.age_max
ORDER BY nom ASC
Mais voilà, j'ai un message d'erreur:

Code : Tout sélectionner

1052 Column: 'sexe' in field list is ambiguous
Qu'est-ce que je dois entendre par là?

Posté : 23 nov. 2005, 20:02
par ouckileou
Salut,

pour les types : tu as choisi Int, pour age_min et age_max, ainsi que pour l'id_categorie.
C'est un peu disproportionné :
"L'intervalle de validité pour les entiers signés est de -2147483648 à 2147483647."
Hors tu ne stockeras ici qu'un âge (de 0 à 150, voyons large) et tu n'auras a priori qu'une dizaine ou quinzaine de catégorie en tout. Tu pourrais donc choisir un type Tinyint, qui permet d'aller de 0 à 255 en UNSIGNED. Cela suffira et ça prendra moins de place disque (Tinyint 1 octet, Integer 4 octets, voir ici)
La documentation sur les types numériques :
http://dev.mysql.com/doc/refman/5.0/fr/ ... rview.html
C'est peut-être un peu chipoter, mais autant prendre de bonnes habitudes dès maintenant, il y a des cas ou c'est important de bien choisir ses types pour optimiser la base.

Quand à ton erreur, c'est très simple :
tu manipules dans ta requête 2 tables, qui contiennent toutes les deux une colonne "sexe". Il faut donc indiquer de quelle table tu parles lorsque tu mentionnes cette colonne ;)
Comme ici :

Code : Tout sélectionner

ON p.sexe = c.sexe
Au passage tu as gardé "p" et "c" comme alias pour tes tables, mais tu n'as pas les mêmes noms de tables, tu devrais peut-être adapter tes alias à tes noms, ce sera plus clair pour toi

Posté : 23 nov. 2005, 20:54
par Invité
Quand à ton erreur, c'est très simple :
tu manipules dans ta requête 2 tables, qui contiennent toutes les deux une colonne "sexe". Il faut donc indiquer de quelle table tu parles lorsque tu mentionnes cette colonne ;)
Comme ici :

Code : Tout sélectionner

ON p.sexe = c.sexe
Au passage tu as gardé "p" et "c" comme alias pour tes tables, mais tu n'as pas les mêmes noms de tables, tu devrais peut-être adapter tes alias à tes noms, ce sera plus clair pour toi
Là, je suis dans le flou. J'ai pourtant donné le nom des tables ... je ne comprends pas!
SELECT id_societaire, nom, prenom, sexe, YEAR(NOW()) - YEAR(naissance) AS age
FROM societaires p 
INNER JOIN soc_categories c  ON p.sexe = c.sexe AND YEAR(NOW()) - YEAR(naissance) BETWEEN c.age_min AND c.age_max
ORDER BY nom ASC 
Faut-il ajouter ceci 'soc_categories.sexe' à la place de 'c.sexe'?

Merci

Posté : 23 nov. 2005, 21:06
par ouckileou
Là, je suis dans le flou. J'ai pourtant donné le nom des tables ... je ne comprends pas!
Non, pas dans le "sexe" du SELECT ;)

Ce que j'ai cité c'est ce qu'il faut faire, c'est bon, il faut faire la même chose à chaque fois que tu cites la colonnes "sexe" (et toute colonne présente dans plusieurs tables utilisées dans une requête)

Posté : 23 nov. 2005, 23:30
par Sebe
Y'a un truc qui ne va pas ou qui me dépasse!
SELECT societaires.id_societaire, societaires.nom, societaires.prenom, societaires.sexe, YEAR(NOW()) - YEAR(societaires.naissance) AS age
FROM societaires
ORDER BY societaires.nom
Quand je teste cela fonctionne mais j'ai tous les sexe 'f' repris et seulement cela!
SELECT societaires.id_societaire, societaires.nom, societaires.prenom, societaires.sexe, YEAR(NOW()) - YEAR(societaires.naissance) AS age
FROM societaires
INNER JOIN soc_categories ON societaires.sexe = soc_categories.sexe AND YEAR(NOW()) - YEAR(societaires.naissance) BETWEEN soc_categories.age_min AND soc_categories.age_max
WHERE societaires.sexe = 'H'
ORDER BY societaires.nom ASC
Ici, je n'ai plus personne ! ! !

De plus, je n'ai pas de variable pour récupérer ma catégorie syle 'AS categorie' ... où ai-je fait une erreur?

Merci

Posté : 23 nov. 2005, 23:55
par ouckileou
La deuxième requête, c'est plus logique qu'il y ait des problèmes, je ne suis pas complètement sûr de ce que je t'ai donné :)

Par contre le première est un bête SELECT, ça devrait fonctionner... et tu dis que tu n'as que des lignes 'F' en résultat ? et il y a bien des lignes 'H' dans ta table ?

Pour récupérer la variable catégorie, il faut que tu indiques dans le SELECT de sélectionner la colonne "nom_categorie"

J'aimerais bien tester pour oir si le Between marche dans une jointure, est-ce qu'il serait possible d'avoir le SQL de tes tables ? Au moins la structure si tu ne peux pas partager les données

Posté : 24 nov. 2005, 00:16
par Sebe
Ceci ne me donne rien sauf si j'enlève [WHERE societaires.sexe = 'H'] alors, il me donne toutes les 'f' avec leur catégorie!
SELECT societaires.id_societaire, societaires.nom, societaires.prenom, societaires.sexe, YEAR(NOW()) - YEAR(societaires.naissance) AS age, soc_categories.nom_categorie
FROM societaires INNER JOIN soc_categories ON societaires.sexe = soc_categories.sexe AND YEAR(NOW()) - YEAR(societaires.naissance) BETWEEN soc_categories.age_min AND soc_categories.age_max
WHERE societaires.sexe = 'H'
ORDER BY societaires.nom ASC
Pour le reste, je t'ai envoyé via e-mail

Posté : 24 nov. 2005, 00:35
par ouckileou
Cette requête fonctionne très bien chez moi :

Code : Tout sélectionner

SELECT s.id_societaire, s.nom, s.prenom, s.sexe, YEAR(NOW()) - YEAR(s.naissance) AS age, s_c.nom_categorie FROM societaires s INNER JOIN soc_categories s_c ON s.sexe = s_c.sexe AND YEAR(NOW()) - YEAR(s.naissance) BETWEEN s_c.age_min AND s_c.age_max WHERE s.sexe = 'H' ORDER BY s.nom ASC
Et c'est exactement la même que celle que tu as collé au dessus, je l'ai juste allégée avec les alias

Voici quelques résultats que j'obtiens (sans les noms) :

Code : Tout sélectionner

7 H 35 SE 20 H 51 V2 21 H 44 V1 23 H 13 EH 2 H 39 SE 1 H 55 V2 25 H 15 EH 6 H 31 SE 3 H 42 V1
Dans ta table catégorie, l'age minimum d'une catégorie est bien l'âge maximum+1 de la catégorie précédente ? On ne sait jamais c'est peut-être ça qui gêne...

Posté : 24 nov. 2005, 00:46
par Sebe
J'ai fait un copié/collé pour être certain de ne pas faire d'erreur et c'est identique à avant!