trueblue
6.08.2014, 10:07:46
Witajcie,
zna ktoś może inny sposób niż zopytmalizowany ORDER BY RAND() poprzez ID>=FLOOR(1+RAND()*MAX(ID)) ?
Chodzi o to, że metoda ma mankament przy nieciągłości ID.
Przykładowo:
ID
126 0
134 0,666666667
136 0,833333333
137 0,916666667
138 1
Jak z tego wynika pierwsze ID wpada w połowę dolnego przedziału.
Ostatecznie zastosowałem PHP, ale być może zna ktoś rozwiązanie na poziomie SQL.
nospor
6.08.2014, 10:20:14
Najlepszy bylby limit, ale do tego musisz uzyc jeszcze php
- zliczasz liczbę rekordów
- na podstawie liczby rekordów losujesz liczbę z zakresu 1:liczba rekordów. Robisz to w php przy pomocy rand()
- mając wylosowaną liczbę ($losowa) pobierasz wylosowany rekord ze swojej tabeli przy uzyciu limit
SELECT * FROM `tabela` WHERE limit $losowa,1
Crozin
6.08.2014, 10:27:47
Podstawowe pytanie: jakiej mniej-więcej wielkości jest pula rekordów spośród których chcesz losować wiersz? Jeżeli jest ona względnie niewielka zwykłe ORDER BY RAND() jest jak najbardziej poprawnym rozwiązaniem. Jeżeli jednak tych rekordów jest sporo skorzystaj z rozwiązania zasugerowanego przez @nospor.
trueblue
6.08.2014, 10:30:30
Crozin, nie jest duża, ale ruch w serwisie jest bardzo duży, stąd zdecydowałem o tym.
Rozwiązanie
nospor, ciekawe, choć jeśli i tak mam zaprzęgać PHP, to zastosowałem:
patryczakowy
6.08.2014, 10:33:54
a co jest w $arr wszystkie rekordy z tabeli?
nospor
6.08.2014, 10:35:20
@trueblue ale w swoim rozwiązaniu musisz pobrac wszystkie rekordy do php co jest bez sensu
Pyton_000
6.08.2014, 10:45:49
A może po prostu coś takiego:
EXPLAIN SELECT * FROM TABLE a1
JOIN (SELECT id FROM TABLE a2 ORDER BY RAND() LIMIT 10) aj
WHERE a1.id = aj.id
trueblue
6.08.2014, 10:45:58
Cytat(nospor @ 6.08.2014, 11:35:20 )

@trueblue ale w swoim rozwiązaniu musisz pobrac wszystkie rekordy do php co jest bez sensu
Gdy ich jest maksymalnie 50?
Uprzedzam ponownie, musiałem zoptymalizować ORDER BY RAND(), takie info dostałem od administratorów serwera.
nospor
6.08.2014, 10:55:30
Pry 50 rekordach order by rand nie powinno mulic i byc wolniejsze od tego co robisz w php.
Ale z ciekawosci można by sprawdzić.
ps: tak rozumiem, ze musiales sie tego pozbyc bo ci kazali. Ale to wyglada teraz tak, jakbys jednego "zamulacza" zamienial na drugiego

ps2: jesli bedziesz robil testy to dorzuc tez moją wersję. Kto wie co moze wyjsc przy tak malej liczbie rekordow

@Pyton a niby w czym mialo pomoc Twoje zapytanie? Przeciez tam ciagle jest order by RAND
Pyton_000
6.08.2014, 11:00:54
W tym że pobiera tylko jedną kolumnę ID, która jest kluczem
Crozin
6.08.2014, 11:02:19
@trueblue: Jeżeli masz raptem 50 rekordów spośród których chcesz losować to ORDER BY RAND() pewnie będzie najszybszym rozwiązaniem. Masz w ogóle jakieś konkretne powody by myśleć, że to właśnie to zapytanie odpowiedzialne jest za problemy z wydajnością? Czy tylko ktoś zobaczył ten fragment kodu i od razu uznał, że to musi być powodem wszystkich problemów?
mmmmmmm
6.08.2014, 11:06:40
SELECT * FROM tabela ORDER BY crc32(concat(current_timestamp, id)) LIMIT 10
rownie dobrze mozesz zrobic
SELECT * FROM zamowienia ORDER BY md5(concat(current_timestamp, id)) LIMIT 10
ale z obserwacji wynika, ze crc32 jest nieco szybsze
Ma to taka zalete, ze jesli zapiszesz sobie gdzies current_timestamp, to mozesz odtworzyc kolejnosc. Tak samo jak w ORDER BY Rand(parametr)
nospor
6.08.2014, 11:10:40
@Pyton faktycznie masz racje. Pobranie jednej kolumny dla order by rand jest o niebo szybsze niz wszystkich. Testowalem na 500tys rekordow. Wszystkie losowal w 15 sekund, jedną w pol sekundy
ps: rozwiązanie podane przez mmmmmmm jest bardzo ciekawe i dla 500tys rekordow trwa rowniez ok. pol sekundy i można pobrac od razu wszystkie dane bez joinowania
trueblue
6.08.2014, 11:28:24
1. Moja metoda
2. Nospor ze znikomym opóźnieniem, praktycznie ex aequo
3. ID>=FLOOR(1+RAND()*MAX(ID)) ok. 33% wolniejsze
4. Pyton_000 ok. 57% wolniejsze
5. mmmmmm ok. 62% wolniejsze
Crozin, nie z kodu, z logów, ale nie mam pojęcia jakich, nie wierzę, że ze slowlog.
P.S. Być może na tysiącach rekordów wynik byłby zupełnie inny.
nospor
6.08.2014, 11:32:52
Nom, przy malej liczbie rekordow miejsca 1 i 2 mogly tak sie klasowac. Przy wiekszej bylaby juz znaczaca roznica. No ale jesli faktycznie masz tam tylko 50 rekordow to nie ma co sie szczypac.
Pyton_000
6.08.2014, 11:38:54
Jeżeli różnica rzędu 0.03 to taka wielka różnica

Olałbym sprawę dla takiej małej ilości rekordów, bo faktycznie wykonywanie dziwactwa zajmie więcej czasu niż samo zapytanie.
nospor
6.08.2014, 11:41:38
@Pyton przy 50 rekordach nie ma co oczekiwac, ze roznice będą większe.
Zas przy 500 tysiacach, twoj skrypt wykonuje sie pol sekundy, moj 0,001s.
Zas przy takiej liczbie rekordow (500 tys) rozwiązanie trueblue nie mialoby w ogole sensu.
trueblue
6.08.2014, 11:42:51
Dziękuję wszystkim za zaangażowanie i pomysły.
Pyton_000
6.08.2014, 11:58:59
Można jeszcze taką magią:
SELECT t.*
FROM (
SELECT ROUND(RAND() * (
SELECT MAX(id)
FROM myTable)) num, @num:=@num+1
FROM (
SELECT @num:=0) AS a, myTable
LIMIT 10) AS b, myTable AS t
WHERE b.num = t.id;
Minus taki że nie może być dziur.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę
kliknij tutaj.