Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Losowy rekord
Forum PHP.pl > Forum > Bazy danych > MySQL
trueblue
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
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
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
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:
  1. $el=$arr[rand(0,count($arr)-1)];
patryczakowy
a co jest w $arr wszystkie rekordy z tabeli?
nospor
@trueblue ale w swoim rozwiązaniu musisz pobrac wszystkie rekordy do php co jest bez sensu
Pyton_000
A może po prostu coś takiego:

  1. EXPLAIN SELECT * FROM TABLE a1
  2. JOIN (SELECT id FROM TABLE a2 ORDER BY RAND() LIMIT 10) aj
  3. WHERE a1.id = aj.id
trueblue
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
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 wink.gif

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

@Pyton a niby w czym mialo pomoc Twoje zapytanie? Przeciez tam ciagle jest order by RAND
Pyton_000
W tym że pobiera tylko jedną kolumnę ID, która jest kluczem
Crozin
@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
  1. SELECT * FROM tabela ORDER BY crc32(concat(current_timestamp, id)) LIMIT 10

rownie dobrze mozesz zrobic
  1. 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
@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
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
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
Jeżeli różnica rzędu 0.03 to taka wielka różnica wink.gif

Olałbym sprawę dla takiej małej ilości rekordów, bo faktycznie wykonywanie dziwactwa zajmie więcej czasu niż samo zapytanie.
nospor
@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
Dziękuję wszystkim za zaangażowanie i pomysły.
Pyton_000
Można jeszcze taką magią:
  1. SELECT t.*
  2. FROM (
  3. SELECT ROUND(RAND() * (
  4. SELECT MAX(id)
  5. FROM myTable)) num, @num:=@num+1
  6. FROM (
  7. SELECT @num:=0) AS a, myTable
  8. LIMIT 10) AS b, myTable AS t
  9. 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.
Invision Power Board © 2001-2025 Invision Power Services, Inc.