JeuWeb - Crée ton jeu par navigateur
[SQL] Récupérer un champ aléatoire - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : [SQL] Récupérer un champ aléatoire (/showthread.php?tid=3408)

Pages : 1 2 3


RE: recuperer un Champ de base de donnees au hasard - Seren - 10-12-2008

Si j'ai fait la remarque, c'est pour voir ce que la solution valait dans d'autre circonstances, pas uniquement dans ce cas là. C'est toujours intéressant d'élargir le problème et de savoir dans quelles situations ont peut utiliser quelles solutions.


RE: recuperer un Champ de base de donnees au hasard - keke - 10-12-2008

C'est rigolo car moi j'utilise jamais le RAND () en SQL ^^.

Mais ma problématique est peut-être différente car toutes les villes n'ont pas le même poids au niveau de la probabilité d'apparition.
Les villes qui ont une cathédrale ont un poids de 35
les villes avec un temple ont un poids de 8
les villes avec une mission (un simple batiment avec une croix si vous voulez ...) ont un poids de 2.

Plus le poids est important, plus la probabilité d'apparition est importante.

Ainsi si une religion a une cathédrale et une mission, le joueur a 35 chances sur 37 d'apparaitre dans la cathédrale et seulement 2 chances sur 37 d'apparaitre dans la mission.

En précode ça donne
Code :
Je récupère dans un tableau toutes les villes où le joueur peut apparaitre et son poids
J'additionne tous les poids dans une variable $poids_total

Je calcule une variable entre 0 et poids total.
Je scrute la liste ordonnée de ville (peut importe l'ordre ... il faut juste qu'on passe pas 2 fois sur la même ville) et je retranche le poids associé. Dès que j'atteinds une valeur négative ... la ville est choisie.

Ma liste des villes n'est jamais amené à dépasser les 100 enregistrements ... aussi je peux me permettre ce genre de méthode qui serait ruineuse dans le cas de 25 M de lignes !

Kéké


RE: recuperer un Champ de base de donnees au hasard - Karedas - 10-12-2008

(10-12-2008, 03:49 PM)Sephi-Chan a écrit :
  • D'après le contexte qui nous est donné, il y a plusieurs personnages pour une ville, donc la table ville contiendra assez peu d'enregistrement.
  • De plus, cette requête n'est appelée qu'à l'inscription, on peut donc se permettre d'utiliser une requête un peu gourmande pour conserver un code clair.
  • Enfin, le script PHP proposé peut poser problème si les identifiants de villes ne se suivent pas. C'est donc plutôt lourd.
Voilà pourquoi je maintiens que — dans ce cas — la requête que je propose est meilleure. Smile

En effet dans le cas d'une mini table, c'est une requete idéale, je m'en sert souvent, mais je suis pas d'accord sur 2 points.

-On ne peut pas se permettre d'utiliser une requête un peu gourmande pour gagner en clareté du code. Du moins si on développe dans l'interêt de l'utilisateur. De plus garder un code clair est théoriquement possible en toute circonstance.
J'ai une base qui se prend 500 requêtes à la seconde en quasi permanence, crois moi, je fais la chasse aux requêtes gourmandes en permanence sinon la bdd :rip:.

-Si les identifiants ne se suivent pas (et même s'ils se suivent théoriquement, on sait jamais ce qui arrive), et qu'on est sur une grosse table donc avec un rand() proscrit, on fera:
Code PHP :
<?php 
$rand
= rand(1,$mysql->sqlunique("select count(*) from villes"));
$ville = $mysql->sqlunique("select ame_villes from villes limit ".$rand.",1");
Ca tient en 2 ligne et 2 requête.

Si on l'utilise souvent on peut même modifier sa classe mysql pour ajouter une method sqlrandom par exemple.
Code PHP :
<?php 
function sqlrandom($query,$table,$quantite=1)
{
$rand = $this->sqlunique("select count(*) from ".$table);
return
$this->query($query.' limit '.$rand.','.$quantite);
//dans ma classe mysql la methode query renvoi l'identifiant du recordset qu'on pourra utiliser ensuite pour lire
}
[/php]


RE: recuperer un Champ de base de donnees au hasard - Sephi-Chan - 10-12-2008

(10-12-2008, 05:08 PM)Karedas a écrit :
(10-12-2008, 03:49 PM)Sephi-Chan a écrit :
  • D'après le contexte qui nous est donné, il y a plusieurs personnages pour une ville, donc la table ville contiendra assez peu d'enregistrement.
  • De plus, cette requête n'est appelée qu'à l'inscription, on peut donc se permettre d'utiliser une requête un peu gourmande pour conserver un code clair.
  • Enfin, le script PHP proposé peut poser problème si les identifiants de villes ne se suivent pas. C'est donc plutôt lourd.
Voilà pourquoi je maintiens que — dans ce cas — la requête que je propose est meilleure. Smile

En effet dans le cas d'une mini table, c'est une requete idéale, je m'en sert souvent, mais je suis pas d'accord sur 2 points.

-On ne peut pas se permettre d'utiliser une requête un peu gourmande pour gagner en clareté du code. Du moins si on développe dans l'interêt de l'utilisateur. De plus garder un code clair est théoriquement possible en toute circonstance.
J'ai une base qui se prend 500 requêtes à la seconde en quasi permanence, crois moi, je fais la chasse aux requêtes gourmandes en permanence sinon la bdd :rip:.

-Si les identifiants ne se suivent pas (et même s'ils se suivent théoriquement, on sait jamais ce qui arrive), et qu'on est sur une grosse table donc avec un rand() proscrit, on fera:
Code PHP :
<?php 
$rand
= rand(1,$mysql->sqlunique("select count(*) from villes"));
$ville = $mysql->sqlunique("select ame_villes from villes limit ".$rand.",1");
Ca tient en 2 ligne et 2 requête.

Si on l'utilise souvent on peut même modifier sa classe mysql pour ajouter une method sqlrandom par exemple.
Code PHP :
<?php 
function sqlrandom($query,$table,$quantite=1)
{
$rand = $this->sqlunique("select count(*) from ".$table);
return
$this->query($query.' limit '.$rand.','.$quantite);
//dans ma classe mysql la methode query renvoi l'identifiant du recordset qu'on pourra utiliser ensuite pour lire
}
[/php]

Je ne sais pas ce que fait la méthode sqlunique()… Peux-tu nous en coller la source s'il te plaît ?

Pour moi l'argument ne tient toujours pas la route. Pour avoir une perte de performance, il faudrait que la requête soit exécutée plus souvent et sur un table énorme (même sur 300 000 champs on a vu que ça restait dérisoire).
Faire une requête, un peu de PHP puis une autre requête me semble pire.

Un petit détail : tu fais la chasse à l'optimisation, mais tu utilises rand() au lieu de mt_rand(). :heuuu:


(10-12-2008, 04:13 PM)Seren a écrit : Si j'ai fait la remarque, c'est pour voir ce que la solution valait dans d'autre circonstances, pas uniquement dans ce cas là. C'est toujours intéressant d'élargir le problème et de savoir dans quelles situations ont peut utiliser quelles solutions.
Tu as bien raison. J'argumentais juste sur "Pourquoi cette méthode est pas une autre". ^^'

Kéké, pourquoi ne pas faire faire cette sélection depuis MySQL ? Comme cela, par exemple :
Code PHP :
<?php 
SELECT villes
.*
FROM villes
WHERE value
= (
SELECT MAX(value)
FROM villes
)
ORDER BY RAND()
LIMIT 1


Sephi-Chan


RE: [SQL] Récupérer un champ aléatoire - Faelnor - 10-12-2008

je pense pas dépasser les 100 villes, je compte mettre 400 personne environs par ville ce qui fait 400*100= 40 000 avant d'arriver a sa en inscrit j'ai le temps^^


RE: recuperer un Champ de base de donnees au hasard - Karedas - 10-12-2008

Je suis tout à fait d'accord pour mt_rand, c'est juste un viel automatisme que j'ai pas encore remplacé dans ma tête, rand vient avant mt_rand quand j'ai besoin d'un random, ce qui est en effet pas génial.

Voilà pour sqlunique:

Code PHP :
<?php 
function sqlunique($query = '')
{
$id = mt_rand();
//on execute la requete
$this->requete($query,$id);
//on fetch row et on renvoi le seul résultat
$x = $this->fetch_row($id);
return
$x[0];
}

Ca me permet de récuperer les requetes ne renvoyant q'un seul champ sur une seul ligne directement dans une variable, histoire de pas me coller des tas de ligne à chaque fois que je veux une valeur simple.

J'ai aussi sqlexiste pour tester l'existence de données, ça renvoi juste vrai ou faux.
Je me met toujours des petites méthodes comme ça qui éclaircissent le code. L'objet mysql étant créé dans un fichier prepend, si j'ai besoin d'infos simples, le plus souvent une ligne suffit.

Pour le rand() sur 300000 lignes, ne le prend pas mal mais je maintient qu'une requête de 600ms c'est pas insignifiant du tout, 2 comme ça et tu t'ajoute 1.2 secondes au traitement du script, alors que 2 requêtes instantanées et un bout de php on doit tourner à 20ms en comptant large.


RE: recuperer un Champ de base de donnees au hasard - keke - 10-12-2008

(10-12-2008, 05:20 PM)Sephi-Chan a écrit : Kéké, pourquoi ne pas faire faire cette sélection depuis MySQL ? Comme cela, par exemple :
Code PHP :
<?php 
SELECT villes
.*
FROM villes
WHERE value
= (
SELECT MAX(value)
FROM villes
)
ORDER BY RAND()
LIMIT 1


Sephi-Chan

Je suis un peu limité actuellement ... j'ai un gros rhume de cerveau.
Heu, je vois pas où apparait les poids des batiments dans la requête.

La requête indiquée semble renvoyer une des villes dont le poids est maximum ..
Dans mon cas, je veux juste donner plus de chance au ville de fort poids, qu'aux autres.
Une requête est cependant peut-être réalisable ... mais elle me semble plus capilotrackée qu'un mixte entre SQL et PHP.

Bonne journée à tous !
kéké qui a cru toute la journée qu'il était un Vendredi ... "Ouin ! je bosse demain !"


RE: [SQL] Récupérer un champ aléatoire - Karedas - 10-12-2008

keke tu as dis que tu récupérait toutes les villes possible dans une table, pourquoi pas à la place tout les poids possibles (donc moins de données).

ensuite tu fait un random sur le carré de la dimension du tableau des poids
et tu prend une ville au hasard parmis les villes ayant pour poid le poid à l'indice arrondi sup de la racine de ton random.
Voilà les probabilité pour 10 poids possibles

1 1.00%
2 3.00%
3 5.00%
4 7.00%
5 9.00%
6 11.00%
7 13.00%
8 15.00%
9 17.00%
10 19.00%

Plus haut ça deviens moins valables, si tu as 35 poids possibles les 6 premiers indices ne dépassent pas le pourcent.

Vite fais sur le gaz en mélange algo/php
Code PHP :
<?php 
$poids
= recordset des poids possibles
$rand
= ceil(sqrt(mt_rant(sizeof($poids)*sizeof($poids))));
$ville = resultat de 'select ville from ville where poid ='.$poids[$rand]' order by rand() limit 1';

Mais autant c'est pas ce qu'il y'a de mieux, y'a surement des triturages mathématique meilleurs pour un random pondéré.


RE: [SQL] Récupérer un champ aléatoire - Sephi-Chan - 10-12-2008

Exact Kéké, j'avais oublié que tu souhaitais pondérer ton random et non envoyer tes joueurs sur les villes de plus fort poids. Smile

Karedas, je ne vois pas trop quoi ajouter, c'est simplement une question de quantité et d'ordres de grandeur.
  • Dans le cas présent, le ORDER BY RAND() est pratique et simple.
  • Quand on dépasse quelques dizaines de milliers d'entrées, ta technique devient beaucoup plus viable (en pratique, je n'ai jamais eu à le faire sur de si grosses tables). Il suffit alors de faire boucler ta requête jusqu'à ce que le nombre de champs renvoyés (mysql_num_rows()) vaille 1 pour éviter les incidents sur les tables aux identifiants non contigus. En pratique, cette boucle n'effectuera qu'une seule itération dans la grande majorité des cas.

Je viens de voir un petit article ORDER BY RAND() Optimisation sur la question. L'approche est intéressante.


Sephi-Chan


RE: [SQL] Récupérer un champ aléatoire - Ter Rowan - 10-12-2008

je viens de lire l'article que tu présentes en lien Sephi, et je me demande quand même si on a là un bon aléatoire. En effet, cela défavorise fortement les extrêmes

et cela réduit encore plus l'aléatoire quand on cherche x nombres alétaoires (plus x est important, moins on a d'aléatoire)

bref, je ne maîtrise pas le côté performance de la chose, mais d'un point de vue "statistique", intuitivement, je trouve cela très bof Smile