Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Losowy rekord
trueblue
post 6.08.2014, 10:07:46
Post #1





Grupa: Zarejestrowani
Postów: 6 799
Pomógł: 1827
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


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.


--------------------
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 10:20:14
Post #2





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




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


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
Crozin
post 6.08.2014, 10:27:47
Post #3





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


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.

Ten post edytował Crozin 6.08.2014, 10:28:21
Go to the top of the page
+Quote Post
trueblue
post 6.08.2014, 10:30:30
Post #4





Grupa: Zarejestrowani
Postów: 6 799
Pomógł: 1827
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


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)];


--------------------
Go to the top of the page
+Quote Post
patryczakowy
post 6.08.2014, 10:33:54
Post #5





Grupa: Zarejestrowani
Postów: 420
Pomógł: 44
Dołączył: 22.10.2008

Ostrzeżenie: (0%)
-----


a co jest w $arr wszystkie rekordy z tabeli?


--------------------
Sztuką jest widzieć to czego nie widać.
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 10:35:20
Post #6





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




@trueblue ale w swoim rozwiązaniu musisz pobrac wszystkie rekordy do php co jest bez sensu


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
Pyton_000
post 6.08.2014, 10:45:49
Post #7





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


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
Go to the top of the page
+Quote Post
trueblue
post 6.08.2014, 10:45:58
Post #8





Grupa: Zarejestrowani
Postów: 6 799
Pomógł: 1827
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


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.


--------------------
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 10:55:30
Post #9





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




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


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
Pyton_000
post 6.08.2014, 11:00:54
Post #10





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


W tym że pobiera tylko jedną kolumnę ID, która jest kluczem
Go to the top of the page
+Quote Post
Crozin
post 6.08.2014, 11:02:19
Post #11





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


@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?
Go to the top of the page
+Quote Post
mmmmmmm
post 6.08.2014, 11:06:40
Post #12





Grupa: Zarejestrowani
Postów: 1 421
Pomógł: 310
Dołączył: 18.04.2012

Ostrzeżenie: (0%)
-----


  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)
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 11:10:40
Post #13





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




@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


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
trueblue
post 6.08.2014, 11:28:24
Post #14





Grupa: Zarejestrowani
Postów: 6 799
Pomógł: 1827
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


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.

Ten post edytował trueblue 6.08.2014, 11:32:07


--------------------
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 11:32:52
Post #15





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




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.


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
Pyton_000
post 6.08.2014, 11:38:54
Post #16





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


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.
Go to the top of the page
+Quote Post
nospor
post 6.08.2014, 11:41:38
Post #17





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




@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.


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
trueblue
post 6.08.2014, 11:42:51
Post #18





Grupa: Zarejestrowani
Postów: 6 799
Pomógł: 1827
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Dziękuję wszystkim za zaangażowanie i pomysły.


--------------------
Go to the top of the page
+Quote Post
Pyton_000
post 6.08.2014, 11:58:59
Post #19





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


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.

Ten post edytował Pyton_000 6.08.2014, 12:00:22
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 22.06.2025 - 01:14