Eviter des milliers de UPDATE

WRInaute impliqué
Bonjour tout le monde,,

Alors voilà j'aurais un petit problème, en fait je désire faire un classement de joueurs.

Ceux-ci sont tout d'abord classés selon leur nombre de points en ORDER BY points DESC. J'ai donc la position de ceux-ci dans un tableau. Mon problème est que je dois "updater" le champ "ranking" de chacun des membres.

Ce que je n'arrive pas à faire c'est une requète unique qui pourrait "updater" tous les ranking en une fois. C'est à dire quelque chose qui éviterait ca :

Code:
<?php 
foreach($classement as $membre => $ranking) { 
mysql_query('UPDATE membres SET ranking = "'.$ranking.'" WHERE login = "'.$membre.'"');
}
?>

Ce qui ferait des milliers de requêtes avec tous les membres !

Merci d'avance
 
WRInaute discret
je crois que vous n'avez pas d'autre solution...dans tous les cas si la base est bien faite c'est pas forcéement lent.une base de données est faite pour ça
pensez à indexer le champ login
 
WRInaute passionné
fredsoft a dit:
je crois que vous n'avez pas d'autre solution...dans tous les cas si la base est bien faite c'est pas forcéement lent.une base de données est faite pour ça
pensez à indexer le champ login

En règle générale il faut éviter de faire des requêtes dans des boucles.
Pour ma part c'est devenu une règle de base :
Jamais de requêtes dans une boucle.

mowmow, Est-ce que tu peux nous poster la structure de tes tables, cela nous aidera à trouver la requête qui va bien.
 
WRInaute occasionnel
il faut faire de sorte que la formule de calcul soit dans la requête unique:
UPDATE membres SET ranking =
(select fromule from tables
WHERE .... and table1.login=memebres.login)
 
WRInaute impliqué
En fait après avoir classé les membres selon leur nombre de points, j'update leur position dans le champ ranking dans la meme table.

nodom a dit:
il faut faire de sorte que la formule de calcul soit dans la requête unique:
UPDATE membres SET ranking =
(select fromule from tables
WHERE .... and table1.login=memebres.login)

Ca m'a l'air intéressant, comment je pourrais faire en sorte que ca fasse comme je le voudrais ? Parce que je ne connais pas trop cette syntaxe :(
 
WRInaute impliqué
Il faudrait déjà que ton serveur fasse tourner une version mysql qui admet les sous-requètes, ce qui me parait compromis sur un serveur de prod.
 
WRInaute occasionnel
mowmow
comment vous faites pour classer les membres? l'idée d'integrer la méthode de classement dans la requête si c'est possible.
 
WRInaute impliqué
nodom a dit:
mowmow
comment vous faites pour classer les membres? l'idée d'integrer la méthode de classement dans la requête si c'est possible.
SELECT 1 FROM membres ORDER BY points DESC

Pour les sous requêtes ca devrait pas être un problème j'ai un dédié
 
WRInaute passionné
mowmow, si tu veux que l'on puisse t'aider :
- bout de code php
- structure des tables

... ça va plus vite, qu'un échange de post
 
WRInaute impliqué
Lol ok c'est tout simple

===================
STRUCTURE TABLE MEMBRES
===================

- login
- ranking
- points

Requête : SELECT login FROM membres ORDER BY points DESC

PHP :
Code:
while($row = mysql_fetch_assoc($sql)) 
$array[] = $row['login'];

Et après je voudrais updater tous les "ranking" des membres avec la clé de array qui représente leur position.

Voilà voilà, je voudrais savoir si c'est possible en une seule requête.

Désolé de pas avoir été assez clair ...[/code]
 
WRInaute occasionnel
c'est un peut compliqué mais pas impossible. mais je pense que vous pouvez faire autrement. enfin pour vous le rank c'est la position du memebre en fonction des points
faites ça alors

update membres set rank=f(points)

à la place de f(points) imaginez une fonction.
qui donne des valeurs qui vont le mieux avec votre besoins.
exemple: des valeurs de 0 à 1
"update membres set rank=points/".$max;

$max le nombre de points le plus grand
calculé ave select max(points) from membres
...
 
WRInaute impliqué
Pas mal du tout !! J'y aurais pas du tout pensé ! Merci beaucoup nodom :)

Et après pour récupérer la position réelle, j'ai plus qu'à faire ranking * max_points ! Merci ^^
 
WRInaute impliqué
Question : cette colonne a-t-elle réellement une utilité, puisque le tri obtenu directement depuis le nombre de points donnera exactement le même résultat ?
 
WRInaute impliqué
Après ma requête, j'enregistre le classement (updaté tous les jours à minuit) dans un fichier texte, et j'ai un lien "Voir ma position" qui transfère le joueur sur la page du classement où il apparaît ;)
 
WRInaute passionné
J'ai une solution en 3 requêtes, avec création d'une table intermédiaire rank.
Il existe certainement une solution plus élégante.
Tu devras optimiser les index sur les tables,
et ensuite tester pour savoir si cette méthode est plus rapide ou pas, par rapport à des milliers d'update dans une boucle :

Cette solution gére les cas d'égalité :
- 2 membres ayant le même nbr de point ont le même rank

Structure des tables :
Code:
-- 
-- Structure de la table `membres`
-- 
CREATE TABLE `membres` (
  `login` varchar(50) NOT NULL default '',
  `rank` int(11) NOT NULL default '0',
  `points` int(11) NOT NULL default '0',
  PRIMARY KEY  (`login`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- 
-- Contenu de la table `membres`
-- 

INSERT INTO `membres` VALUES ('Log1', 0, 55);
INSERT INTO `membres` VALUES ('Log2', 0, 12);
INSERT INTO `membres` VALUES ('Log3', 0, 55);
INSERT INTO `membres` VALUES ('Log4', 0, 28);
INSERT INTO `membres` VALUES ('Log5', 0, 45);
INSERT INTO `membres` VALUES ('Log6', 0, 150);
INSERT INTO `membres` VALUES ('Log7', 0, 5);
INSERT INTO `membres` VALUES ('Log8', 0, 3);

-- --------------------------------------------------------
-- 
-- Structure de la table `rank`
-- 

CREATE TABLE `rank` (
  `IDRank` int(11) NOT NULL auto_increment,
  `points` int(11) NOT NULL default '0',
  PRIMARY KEY  (`IDRank`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Etape 1 : je vide la table rank.
Code:
TRUNCATE TABLE `rank`

Etape 2 : Insertion dans la table rank, des points des membres, avec un GROUP BY sur `points` pour tenir compte des ex-aequo
Code:
INSERT INTO `rank` ( `points` )
SELECT `points`
 FROM membres 
GROUP BY `points` 
order by `points` desc

3. Mise à jour du rank de la table membre, en faisant une jointure sur le nombre de points de chaque membre.

Code:
UPDATE `membres`, `rank` 
SET `rank` = `IDRank`
WHERE `membres`.`points` = `rank`.`points`

Résultat final :

Code:
Log6 	1 	150
Log1 	2 	55
Log3 	2 	55
Log5 	3 	45
Log4 	4 	28
Log2 	5 	12
Log7 	6 	5
Log8 	7 	3

Ce n'est certainement pas la meilleure solution, c'est juste une solution
Il faut tester les perfs sur des milliers d'enregistrement (pense aux index)
Dans la gestion des égalité, ne gère pas les "trous"
Est-ce que tu veux que Log5, ait comme rang 3, ou alors le rang 4 ( pour tenir compte de l'égalité sur le rang 2).
 
WRInaute impliqué
Merci pour cette solution, mais en effet, une fonction serait plus rapide et moins compliquée :) Oui je préférerais que Log5 aie le rang 4 :)

Par contre le seul problème se pose au niveau des égalités dans la fonction ... Je ne sais pas comment je pourrais gérer ca correctement car en fait j'affiche 20 positions par page, et si 3 personne sont ex eaquo à la position 20,21,22 ... Je ne saurais pas la position exacte.
 
WRInaute passionné
tient nous au courant quand tu auras trouvé ta solution,
et si elle n'est pas ultra confidentielle, poste là :)
à titre perso ça m'intéresse de savoir quelle solution tu auras implémenté
 
WRInaute passionné
mowmow a dit:
Je pense en effet intégrer ta solution car avec une fonction, il y'a en effet le problème du ex eaquo ;)

Le pb des ex-aequo, n'est pas résolu avec la solution que j'ai proposée, il faudrat certainement l'adapter.

Le pb de la fonction, pour moi qui ne suis pas très doué en mathématiques, c'est qu'il faut trouver une bijection décroissante de I vers I, dont l'image est une séquence d'entier ( 1, 2, ......etc).
En fait je n'ai pas compris comment rank = points / max(points) pouvait ensuite être transformé en 1, 2, 3 ......
C'est pour ça que ta solution avec une fonction m'intéresse, ça me permettra de progresser
:D
 
WRInaute impliqué
spidetra a dit:
mowmow a dit:
Je pense en effet intégrer ta solution car avec une fonction, il y'a en effet le problème du ex eaquo ;)

Le pb des ex-aequo, n'est pas résolu avec la solution que j'ai proposée, il faudrat certainement l'adapter.

Le pb de la fonction, pour moi qui ne suis pas très doué en mathématiques, c'est qu'il faut trouver une bijection décroissante de I vers I, dont l'image est une séquence d'entier ( 1, 2, ......etc).
En fait je n'ai pas compris comment rank = points / max(points) pouvait ensuite être transformé en 1, 2, 3 ......
C'est pour ça que ta solution avec une fonction m'intéresse, ça me permettra de progresser
:D

Oui c'est vrai en fait j'avais pas vu que ca donnerait pas les positions, j'ai un peu la tête ailleurs en ce moment.

Pour les problèmes d'ex-aequo j'ai pensé à cette solution : on met dans la table rank le nom du membre + les points, et après on récupère l'IDRank du membre pour l'update, ca marcherait bien comme ca non ?
 

➡️ Offre MyRankingMetrics ⬅️

pré-audit SEO gratuit avec RM Tech (+ avis d'expert)
coaching offert aux clients (avec Olivier Duffez ou Fabien Faceries)

Voir les détails ici

coaching SEO
Discussions similaires
Haut