DevProTalk

DevProTalk (http://www.devprotalk.com/index.php)
-   SQL baze podataka - Sponzor: Baze-Podataka.net (http://www.devprotalk.com/forumdisplay.php?f=10)
-   -   random select na velikoj tabeli (http://www.devprotalk.com/showthread.php?t=5061)

kodi 08. 04. 2008. 14:05

random select na velikoj tabeli
 
problem:
imam tabelu (~2M unosa), primarni kljuch je polje id, medjutim ono nije uniformno rasporedjeno, tj postoje veliki blokovi obrisanih unosa, naprimer id moze da ide:
1
2
3
434
454
455
456
900
901
.
.
.
i tako, e sad meni treba query koji ce na najbrzi nacin da mi vrati recimo 2000 random row-ova iz tablele (tih 2000 moraju biti unikatni)

any idea? (mySQL)

P.S.
znam za
ORDER BY RAND()
LIMIT XYZ
ali je to jako sporo, i ima tendenciju da ubije cpu :)

ivanhoe 08. 04. 2008. 14:23

mozes da smanjis broj recorda koji se sortiraju tako sto ces da stavis:
"WHERE id BETWEEN ". $rand ." AND ". ($rand+10000) ."
ORDER BY RAND()
LIMIT XYZ"

ako 10000 nije dovoljno stavis vecu granicu

kodi 08. 04. 2008. 14:35

hmm...

to bi definitivno smanjilo vreme, i olaksalo stvari, ali bi onda favorizovalo unose iz tih 10k, sto na 2M unosa nije naivno..
a ako povecam na recimo 100k opet ce trajati mnogo taj query

evo sad googlam, i izgleda da uopste nema lakog resenja za ovaj problem.. :(

jel ima neko neku ideju, ili sam osudjen na dodatno polje sa sekvencom koja ce se updejtovati na svaki insert i delete :1027:

zira 08. 04. 2008. 14:56

http://akinas.com/pages/en/blog/mysql_random_row/
Valjda Solution 3 na toj strani pokriva i tvoj slucaj...

ivanhoe 08. 04. 2008. 18:55

@zira: ovaj primer dohvat samo jedan rekord

@kodi: mozes da umesto jednog opsega od 10000 rekorda dohvatis par manjih (5-6 recimo, ili koliko mislis da ti treba), pa da ih unijom spojis, pa onda na celom rezultatu uradis ORDER BY rand()

u sustini, ovde ti je pitanje jel ti treba stroga uniformna raspodela verovatnoce, ili ti treba brzina, treba naci neki najbolji odnos..

kodi 08. 04. 2008. 19:41

Citat:

u sustini, ovde ti je pitanje jel ti treba stroga uniformna raspodela verovatnoce, ili ti treba brzina, treba naci neki najbolji odnos..
:/

trabaju mi oba :)

odustao sam od uniformne raspodele, i posto ti blokovi obrisanih podataka nisu toliko veliki uradio sam varijantu da prvo u phpu generishem niz sa offsetima a onda fetchujem sa where id >=$offset, radi prilichno brzo....


Off Topic:
btw, radi se o gomili keyworda, cije rezultate sa googla treba da updatujem i da sve bude mladje od 10 dana, u medjuvremenu je dataset bash narastao, tako da smo preshli na model gde se na svakihsat vremena updejtuje deo sadrzaja, a onda na svakih 10 dana pokrene skripta koja pokupi sve sto je po timestamp-u starije od 10dana, obichno ostane manje od procenat onoga sto se ovakvom metodom promashi, a generalno podaci budu mnogo sveziji, samo treba nashtelovati koliku porciju da uzima pri svakom prolazu i koliko chesto to da ide

eto mozda nekome bude korisno...

conica 08. 04. 2008. 19:56

evo dodjosmo do ovoga
PHP kôd:

SELECT FLOOR(+ (RAND() * (SELECT COUNT(*) from table_name))) from table_name limit 4

primer je testiran na tabeli sa oko 1.200.000 recorda sa limitom 4

kodi 08. 04. 2008. 20:06

nisam siguran da razumem najbolje ovo, ovo mi vrati samo xyz id-jeva.. opet bih morao da radim fetch za svaki od njih, a i cini mi se da je moguce da mi vrati id koji je recimo izbrisan (?) ili greshim ?

ivanhoe 08. 04. 2008. 22:13

A mozda moze da se naprosto izgenerise jedno 4000 random brojeva (lepa okrugla cifra), i onda uradis:

SELECT * FROM tabela
WHERE id IN ($gomila_rnd_brojeva)
ORDER BY RAND()
LIMIT 2000;

Ako ti se ne vrati dovoljno rezultata uradis naprosto ponovo upit sa korigovanim limitom, i tako sve dok ne skupis dovoljno...

Dejan Topalovic 08. 04. 2008. 23:29

Nadam se da ovaj kôd govori sve sam od sebe i da ne moram komentarisati nista :D
Kôd:

use test;
create table keywords (
  id int unsigned not null auto_increment,
  keyword varchar(32) not null,
  url varchar(255),
  primary key(id)
);
DELIMITER $$
DROP PROCEDURE IF EXISTS test.unesi_podatke $$
CREATE PROCEDURE test.unesi_podatke(IN koliko INT)
BEGIN
DECLARE brojac INT DEFAULT 1;
while brojac <= koliko do
INSERT INTO keywords (id, keyword, url)
VALUES(NULL, CONCAT('keyword_', brojac), CONCAT('http://www.url', brojac, '.com'));
set brojac = brojac + 1;
end while;
END $$
DELIMITER ;

mysql> create table keywords (
    ->  id int unsigned not null auto_increment,
    ->  keyword varchar(32) not null,
    ->  url varchar(255),
    ->  primary key(id)
    -> );
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS test.unesi_podatke $$
Query OK, 0 rows affected (0.03 sec)
mysql> CREATE PROCEDURE test.unesi_podatke(IN koliko INT)
    -> BEGIN
    -> DECLARE brojac INT DEFAULT 1;
    -> while brojac <= koliko do
    -> INSERT INTO keywords (id, keyword, url)
    -> VALUES(NULL, CONCAT('keyword_', brojac), CONCAT('http://www.url', brojac,
 '.com'));
    -> set brojac = brojac + 1;
    -> end while;
    -> END $$
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> call test.unesi_podatke(2000000);
Query OK, 1 row affected (1 min 31.95 sec)
mysql> select count(*) from keywords;
+----------+
| count(*) |
+----------+
|  2000000 |
+----------+
1 row in set (0.00 sec)
mysql> create index indeks_id on keywords(id);
Query OK, 2000000 rows affected (15.28 sec)
Records: 2000000  Duplicates: 0  Warnings: 0
mysql> DELETE FROM keywords WHERE mod(id, 7) = 0;
Query OK, 285714 rows affected (6.06 sec)
mysql>
mysql> select count(*) from keywords;
+----------+
| count(*) |
+----------+
|  1714286 |
+----------+
1 row in set (0.00 sec)
 
mysql> select * from keywords
    -> order by rand()
    -> limit 2000;
2000 rows in set (1 min 43.15 sec)

mysql> SELECT *
    -> FROM keywords k
    -> JOIN (SELECT FLOOR( MAX(ID) * RAND()) AS Rand_ID FROM keywords) AS x
    -> ON k.id >= x.Rand_ID
    -> limit 2000;
2000 rows in set (0.63 sec)

Posto je RAND() prilicno spor, mozes upit iz PHP-a proslijediti i drugacije:
Kôd:

$sql = "SELECT * FROM keywords k JOIN (SELECT FLOOR( MAX(ID) * ".lcg_value().") AS Rand_ID FROM keywords) AS x ON k.id >= x.Rand_ID limit 2000";
Ukoliko takav upit proslijedis MySQL-u, mozes dobiti npr. ovakav rezultat:
Kôd:

mysql> SELECT *
    ->  FROM keywords k
    ->  JOIN (SELECT FLOOR( MAX(ID) * 0.234) AS Rand_ID FROM keywords) AS x
    ->  ON k.id >= x.Rand_ID
    ->  limit 2000;
2000 rows in set (0.02 sec)

Nema na cemu.


Vreme je GMT +2. Trenutno vreme je 12:37.

Powered by vBulletin® Verzija 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Copyright © DevProTalk. All Rights Reserved.

Mišljenja, saveti, izjave, ponude ili druge informacije ili sadržaji nastali na Sajtu su vlasništvo onoga ko ih je kreirao, a ne DevProTalk.com, tako da ne morate da se oslanjate na njih.
Autori poruka su jedini odgovorni za ovakve sadržaje. DevProTalk.com ne garantuje tačnost, kompletnost ili upotrebnu vrednost informacija, stavova, saveta ili datih izjava. Ne postoje uslovi pod kojima bi mi bili odgovorni za štetu ili gubitak koji je posledica bilo čijeg oslanjanja na nepouzdane informacije, ili bilo kakve informacije nastale kroz komunikaciju između registrovanih članova.
Web sajt može sadržavati linkove na druge web sajtove na Internetu ili neke druge sadržaje. Ne kontrolišemo niti podržavamo te druge web sajtove, niti smo pregledali bilo kakve sadržaje na takvim sajtovima. Mi nećemo biti odgovorni za legalnost, tačnost ili prikladnost bilo kog sadržaja, oglasa, proizvoda, usluga ili informacije lociranim na ili distribuiranih kroz druge web sajtove, niti za bilo kakvu štetu nastalu kao posledica takvih informacija. DevProTalk.com drži i čuva druga prava vlasništva na web sajtu. Web sajt sadrže materijale zaštićene copyright-om, zaštitne znakove i druge informacije o pravu vlasništva ili softver. Članovi mogu poslatu informacije zaštićene pravima vlasništva njihovih nosilaca i ona ostaju zaštićena bez obzira da li su oni koji prenose te informacije to naveli ili ne. Osim informacija koje su u javnom vlasništvu ili za koje dobijete dozvolu, nemate pravo da kopirate, modifikujete ili na bilo koji način menjate, objavljujete, prenosite, distribuirate, izvršavate, prikazujete ili prodajte bilo koju informaciju zaštićenu pravima vlasništva. Slanjem informacija ili sadržaja na bilo koji deo DevProTalk.com, Vi automatski dozvoljavate i predstavljate garanciju da imate pravo da dozvolite DevProTalk.com ili članovima DevProTalk.com bespovratnu, kontinualnu, neograničenu, globalnu dozvolu da koriste, kopiraju, izvršavaju, prikazuju i distribuiraju takve informacije i sadržaje i da iz takvih sadžaja koriste bilo koji deo u bilo koje svrhe, kao i pravo i dozvolu da koriste gore navedene sadržaje. Svi zaštitni znakovi (trademarks), logotipi, oznake usluga, firme ili imena proizvoda koji se pominju na ovom web sajtu su vlasništvo kojim raspolažu njihovi vlasnici.