Avaleht
uus teema   vasta Tarkvara »  Programmeerimine »  MySQL kiire random päring rohkem kui 1 rea kuvamiseks korraga märgi kõik teemad loetuks
märgi mitteloetuks
vaata eelmist teemat :: vaata järgmist teemat
Hinnavaatlus :: Foorum :: Uudised :: Ärifoorumid :: HV F1 ennustusvõistlus :: Pangalink :: Telekavad :: HV toote otsing
autor
sõnum Saada viide sõbrale.  :: Teata moderaatorile teata moderaatorile
otsing:  
poroloon
HV kasutaja
poroloon

liitunud: 25.07.2010




sõnum 25.03.2011 14:15:14 MySQL kiire random päring rohkem kui 1 rea kuvamiseks korraga vasta tsitaadiga

Ma leidsin netis õpetuse, kuidas asendada RAND() vastava php funktsiooniga, et suure tabeli puhul ei läheks aeglaseks ega koormavaks.
Ma üritasin koodi oma vajaduste jaoks ümber teha. Kuid, siin on üks probleem:
See funktsioon tagastab vaid ühe (juhusliku) rea kuid mul on vaja tagastada rohkem juhuridu. Kood on praegu sihuke:

function random_row($table, $column, $where_and = "", $limit = 1)
{
   $max_sql = "SELECT max($column) FROM $table";

   $max_row = mysql_fetch_row(mysql_query($max_sql));

   $random_number = mt_rand(1, $max_row[0]);

   $random_sql = "SELECT * FROM $table WHERE $column >= $random_number $where_and ORDER BY $column ASC LIMIT $limit";

   $random_row = mysql_fetch_row(mysql_query($random_sql));

   if(!is_array($random_row))
   {
      $random_sql = "SELECT * FROM $table WHERE $column < $random_number $where_and ORDER BY $column DESC LIMIT $limit";

      #$random_row = mysql_fetch_row(mysql_query($random_sql));
   }

   return $random_sql;
}

// Päringu kutsun esile sellisena:

$query = mysql_query(random_row("pildid", "id", "AND pub = '1'", 10);

Kahjuks see ei kuva alati 10 juhuslikku pilti. icon_sad.gif
Mõnikord kuvab 2, mõnikord 8, mõnikord mitte ühtegi!
Kui limit on 1, siis kuvab alati 1 juhusliku pildi ja siis ei juhtu nii, et vahel ei kuva mitte midagi. Mida saaks teha, et alati tuleks 10 rida? Muidugi juhul kui tabelis on 10+ rida. Kui tabelis on vaid 8 rida, sel puhul peaks alati kuvama 8 juhurida.
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
neros
HV Guru
neros

liitunud: 26.11.2003




sõnum 25.03.2011 14:34:31 vasta tsitaadiga

$random_number = mt_rand(1, $max_row[0]);
Mõni ime siis, et üheainsa kuvab icon_smile.gif


function random_row($table, $column, $limit) {
    return "SELECT {$column} FROM {$table} WHERE id IN ( SELECT id FROM {$table} ORDER BY RAND() LIMIT {$limit})";
}


Milleks teha asju raskemaks kui tarvis on? KIS!

Vaata, et tablelis oleks mingi primary key (näites 'id') millel oleks indeks peal, ning siis ei ole isegi 10000 reaga tabelitel üle millisekundite execution time'i.
Kommentaarid: 48 loe/lisa Kasutajad arvavad:  :: 0 :: 1 :: 40
tagasi üles
vaata kasutaja infot saada privaatsõnum
poroloon
HV kasutaja
poroloon

liitunud: 25.07.2010




sõnum 25.03.2011 15:09:06 vasta tsitaadiga

uhmm, kas just see RAND() mitte ei tee päringut väga aeglaseks? 10k reaga tabeli puhul RAND() koormab serverit päris koledasti ja kuvab tulemust aeglaselt (vähemalt 1-2 sek)

"id" on primary.

EDIT:
ülaltoodud lühike versioon ei toimi.
mysql_error() -- "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
Ho Ho
HV Guru
Ho Ho

liitunud: 16.02.2002




sõnum 25.03.2011 17:14:42 vasta tsitaadiga

poroloon kirjutas:
10k reaga tabeli puhul RAND() koormab serverit päris koledasti ja kuvab tulemust aeglaselt (vähemalt 1-2 sek)
Tee indeksid korda.

Kui indekseid parandada ei viitsi siis võid ju lihtsalt selle alamquery asemele PHP's genereerida kümne juhuslikku ID'd.
Vahet pole kas kutsud rand()'i PHP's või SQL'is, kümne juhuslik arvu jaoks on sul endiselt tarvis seda 10x välja kutsuda.

[edit]
Su praegust koodi lugedes teed sa kõike muud kui küsid baasilt 10 juhuslikku pilti. Hetkel paistab ta lihtsalt otsivat ühe juhusliku indeksi ning võtma sellele järgnevad N rida. Kui too juhuslik indeks langeb tabeli viimase N rea hulka siis loomulikult tagastatkse vähem tulemusi.

_________________
Teach a man to reason and he'll think for a lifetime
Common sense - so rare that it's a damn superpower
Vaadates paljude inimeste sõnavõtte siin ja mujal jääb üle ainult klassikuid tsiteerida - "I weep for humanity"
Kommentaarid: 106 loe/lisa Kasutajad arvavad:  :: 0 :: 1 :: 86
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
poroloon
HV kasutaja
poroloon

liitunud: 25.07.2010




sõnum 25.03.2011 19:13:00 vasta tsitaadiga

Ho Ho kirjutas:
poroloon kirjutas:
10k reaga tabeli puhul RAND() koormab serverit päris koledasti ja kuvab tulemust aeglaselt (vähemalt 1-2 sek)
Tee indeksid korda.

Sama asja räägitakse mitmel pool netis, et rand() pole hea kui tegemist suure tabeliga. "Using order by rand() is bad for big tables."
Tabelist on vaja saada 10x random "id" ja see "id" on nagunii primary (index). Sellest ei piisa?

Siin räägitakse ka rand() aeglusest ja pakutakse mingi lahendus, mis siiski mul ei toimi. Harilik rand() päring toimib, aga mis siis juhtub, kui tabel kasvab suuuureks...?
http://www.rndblog.com/how-to-select-random-rows-in-mysql/
"This can work fine for small tables. However, for big table, it will have a serious performance problem as in order to generate the list of random rows, MySQL need to assign random number to each row and then sort them."
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
Ho Ho
HV Guru
Ho Ho

liitunud: 16.02.2002




sõnum 25.03.2011 19:40:02 vasta tsitaadiga

Nujah, lootsin et andmebaasi mootorid ikka vähe targemad on, tundub et pean pettuma icon_smile.gif


Ma siis lihtsalt teeks nii nagu eespool kirjeldasin, et PHPs genereerin nood 10 random ID'd ning topin need sinna WHERE järgi

_________________
Teach a man to reason and he'll think for a lifetime
Common sense - so rare that it's a damn superpower
Vaadates paljude inimeste sõnavõtte siin ja mujal jääb üle ainult klassikuid tsiteerida - "I weep for humanity"
Kommentaarid: 106 loe/lisa Kasutajad arvavad:  :: 0 :: 1 :: 86
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
Le Inc
HV Guru
Le Inc

liitunud: 06.09.2002



Autoriseeritud ID-kaardiga

sõnum 25.03.2011 22:19:19 vasta tsitaadiga

Kuidas oleks kiirusega siis kui:
a) select count(*) tabel. Kontrollid palju sul ridu tabelis on.
b) genereeri php rand(1,tabel(max(id)), lased for tsükliga nii palju kui sul suvalisi nummereid vaja on
c) pärid saadu array'iga select * from tabel where pilt in (array)

Natuke ringiga värk ... samas "in" funktsioon ei pruugi alati kõige kiirem olla.
Kommentaarid: 56 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 54
tagasi üles
vaata kasutaja infot saada privaatsõnum
andrusny
Kreisi kasutaja
andrusny

liitunud: 20.03.2006




sõnum 25.03.2011 22:33:18 vasta tsitaadiga

Sellisel juhul peab sul olema kindel rea nr, kui oled vahepael pilte kustutanud, siis ei pruugi ID järjest olla.
_________________
Kommentaarid: 7 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 7
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
Ho Ho
HV Guru
Ho Ho

liitunud: 16.02.2002




sõnum 25.03.2011 22:37:58 vasta tsitaadiga

Teoorias peaks saama ka füüsilise andmebaasi rea järgi pärinuid teha. SQLites vähemalt saab icon_smile.gif
_________________
Teach a man to reason and he'll think for a lifetime
Common sense - so rare that it's a damn superpower
Vaadates paljude inimeste sõnavõtte siin ja mujal jääb üle ainult klassikuid tsiteerida - "I weep for humanity"
Kommentaarid: 106 loe/lisa Kasutajad arvavad:  :: 0 :: 1 :: 86
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
andrusny
Kreisi kasutaja
andrusny

liitunud: 20.03.2006




sõnum 25.03.2011 22:49:27 vasta tsitaadiga

Jah saab küll
mysql_data_seek

_________________
Kommentaarid: 7 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 7
tagasi üles
vaata kasutaja infot saada privaatsõnum mine selle kasutaja kodulehele
kiiver
HV vaatleja

liitunud: 03.04.2003




sõnum 26.03.2011 01:58:02 vasta tsitaadiga

Niikaua kui tabelis pole ühtegi unikaalset ja järjestikust (näiteks id garanteeritult 1, 2, 3... ilma vahepealsete aukudeta) veergu ei saa minuteada teha täielikku random valikut. Füüsilise rea järgi ka minuteada sama teema, et rea füüsiline aadress või id ei ole garanteeritult järjestikune. (Ma pole küll mysql-ga sinapeal aga kas mysql_data_seek mitte ei käi ka läbi kõiki ridu, ehk kiiruse võitu ei tekita?)

Jääb kaks varianti:
1. Tekitada eelnimetatud reeglitele vastav veerg ja hoida seda pidevalt sünkroonis - eeliseks on hea kiirus [O(1)] aga ebamugav haldamine ja küsitav töökindlus
Siit pealt saab teha oma päringu ühekaupa või korraga niimitmest random reast kui vaja, kiirus sellest ei kannata. Ridade arv ei oma tähtsust.

2. Teha jooksvalt kogu arvutus - eeliseks mugavus ja töökindlus aga suurte tabelite korral tunduvalt aeglasem [O( n)]
Sel puhul tuleks olenevalt baasist/versioonist jms. valida kiireim vahend n.n. id tekitamiseks. Ise Oracles olen kasutanud kahte varianti - random funktsioon või rea number.
Random funktsiooni igale reale rakendades tuli päring 13x aeglasem kui rea numbri järgi valik. (tabelis 5 mlj. rida, 40 vs 3 sekundit).

Kui tabelis on kuni mõni miljon rida ja päringut tehakse mõni kord minutis või harvem siis paistab, et antud baasis on mõistlik lahendus rea numbrite järgi selekteerimine.

Näide on oracles aga sama loogika peaks töötama ka mysql-s. Päringu keerukus on tabeli ridade arv (5 mlj.) korda kiire funktsioon "rownum", tulemuste ridade arv (5) korda aeglane random funktsioon ja üks kord count(*).
Hoiatuseks, et antud päring ei garanteeri ridade unikaalsust.

sql:
  1. SELECT * FROM
  2.   (SELECT rownum-1 AS RN, a.* FROM tabel a) tab_data -- kõik read, rownum-1 tagastab rea numbri 0, 1, 2...
  3.      
  4.   -- select tagastab 5 rida random väärtustega TABEL ridade arvu seast
  5. , (SELECT RAND FROM
  6.     (SELECT ceil(dbms_random.value(0, (SELECT count(*) FROM tabel))) AS RAND FROM dual)
  7.   , (SELECT level FROM dual connect BY level <= 5) -- suvaline päring 5 rea tekitamiseks
  8.   ) valik
  9.      
  10. WHERE tab_data.RN = valik.RAND


Php-st välja kutsudes vist mõistlik teha sarnaselt nagu Le Inc mainis, et esiteks count(*), teiseks tekitada php-s random numbrid selles vahemikus ja kolmandaks lihtne sql päring rea numbri järgi:

sql:
  1. SELECT * FROM (
  2.   SELECT rownum AS RN, a.*
  3.   FROM tabel a
  4. )
  5. WHERE RN IN (php_rand_array)
Kommentaarid: 3 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 3
tagasi üles
vaata kasutaja infot saada privaatsõnum
Le Inc
HV Guru
Le Inc

liitunud: 06.09.2002



Autoriseeritud ID-kaardiga

sõnum 26.03.2011 14:51:09 vasta tsitaadiga

Oracle rownum peaks saama emuleerida php's (aaffggeddjjj formaat) !? Seal on kohe miskine süsteem mismoodi neid antakse.
Kommentaarid: 56 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 54
tagasi üles
vaata kasutaja infot saada privaatsõnum
näita postitusi alates eelmisest:   
uus teema   vasta Tarkvara »  Programmeerimine »  MySQL kiire random päring rohkem kui 1 rea kuvamiseks korraga
[vaata eelmist teemat] [vaata järgmist teemat]
 lisa lemmikuks
näita foorumit:  
 ignoreeri teemat 
sa ei või postitada uusi teemasid siia foorumisse
sa ei või vastata selle foorumi teemadele
sa ei või muuta oma postitusi selles foorumis
sa ei või kustutada oma postitusi selles foorumis
sa ei või vastata küsitlustele selles foorumis
sa ei saa lisada manuseid selles foorumis
sa võid manuseid alla laadida selles foorumis



Hinnavaatlus ei vastuta foorumis tehtud postituste eest.