Vytažení náhodného záznamu z DB
- Panda
- Člen | 569
Toto by mohlo být relativně rychlé (subquery používá index):
$this->db->select('*')->from('table')->where('%n >= RAND() * (SELECT MAX(%n) FROM %n)', 'id', 'id', 'table')->limit(1);
Testoval jsem to na MySQL a zdá se, že to funguje bez problémů. Po drobných změnách by to mělo fungovat i v jiných databázích.
Editoval Panda (29. 8. 2009 19:07)
- weckho
- Člen | 94
Díky moc, dotaz jako takový funguje, bohužel mi vytahuje z DB vždy poslední záznam. Kde by mohla být chyba?
Díky
Panda napsal(a):
Toto by mohlo být relativně rychlé (subquery používá index):
$this->db->select('*')->from('table')->where('%n >= (SELECT MAX(%n) FROM %n)', 'id', 'id', 'table')->limit(1);
Testoval jsem to na MySQL a zdá se, že to funguje bez problémů. Po drobných změnách by to mělo fungovat i v jiných databázích.
- Panda
- Člen | 569
weckho napsal(a):
Díky moc, dotaz jako takový funguje, bohužel mi vytahuje z DB vždy poslední záznam. Kde by mohla být chyba?
Mezi mojí klávesnicí a židlí, zapomněl jsem tam napsat RAND(). :)
Správně to vypadá takto:
$this->db->select('*')->from('table')->where('%n >= RAND() * (SELECT MAX(%n) FROM %n)', 'id', 'id', 'table')->limit(1);
Opravím to i v původním příspěvku, aby to někoho nemátlo.
- Honza Marek
- Člen | 1664
Což takhle:
$this->db->select("*")->from("table")->orderBy("rand()")->limit(1);
Jinak bych si troufnul říct, že na fóru nette je to lehký off-topic.
- pmg
- Člen | 372
To je funkční způsob, který ale MySQL neumí optimalizovat, takže se
RAND()
volá (n * log n)
-krát.
Lze to optimalizovat jako
SELECT *, RAND() AS foo FROM table ORDER BY foo
.
Složitější způsob je uložit si ke každému řádku náhodnou hodnotu a podle ní pak vybírat, tak to dělá Wikipedie. Je to rychlé, ale když se některé hodnoty dostanou moc blízko, některé záznamy to nevytáhne. Leda hodnoty po čase přegenerovat.
- Panda
- Člen | 569
pmg napsal(a):
To je funkční způsob, který ale MySQL neumí optimalizovat, takže se
RAND()
volá(n * log n)
-krát.Lze to optimalizovat jako
SELECT *, RAND() AS foo FROM table ORDER BY foo
.
Dovolil jsem si provést benchmarky a zdá se, že ani tento způsob MySQL moc optimalizovat neumí. Měl jsem tabulku jen o 7 řádcích, ale 1 000 000 průchodů mému dotazu zabralo kolem 3.5s, Tvé řešení trvalo při stejném počtu něco málo přes 20s. Podobně na tom bylo i původní řešení od Honzy Marka.
U tabulky locales_source
z jedné testovací instalace Drupalu
(3441 řádků) byl rozdíl znát ještě mnohem víc. Při 1 000 průchodech
zabrala obě řešení s ORDER BY
kolem 4s, mé řešení kolem
60ms. Problém je v tom, že MySQL musí nejdřív všem řádkům vygenerovat
náhodnou hodnotu a pak to podle ní setřídit. K tomu musí použít
dočasnou tabulku a na třídění nemůže použít indexy.
Testoval jsem na MySQL 5.0.67, Win32 (XP SP3), obě tabulky MyISAM.
Jinak souhlasím s Honzou, na fóru Nette je to trochu off-topic.
- pmg
- Člen | 372
Tvoje řešení je bezpochyby rychlejší, jen je trochu složitější a
hlavně se jeho náhodnost bude snižovat s rozptylováním hodnot
id
.
A díky za ten benchmark, vycházel jsem z předpokladu, který jsem před pár lety někde četl, ale nikdy neotestoval. Možná se to s pátou verzí MySQL změnilo, myslím moje vs Honzovo řešení.