Page 1 sur 1

Mise à jour d'un champ en fonction d'un autre

Posté : 10 nov. 2007, 18:39
par Ultim4T0m
Bonjour à tous,

Commençons tout de suite par la table :

Code : Tout sélectionner

CREATE TABLE `test` ( `points` int(14) NOT NULL default '0', `classement` int(9) NOT NULL default '0' )
J'aimerais que le champ classement contienne la position de l'enregistrement.

La requête doit être toute simple, mais je n'ai pas réussi à trouver quelque chose de pertinent...

J'ai du mal à exprimer le problème, un petit exemple serait peut-être bienvenu :

nom = Jean
points = 400

nom = Jacques
points = 600

Après exécution de la requête, Jacques aura pour `classement` 1 et Jean 2.
La requête se fait sur tous les enregistrements de la table, en les triant par `points` décroissants et en mettant la position dans `classement`

J'ai testé, sans succès un peu tout ce que je pensais, mais n'étant pas encore suffisamment calé là dedans, ça ne marche évidement pas ^^

En espérant avoir été compréhensible ^^'

Merci d'avance (je ne demande pas forcement un code fonctionnel, mais ne serait-ce savoir si c'est envisageable, et avoir une piste pour savoir dans quelle direction aller, merci :))

Posté : 10 nov. 2007, 18:53
par h0_noMan
Dans quel ordre recoit tu les enregistrements ?

Donne un exemple.

Posté : 10 nov. 2007, 19:00
par Ultim4T0m
Ils sont dans l'ordre dans lequel ils sont enregistrés, à savoir la clé primaire en auto_increment.

id | points | classement
---------------------------
1 | 93400 | 2
2 | 49489 | 1

Il faudait dans ce cas là, qu'après requête (un UPDATE donc), on obtienne

id | points | classement
---------------------------
1 | 93400 | 1
2 | 49489 | 2

Il faut recalculer le `classement` à partir des points. Je ne sais pas si c'est faisable en une seule requête SQL, mais vu la puissance de ce langage, je me doute que si ^^'

J'espère que c'est un peu plus clair... :/

Posté : 10 nov. 2007, 19:24
par h0_noMan
Pourquoi as tu besoin d'avoir le classement de chaque participants dans la base ?

Posté : 10 nov. 2007, 19:29
par Ultim4T0m
Il est clair que pour afficher la liste des utilisateurs, il suffit de faire une boucle :
$sql = mysql_query("SELECT * FROM membres ORDER BY points DESC");
while($row=mysql_fetch_assoc($sql))
  // Affichage ligne par ligne


Mais pour que la position de chaque utilisateur soit affichée directement sur son compte, il faut que la position soit stockée directement dans la table.

Posté : 10 nov. 2007, 21:23
par Hubert Roksor
Le plus simple serait d'utiliser un sous-requête pour compter le nombre d'utilisateurs ayant un plus grand nombre de points.

Code : Tout sélectionner

UPDATE membres SET classement = ( SELECT 1 + COUNT(*) FROM membres AS m2 WHERE m2.points > membres.point )
Ce n'est pas rapide (c'est même lent) mais si tu n'exécutes cette requête que rarement ça ne devrait pas poser de problèmes. Sinon on doit pouvoir trouver des formes plus optimisées comme

Code : Tout sélectionner

UPDATE membres m JOIN ( SELECT m1.id, 1 + COUNT(*) AS classement FROM membres m1 JOIN membres m2 ON m2.points > m1.points GROUP BY m1.id ) AS tmp SET m.classement = tmp.classement WHERE m.id = tmp.id
...ou encore des trucs avec des variables et un peu de magie, mais inutile de trop se prendre la tête sur le problème.
pour que la position de chaque utilisateur soit affichée directement sur son compte, il faut que la position soit stockée directement dans la table.
Pas forcément, tu peux la récupérer directement avec

Code : Tout sélectionner

SELECT 1 + COUNT(*) AS classement FROM membres WHERE points > 123
...où 123 est le nombre de points du membre. D'un autre côté, évite de faire ça sur toutes les pages de ton site, ce serait plutôt lent.

Posté : 10 nov. 2007, 23:39
par Ultim4T0m
Justement, j'ai besoin de ce nombre sur toutes les pages.

Pour ce qui est du temps d'exécution de la requête, ce n'est pas un soucis, je compte la mettre en cron, qui s'exécuterait toutes les 10mn.

La première requête que t'as fait, j'étais pas si loin =(

En tout, merci Hubert, je vais regarder ça de plus prêt.

Posté : 10 nov. 2007, 23:52
par Hubert Roksor
Toutes les dix minutes ça va faire vraiment rapproché pour ce genre de requêtes et tu risques d'avoir des problèmes, je pensais plutôt à quelque chose mis à jour chaque nuit. Il va te falloir trouver quelque chose de moins intensif comme traitement exécuté à chaque changement de points d'un joueur, par exemple.

Posté : 10 nov. 2007, 23:57
par Ultim4T0m
Hum, ou tout simplement passer par le biais du php peut-être, quelque chose de ce style serait trop lourd tu crois ?

A mon avis, c'est plus une usine à gaz qu'autre chose, mais sait-on jamais ^^'
$sql = mysql_query("SELECT id FROM membres ORDER BY points DESC");
for($i=0; $row=mysql_fetch_assoc($sql); $i++)
 mysql_query("UPDATE membres SET classement=".$i." WHERE id=".$row['id']);
J'ai testé les deux requêtes, en faisant quelques modifications, mais rien n'y fait, elles ne fonctionnent pas. Ma version de MySQL en serait à l'origine ? MySQL 4.0 il me semble

Posté : 11 nov. 2007, 00:38
par Hubert Roksor
L'avantage d'un SELECT, c'est qu'il bloque le serveur beaucoup moins de temps. Et tu n'es pas obligé de faire un UPDATE pour tous les membres, seulement ceux qui ne sont pas à la bonne place. Le meilleur algorithme dépend du type de scores et la façon dont ils évoluent, mais tu dois pouvoir t'en sortir avec quelque chose comme
$result = mysql_query('SELECT id, points, classement FROM membres ORDER BY points DESC'); 

$classement = 0;
$points = 0;
$n = 1;

while ($row = mysql_fetch_assoc($result))
{
	if ($row['points'] == $points)
	{
		++$n;
	}
	else
	{
		$points = $row['points'];
		$classement += $n;
		$n = 1;
	}

	if ($row['classement'] != $classement)
	{
		mysql_query('UPDATE membres SET classement = ' . $classement . ' WHERE id = ' . $row['id']);
	}
}
Au cas où ce ne soit pas évident, le truc avec $n c'est pour que plusieurs membres de même scores soient ex-æquo et ajouter les places sautées au score suivant.
J'ai testé les deux requêtes, en faisant quelques modifications, mais rien n'y fait, elles ne fonctionnent pas. Ma version de MySQL en serait à l'origine ? MySQL 4.0 il me semble
C'est pas impossible. En même temps, sans la version de MySQL et sans le message d'erreur j'aurais du mal à dire.

Posté : 11 nov. 2007, 16:47
par Ultim4T0m
Merci beaucoup, ça marche très bien :)

Et oui, désolé pour mon imprécision quant à la version SQL, mais je faisais la requête directement en php avec un mysql_error().