Willekeurige prijzen uitkeren aan gebruikers
Ik ben bezig met het maken van een online loterij, wat bedoelt is als kleine
event / activiteit. Nu lukt het mij niet om willekeurige prijzen uit te keren aan gebruikers.
Ik deed het zo, ik kon geen andere manier bedenken dan deze:
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
$sql = (mysql_query("SELECT * FROM loteryprijzen ORDER BY rand()"));
while($row = mysql_fetch_assoc($sql)) {
mysql_query("UPDATE `lotery` SET `icon`='".$row['icon']."', `price`='".$row['itemname']."'");
}
?>
$sql = (mysql_query("SELECT * FROM loteryprijzen ORDER BY rand()"));
while($row = mysql_fetch_assoc($sql)) {
mysql_query("UPDATE `lotery` SET `icon`='".$row['icon']."', `price`='".$row['itemname']."'");
}
?>
Je zult vast het probleem al zien, namelijk, er word 1 random prijs gekozen, en die word aan alle deelnemers gegeven.
De bedoeling is, als er bijvoorbeeld 5 deelnemers zijn, dat de prijzen random worden verdeeld :
Winnaar 1 wint : 50 munten
Winnaar 2 wint : 30 munten
winnaar 3 wint : een gelukskoekje
" "
" "
Wanneer de loterij is afgelopen, gebruik ik het zelfde methode, om 5 winnaars te selecteren.
Weet iemand een betere / goede manier?
Alvast bedankt.
Groeten,
Marco
Gewijzigd op 15/05/2013 23:45:00 door Marco Eilander
http://www.webtrenches.com/post.cfm/avoid-rand-in-mysql
Als in jouw database tabel slechts vijf rijen zijn en dit ook zo blijft, dan zal je code denk ik prima zijn. Zou je echter duizend deelnemers hebben, dan gaat die rand() functie flink aan de performance vreten.
Dan even wat verder gezocht: http://www.rndblog.com/how-to-select-random-rows-in-mysql/
Betere code lijkt dan het volgende resultaat te krijgen:
SELECT * FROM lotery WHERE RAND()<=[aantal winnaars/totaal aantal rijen] limit [aantal winnaars];
Nogmaals: voor een tabel met slechts 5 rijen zal dit verschil onmerkbaar zijn, maar 't is wel iets om in gedachte te houden.
Goh, na toch redelijk wat met mysql te maken gehad te hebben was 'order by rand()' mij nog onbekend. Uit de eerste zoekresultaten zie ik echter meteen al dingen als 'vermijd het gebruik hiervan'. Het schijnt niet echt goed te zijn voor de performance: Als in jouw database tabel slechts vijf rijen zijn en dit ook zo blijft, dan zal je code denk ik prima zijn. Zou je echter duizend deelnemers hebben, dan gaat die rand() functie flink aan de performance vreten.
Dan even wat verder gezocht: http://www.rndblog.com/how-to-select-random-rows-in-mysql/
Betere code lijkt dan het volgende resultaat te krijgen:
SELECT * FROM lotery WHERE RAND()<=[aantal winnaars/totaal aantal rijen] limit [aantal winnaars];
Nogmaals: voor een tabel met slechts 5 rijen zal dit verschil onmerkbaar zijn, maar 't is wel iets om in gedachte te houden.
Alvast bedankt! :)
Nu is dat stukje voor mij bekent, al zit ik nog wel met het probleem bij het uitkeren van de prijzen.
Ook dat gaat willekeurig, maar wil wel hebben dat ze daadwerkelijk verschillende prijzen krijgen en niet allemaal het zelfde, zoals het te zien is in mijn voorbeeld.
Quote:
Ik ben er de expert niet naar om dit te beantwoorden, maar in de eerste link die ik gaf, werd gezegd dat het meer dan een seconde duurde om ruim 5000 rijen op die manier te verwerken. Te verwachten is dat 50 rijen weinig voorstelt en dus nog steeds niet echt merkbaar is. er kunnen maximaal 50 deelnemers mee doen. Is het dan nog steeds handig om de rand() op mijn manier te gebruiken?
Het is echter nooit slecht om een betere aanpak te verkiezen. Stel dus dat je een onbekend aantal deelnemers hebt en hiervan 5 wil selecteren, dan denk ik dat je de volgende code zou kunnen gebruiken:
Quote:
maar wil wel hebben dat ze daadwerkelijk verschillende prijzen krijgen en niet allemaal het zelfde, zoals het te zien is in mijn voorbeeld.
Als ik jouw code goed begrijp, dan selecteer jij eerst 5 willekeurige rijen uit loteryprijzen. Vervolgens doorloop je deze rijen en bij iedere rij update je alle rijen in lotery. Het effect is vanzelfsprekend: de laatste rij zorgt ervoor dat iedereen in lotery dezelfde prijs heeft (ook de niet-winnaars).
Selecteer je daarna dus 5 winnaars, dan hebben deze vanzelfsprekend dezelfde prijs.
Misschien is het een beter idee om eerst 5 winnaars te selecteren. Vervolgens doorloop je de rijen van deze winnaars en kies je een willekeurige prijs uit. Waar je dan nog voor moet zorgen is dat je geen prijs selecteert die je al had geselecteerd (tenzij het toegestaan is dat dezelfde prijs bij toeval wordt uitgekeerd).
Toevoeging op 16/05/2013 12:46:12:
Ik denk dat de code er ongeveer zo komt uit te zien, uitgaande dat het toegestaan is dezelfde prijs bij toeval uit te keren:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
<?php
mysql_query("
UPDATE lotery
SET icon = price_icon, price = price_itemname
FROM (
SELECT icon AS price_icon, itemname AS price_itemname
FROM loteryprijzen
WHERE RAND()<=1/(select count(1) from loteryprijzen) limit 1
)
WHERE RAND()<=5/(select count(1) from lotery) limit 5
");
?>
mysql_query("
UPDATE lotery
SET icon = price_icon, price = price_itemname
FROM (
SELECT icon AS price_icon, itemname AS price_itemname
FROM loteryprijzen
WHERE RAND()<=1/(select count(1) from loteryprijzen) limit 1
)
WHERE RAND()<=5/(select count(1) from lotery) limit 5
");
?>
Gewijzigd op 16/05/2013 12:47:14 door Kevin Driessen
Ik krijg geen errors, dus dat probleem is het niet. Er word niks gewijzigd.
Als er trouwens iets mis gaat in enkel sql dan krijg je dat niet standaard te zien in php. Om wel een melding te krijgen, kun je bij dit soort dingen gebruik maken van 'OR DIE(mysql_error())', dus als volgt:
In een poging mijn wandaden recht te zetten, hier nieuwe code
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$sql = mysql_query("
SELECT * FROM lotery
WHERE RAND()<=5/(select count(1) from lotery)
LIMIT 5
") or die(mysql_error());
while($row = mysql_fetch_assoc($sql)) {
mysql_query("
UPDATE lotery L
CROSS JOIN(
SELECT icon, itemname
FROM loteryprijzen
WHERE RAND()<=1/(select count(1) from loteryprijzen)
LIMIT 1
) AS P
SET L.icon = P.icon, L.price = P.itemname
WHERE name = '".mysql_real_escape_string($row['name'])."'
") or die(mysql_error());
}
?>
$sql = mysql_query("
SELECT * FROM lotery
WHERE RAND()<=5/(select count(1) from lotery)
LIMIT 5
") or die(mysql_error());
while($row = mysql_fetch_assoc($sql)) {
mysql_query("
UPDATE lotery L
CROSS JOIN(
SELECT icon, itemname
FROM loteryprijzen
WHERE RAND()<=1/(select count(1) from loteryprijzen)
LIMIT 1
) AS P
SET L.icon = P.icon, L.price = P.itemname
WHERE name = '".mysql_real_escape_string($row['name'])."'
") or die(mysql_error());
}
?>
Helaas heb ik niet even snel iets kunnen bedenken om dit elegant met één SQL-query af te handelen, maar bovenstaande code zal de klus denk ik wel klaren.
In jouw voorbeeld/beschrijving komt geen primaire of unieke sleutel naar voren als het gaat om de deelnemers in de tabel lotery. Ik ben voor het gemak maar even uitgegaan van een unieke naam. Als je hier iets anders hebt, zoals 'id', dan kun je naam daarmee vervangen natuurlijk.
Kort toegelicht wat bovenstaande code doet:
- Er worden eerst 5 willekeurige winnaars geselecteerd.
- Iedere rij van deze winnaars wordt doorlopen in php.
- Bij het doorlopen wordt de winnaar ge-update, waarbij één willekeurige prijs geselecteerd wordt.
Gewijzigd op 18/05/2013 02:39:12 door Kevin Driessen
Kevin Driessen op 16/05/2013 12:40:40:
Ik kan dit geen betere aanpak noemen, want de kans is aanwezig dat er geen 5 records geselecteerd worden.
Buiten dat vraag ik me af of dit werkelijk verschil maakt in performance, deze manier heeft ook een full table scan tot gevolg.