ugrás a tartalomhoz

RAND() használata lekérdezésekben

enakos · 2009. Jan. 21. (Sze), 16.30
Sziasztok!

A célom, az hogy frissítésenként másik sort jelenítsen meg a programom. Ez
sikerült is, de azt nem értem, hogy néha miért nem jelenik meg semmi?
Ezt hogyan tudnám kikerülni, hogy ne legyen üres megjelenés, hanem mindig
jelenítsen meg egy sort?
$sql = "SELECT * FROM tablam WHERE id ORDER BY rand() LIMIT 1";
Ezzel kapcsolatban még abban is szeretnék segítséget vagy tanácsot, hogy
az hogyan oldható meg, hogy egy véletlen kiválasztott sor, ne a
következő oldalfrissítésig legyen látható, hanem egy napig vagy egy
hétig is és egy hét után frissítse magát újra?
Ezt már úgy, hogy a honlap összes látogatójának ugyan az jelenjen meg
az adott időben.
 
1

Kerülendő

vbence · 2009. Jan. 21. (Sze), 17.19
Az ORDER BY RAND()-ot nem anjánlják, mivel ilyenkor az egész táblát értinti a lekérdezés. - Minden egyes rekordhoz legenerál egy véletlen számot, majd rendezi őket eszerint stb stb.

A javbasolt megoldás alapja valami ilyesmi:
SELECT MAX(id) FROM tabla
aztán phpben generálesz egy véletlen számot, majd kikéred a rekordot azzal az id-vel. Itt célszerű while ciklust használni, mert lehet, hogy a kisorsolt ID-jű rekord nem létezik (törlődött).

A véletlen korlátozását úgy oldhatod meg, hogy a PHP srand függvényével a random seedet az időből generálod pl ha 5 percig álladó rekordot szeretnél:
srand (floor(time () / (5 * 60)));
majd eyután generálod a véletlen számot, ami minden oldallekérdezésnél ugyanaz lesz amég a renszeridő nem lép 5 * 60 máodperccel odébb.
4

Gyakoriság

zila · 2009. Jan. 21. (Sze), 19.02
Azért, szerintem, hetente egyszer beleférhet az order by rand...

A kérdezőnek:
A query-ben elég rosszul néz ki az a "where id order by rand"
10

nem tábla, hanem recordset

Hodicska Gergely · 2009. Jan. 21. (Sze), 23.17
Az ORDER BY RAND()-ot nem anjánlják, mivel ilyenkor az egész táblát értinti a lekérdezés.
Nem a teljes tábla érintett, hanem a recordset, amit a query meghatároz. Amit olvastam ezzel kapcsolatban, hogy kb. olyan 1000 rekord után kezd belassulni.

Ajánlott olvasmány Jan Kneschke írása:
http://jan.kneschke.de/projects/mysql/order-by-rand/.
11

Szép összefoglalás

vbence · 2009. Jan. 22. (Cs), 01.14
... de az 1000 rekord alatti alacsony költség talán betudható a kisméretű rekordoknak is. Az ösztönöm azt súgja, hogy egy valódi példánál lényegesen lejjebb fog csúszni ez a határ.
2

Nem létező ID-t kiiktatni

enakos · 2009. Jan. 21. (Sze), 18.48
Köszönöm a segítséged!

Minden jó, csak még azt nem sikerült megoldanom, hogy nem létező id esetén ne a nagy semmi jelenjen meg, hanem keressen egy új id-t.

Ez most a kódom:

<?
srand (floor(time () / (5 * 60))); 
    $kod = rand(1020,1031);


$sql = "SELECT * FROM temak WHERE id=$kod";

            $eredmeny = mysql_query($sql);
            while ($sor=mysql_fetch_array($eredmeny)) {
              $id = $sor["id"];
              $tcim = $sor["tcim"];
	$tema = $sor["tema"];
              ?>
3

sorsolás

vbence · 2009. Jan. 21. (Sze), 18.57
Természetesen új random számot kell generálnod, majd újra futtatni a query-t is a while ciklusban. Annak smemi értelme, ha az eredmény nélküli querydből várod, hogy valami csoda folytán új rekordok jöjjenek ki :)
5

Világos

enakos · 2009. Jan. 21. (Sze), 19.33
Ezt én is tudtam, csak a megoldani nem sikerül... :S
6

Kábé

vbence · 2009. Jan. 21. (Sze), 19.56
Kábé így:
  
srand (floor(time () / (5 * 60)));

do {
    $kod = rand(1020,1031);  
    $sql = "SELECT * FROM temak WHERE id=$kod";  
  
    $eredmeny = mysql_query($sql);  
    $sor=mysql_fetch_array($eredmeny);
} while (!$sor);

$id = $sor["id"];
$tcim = $sor["tcim"];  
$tema = $sor["tema"];
De a ciklusba nem ártana egy számlálót is beiktatni, ha mondjuk valamiért törlődött a tábla, vagy elromlik a random generátor, ne legyen végtelen ciklus belőle...
7

hatékonyabb?

Szekeres Gergő · 2009. Jan. 21. (Sze), 20.41
azért ezt le kéne mérni, mert elég abszudnak tartom, hogy az általad javasolt megoldás hatékonyabb, mint az order by rand(). egyrészt az csak az adatbázisszervert használja, míg a te megoldásod mindkettőt, igaz a dbt kevésbé.
8

valóban igazad volt

Szekeres Gergő · 2009. Jan. 21. (Sze), 20.44
találtam egy postot:
itt
9

A cikk megoldásához

vbence · 2009. Jan. 21. (Sze), 21.26
Én valahol a mysql fórumaiban találkoztam a problémával. Az idézett cikkhez annyit kommentálnék, hogy az ott említett második megoldás (79%) lényegesen különbözik az általam javasoltnál, hiszen ott a >= miatt szintén léterjön egy eredményhalmaz (átlaghosan a sorok felével), amit szintén rendezni kell.

A logikai buktató pedig az, hogy egy nagy "lyukat" követő rekord sokszorta gyakrabban fog megjelenni. Pl a következő id-knél: {1, 2, 3, 4, 5, 10} a 10 lesz a megoldás, ha 6, 7, 8, 9 vagy 10 a random szám, amivel ötször akkora a valószínűsége, mint a többi rekordnak. Ezt azért meg kellett volna említeni a cikk szerzőjének. (Rosszul veheti ki magát például ha az egyik előfizetőt látszólag favorizáljuk a többiek kárára).
13

valószínűség

gex · 2009. Jan. 22. (Cs), 10.50
tegnap este olvastam a szálat és nekem is ez jutott eszembe, hogy nem azonos valószínűséggel fognak kikerülni az adatok. az a megoldás segítene rajta, ha először egy count(*)-ot kérnénk le, abból generálnánk véletlenszámot (0 és közte), és utána a lekérdezésben nem id alapján szűrnénk, hanem limit véletlenszám, 1-et írnánk a végére. ezzel egyrészt elkerülhető hogy lukat akarunk lekérni, azaz nem lesz szükség while ciklusra, másrészt a rekordokat ugyanolyan valószínűséggel tudjuk kiválasztani.

van amire nem gondoltam?
15

Nem kell rendezni

Rici · 2009. Jan. 22. (Cs), 12.37
Egy normális SQL szerver (és tapasztalataim szerint a MySQL az ebből a szempontból normálisnak tekinthető) nem fog rendezni, ugyanis alapból primary key szerint rendezetten vannak tárolva a sorok. Tehát egyszerűen megkeresi a legelső, a kérdéses id-nál nagyobb id-jú sort (ez egy index lookup), és aztán innentől kezdve csak végigmegy a sorokon. Sőt, mivel ott van a LIMIT 1, csak ezt a legelsőt adja vissza.

Az már más kérdés, hogy ilyen módszerrel valóban nem lehet egyenletes eloszlással véletlenszerűen kiválasztani egy sort, az említett lyukak miatt.
22

táblatípus függő

Hodicska Gergely · 2009. Jan. 22. (Cs), 22.26
alapból primary key szerint rendezetten vannak tárolva a sorok
Ez pl. MySQL esetén táblatípus függő. MyIsam esetén ha törölsz egy sort, akkor a helyére simán kerülhet egy másik sor. InnoDB esetén a primary key az clustered index, e szerint vannak fizikailag a sorok. De emlékeim szerint MSSQL esetén is külön kellett egy indexnek megmondani, hogy ő clustered index legyen, és ebből csak egy lehetett.
12

felesleges while

SamY · 2009. Jan. 22. (Cs), 05.15
Mivel nagy valószínűséggel az id -d primary key ezért alapból aszerint van egy sorba rendezett indexed (ha nincs akkor állítsd be) ezért a while -t el is hagyhatod, így:

 srand (floor(time () / (5 * 60)));  
 
 $kod = rand(1020,1031);    
 $sql = "SELECT * FROM temak WHERE id >= $kod ORDER BY id LIMIT 1";    
     
 $eredmeny = mysql_query($sql);    
 $sor=mysql_fetch_array($eredmeny);  
   
 $id = $sor["id"];  
 $tcim = $sor["tcim"];    
 $tema = $sor["tema"]; 
14

Szóródás

vbence · 2009. Jan. 22. (Cs), 12.19
Ajánlom figyelmedbe a kilences hozzászólást.
16

Tiszta vizet

vbence · 2009. Jan. 22. (Cs), 14.53
Mivel sokan sok okos dolgot mondtatok, kipróbáltam a dolgot. So without further ado:

rekordok számaid >=whilelimitorder by rand()
1001351501441080
1000134151141
100006435877841


Here's some ado: 10%-kal több rekordot hoztam létre a célzott számnál, majd 10%ukat töröltem egy
delete from random where rand() * 10 < 1; kverivel, úgyhogy a reodok száma nem fix, a lyukak száma pedig 10% körüli.

Amit én leszűrtem:
- Ezer rekord körül sincs lényeges eltérés a futási időben (memóriafoglalást nem viszgáltam).
- Ezen a szinten WHILE-os módszer a lyukak arányában több időt használ (hoiszen ennyiszer kell új kverit futtatni).
- A LIMIT-es lekérdezés picit több időbe kerül, de nem kell ismételni, mint a while-t és igazságosan sorsol.
17

köszi

gex · 2009. Jan. 22. (Cs), 16.32
köszi a mérést!

szerk: a limit és a 10000 kereszteződésénél az tényleg ilyen sok lett?
19

igen

vbence · 2009. Jan. 22. (Cs), 18.08
Ott nagyon megugrott. talán csinálok egy 5000-es mérést is majd...
18

Több különböző sort egyszerre

enakos · 2009. Jan. 22. (Cs), 17.13
Köszönöm az eddigi segítségeket.

Azt nem sikerült megoldanom, hogy egyszerre 2, 3 vagy több sort válasszon ki véletlen.
Ezzel a véletlen szám generálóval ezt meglehet oldani?
Csak a Limit paranccsal sikerült, de akkor is sokszor volt, hogy kétszer is kiírt egy sort.
Szóval, ha több sort szeretnék véletlen kiválasztani a táblámból és nem szeretném, hogy ugyan azt a sort kétszer is kiírja, akkor mi a teendő?
20

Mondjuk a következő csak

Poetro · 2009. Jan. 22. (Cs), 18.18
Mondjuk a következő csak MySQL-ben műküdik, de imhol:
SELECT * FROM tablam ORDER BY RAND() LIMIT 3;
21

mehet máshol is

Hodicska Gergely · 2009. Jan. 22. (Cs), 22.08
PostgreSQL-ben is menne ez szerintem, csak rand helyett random.