Véletlen sor változó feltételek alapján
Sziasztok!
Az
Tegyük fel, hogy van egy weboldal, ahol minden cikk végén szeretnénk az adott szerzőtől 5 véletlenszerű további cikket ajánlani. Sok szerző van, sok cikk, és sok törölt is.
– MAX(article_id) alapján hiába generálunk találomra lehetséges ID-kat akár nagyobb számban, mert jó eséllyel vagy nem az adott szerzőtől származik, vagy nem létezik.
– Ha "cikk ID >= random ID" feltételt adunk meg, hogy biztosan létezzen a cikk, akkor több egymás után törölt cikket követő ID túl gyakran fog szerepelni, ezt sem szeretnénk.
– Az összes lehetséges sor lekérése (a programban történő utólagos feldolgozásra) nagyon nagy számú eredményt is hozhat.
Ti hogyan közelítenétek meg a problémát?
Előre is köszönettel,
pkadam
■ Az
ORDER BY RAND()
kiváltásának elég bő az irodalma, azonban ezek döntő többsége nem foglalkozik egyéb kiválasztási kritériumokkal.Tegyük fel, hogy van egy weboldal, ahol minden cikk végén szeretnénk az adott szerzőtől 5 véletlenszerű további cikket ajánlani. Sok szerző van, sok cikk, és sok törölt is.
– MAX(article_id) alapján hiába generálunk találomra lehetséges ID-kat akár nagyobb számban, mert jó eséllyel vagy nem az adott szerzőtől származik, vagy nem létezik.
– Ha "cikk ID >= random ID" feltételt adunk meg, hogy biztosan létezzen a cikk, akkor több egymás után törölt cikket követő ID túl gyakran fog szerepelni, ezt sem szeretnénk.
– Az összes lehetséges sor lekérése (a programban történő utólagos feldolgozásra) nagyon nagy számú eredményt is hozhat.
Ti hogyan közelítenétek meg a problémát?
Előre is köszönettel,
pkadam
SELECT * FROM cikkek WHERE
ORDER BY RAND() nélkül
ORDER BY RAND()
-ot kellene kiváltani, mivel nagyobb táblák esetén (>10 ezer sor) már nagyon lassú.Akkor le lehetne generálni
Egyszerűbb tábla?
Ehhez elég egy jó index.
Nem hiszem, hogy sok olyan
Csak példa
Szeretnék egy tömbből
Kérd le feltételeknek megfelelő sorok számát, válassz ki rajta 5 random indexet, és kérd le azokat (egyesével, vagy seekelve)
Nem lehet, mert nem folytonos
Nem is kell, az
Ezzel gyakorlatilag azt
Nem kell lekérdezni mindet,
where author_id=x and author_seq in (3,6,8,16,42)
, vagy ilyesmi. Erre tud indexet is tenni. Nyilván karban kell tartani, de az azért nem akkora mágia.Karbantartás
Több tízezres rekordnál,
Ez már miért lenne így?
A felvetés egyébként a lekérdezésről szólt. Legalábbis nem említette, hogy nagyon write-heavy lenne az alkalmazás. Ha ritka a törlés (pl adminról történik), akkor gyakorlatilag ez mindegy még akkor is, ha 1 másodpercig tart egy update, amit ekkora rekordszámnál én nem hinnék. Az íráson is lehet optimalizálni többféle módon, pl ki lehet tenni kapcsolótáblába stb.
Annál, hogy a lekérdezés pontosan 5 random rekordot választ ki egyetlen lekérdezéssel index alapján, aligha fogsz hatékonyabb eredményt kapni.
Gyors mérés
Mérés jó
Említette, hogy kevés szerzőtől tízezernyi cikk.
A mérés +1, bár a szöveges mezők hiányában nyilván jóval kisebb a táblád a valódinál.
Ezért érdemes kitenni egy
Ezzel megintcsak az a baj,
Ha jól emlékszem
Akkor fogalmazzunk úgy, hogy
Egyetértek, 1000 rekordnál (a
De igazából semmi szükség az
Erre nem gondoltam; így ez tűnik eddig a legjobb megoldásnak.
Úgy van
Sequence ID karbantartás
$rand = mt_rand(0,100);
$sql='blabla where (id & '.$rand.') = '.$rand; // plusz még vmi egyéb random értéknél nagyobb id vizsgálat
esetleg union egyéb querykkel, hogy mindenképp meglegyen az 5 rekord..
pl ha valaminél nagyobbra szűrsz, akkor uniózd az annál kisebbekre is, két subquery limit 5, unió egészére limit 5 szintén
biztos lehet játszani
hátha kapsz valami ihletet..
Ez ugye szintén lassú lesz,
Csinálsz egy extra mezőt,
RAND()
-ot írsz bele, indexeled,SELECT * WHERE rand > $random AND author = $authorId ORDER BY rand DESC LIMIT 1
. Néha meg kell ismételni, mert az összes rand értéknél nagyobb számot választottál, de sok rekord esetén ennek kicsi az esélye (ha nincs sok rekordod, akkor meg használd azORDER BY RAND()
-ot).Az öt cikket egyesével kell lekérni (esetleg egy únióval); szerintem ez semmilyen valóban véletlen kiválasztási módszernél nem kerülhető el.
Szimpatikus
ORDER BY rand
viszontASC
kell, hogy legyen, különben mindig a legmagasabb random értékű sor fog megfelelni.Illetve így egy rekord könnyen "hátrányos helyzetbe" kerülhet, ha olyan random értéket kap, ami épp egy kicsit nagyobb egy már létezőnél – mivel így csak akkor kerülhet kiválasztásra, ha a két érték közé esik a
$random
, tehát máris sérül a véletlenszerűség.10-es
összevegyítve eddigieket egy külön indextáblát csinálnék a random lekérdezéshez, cikk_id, szerző_id, random mezőkkel.
Természetesen kapcsolni kell ONDELETE-re.
A randomba egyszerűen beírsz egy jó nagy nagyságrendű véletlen számot.
- A lekérdezés szíve persze az indextábla, szűrsz szerzőre és egy generált számnál nagyobb értékre, LIMIT 5.
- Ha 5-nél kevesebb eredményt kaptál, akkor megnézed, hogy van-e 5 cikke a szerzőnek, ha van, akkor viszed lejjebb a generált számodat, mondjuk az eredeti 10 %-ával, és újra próbálsz. (Ha nem egyesével viszed lejjebb a számot, akkor picit sérül a véletlenszerűség, de egyesével meg hatalmas lekérdezésmennyiségbe futnál.)
- Ha kevesebb, mint 5 cikke van, akkor lekéred random nélkül az összeset.
- Persze JOIN a cikkekkel, de szerintem az nem jelent gondot.
Ha esetleg a sorrendnek is véletlenszerűnek kell lenni, akkor pl. crontabbal(*) rendszeresen update-eled a random értékeket, vagy csinálsz erre random2 oszlopot, és azt update-eled. Előbbi a "valódi véletlenszerűség" szempontjából jobb.
Természetesen mindehhez ügyes tranzakciókezelés is szükséges.
*: ha nincs cron, akkor pl. cikk insertkor, vagy minden 10. lekérdezés után (akár kiküldött kimenet után, hurokban), stb.
A limit 5-tel az a baj, hogy
Cron
Nem tudsz egyszerre több
Nem muszáj
A cronnal, vagy ezt helyesen kiváltó eseményvezérléssel annyival vagyok beljebb, hogy a kimenet kiküldését nem lassítja, elvileg egyetlen szálon fut (ha valóban cron, akkor biztosan, egyébként meg egy egyszerű fájl IO-val lehet garantálni), és nem szükséges csak mondjuk 10 percenként futtatni, feladatfüggő. (Ugye jelezte is, hogy a cikkes történet csak példa, a tényleges feladat más.)
Ennyire egyedien gondolkodom?
Az írás belockloja a táblát,
Nem
Szerk.: a lényeg, hogy nem a kiválasztott cikktől függ a kezdősorszám (hisz ez is feladat volt), hanem randomtól. Lehet, hogy nincs is jelentősége a cron-nak, mert mire kijön ugyanarra a cikkre ugyanaz a kezdő ajánlott, addigra rég elhal az a látogató, aki az elsőt generálta, mert eltelt vagy 100 év... :)
Hi! Ezt csinálnám: 1. Adott
Ezt csinálnám:
1. Adott szerző cikk darabszámának lekérdezése
2. 5 (n) különböző random szám generálása 1. pontos darabszám maximummal
3. 5 (n) random számmal külön (vagy unionolva) alacsony költségű rowid-re rendezett selectek limittel: SELECT .... ORDER BY <rowid> LIMIT 1 OFFSET <random szám>
Mysql esetén LIMIT 1, <random szám>
H.
Szerk: igazából a rendezés bármire lehet (ha jól indexelt a mező/mezők), csak a rowid volt a legkézenfekvőbb...
Szerk2: fontos: a darab szám lekérdezésnél is csak a nem törölt rekordok darabszáma kell.
Nagy offsettel lekérdezni
Ha tényleg nagyon kevés
Pl: 2 szerző, 50-50% a cikkek aránya, 5 cikkre van szükséged: kiválasztasz 10 cikket teljesen random, aztán statisztikai alapon benne kell lennie 5-nek az egyik szerzőtől. Ennek a költsége nő a szerzők számával, és csak akkor működik hatékonyan, ha közel azonos számú cikk tartozik minden szerzőhöz.
Az egésznél az alap probléma, hogy a teljes eredményhalmazt rendezi az order miatt, és csak utána választ belőle a limit-nek megfelelőt. Helyette azt kéne, hogy lekérdezni az eredményhalmaz hosszát - hány rekord tartozik bele - és random rekord sorszámot generáltatni ez alapján, és utána sorszám alapján kikeresni a nekünk kellő rekordokat. Erre szerintem mysql nem képes, legalábbis nem tudok róla, hogy sorszámozná a találatokat, vagy hogy lenne bármilyen ezzel kapcsolatos függvény. Nyitottam egy témát ezzel a fajta megoldással kapcsolatban: http://stackoverflow.com/questions/19647474/mysql-order-by-rand-faster, kíváncsi vagyok, hogy mi jön ki belőle.
Ezt dobta a gép:
Szóval mysql-ben csak id alapján lehet tippelni, nincs beépített feature ilyesmire...
Nem néztem utána, de
Ja, de pontosan ez az, ami
Ha jól értem, Bence azt
Ez a random rendezős nem bug,
Nem várható el, hogy a mysql kitalálja, hogy a fejlesztő valójában mit akart, és pláne nem azt, hogy ezt még ki is optimalizálja...
Ebben az esetben feature
Küldjél rá légyszives két