Blog - Szara Myszka

v.2.0 beta
Trwają prace nad ulepszeniem kodu stron. Prosimy o cierpliwość. Strony nie zostały porzucone
Login: Hasło:

2010-07-16 13:15:16: Kolejne podbicie wersji bloga po pewnym czasie ciszy

Losowy rekord tabeli z MySQL

bazy Dodano: 2009-02-14 10:55:53
Edytowano: 2009-02-14 11:18:36

Spis treści


Ciekawy problem, czytałem o nim wiele, aż sam postanowiłem na ten temat coś napisać Grinning

Wstęp teoretyczny

Cały ten wpis ma na celu pokazania możliwości w jaki sposób mamy wybrać losowy rekord z bazy danych. Pokazać jaka metoda jest najlepsza i dla kogo. Jeżeli ktoś nie wie czym jest MySQL to może dalej nie czytać, gdyż w tym wpisie nie zostanie to wytłumaczone, gdyż aby szukać odpowiedzi na takie pytanie powinniśmy już wiedzieć czym jest baza i wszystkie zwoązane z tym pojęcia. Resztę zapraszam dalej.

Opis problemu

Pewnie każdy w pewnym momencie będzie potrzebował czegoś losowego na stronę. Losowy wpis z bazy danych, czy może coś innego losowane z bazy danych. Nie chodzi mi tu o jakieś losowe wyświetalnie napisu, bo przeciętny napis można sobie spokojnie na poziomie PHP generować. Chodzi tu o coś w stylu wpisu z bloga, artykule czy czymś podobnym. No a skoro czegoś takiego nie znamy to pytamy wujka Google. Oczywiście znalazłem trochę wpisów na różnych blogach, ale nie każdy podawał właściwe (odpowiednie) rozwiązanie. O tym w dalszej części.

Sposoby rozwiązania

  1. <?php
  2.  
  3. // Wersja pierwsza, bardziej muląca serwer przy dużej ilości rekordów
  4.  
  5. $result = mysql_query('SELECT * FROM `jakas_baza` ORDER BY RAND() LIMIT 1'); // pobiera jeden losowy rekord
  6.  
  7. // A tu funkcja mojej roboty ;)
  8. // uznajemy że $query to treść zapytania z którego mamy zamiar wziąć losowy wpis, czyli np. "SELECT * FROM `cos`", ale nie można dać średnika na końcu !
  9. function losowy($query){
  10.  
  11. $count = mysql_num_rows(mysql_query($query));
  12.  
  13. $num = rand(0, $count - 1);
  14.  
  15. return mysql_query($query . ' LIMIT '.$num.', 1');
  16.  
  17. }
  18.  
  19. /*
  20.  
  21. Trochę bardziej skomplikowana funkcja, ale i najbardziej poprawna, bo we wcześniejszej pobieraliśmy wszystkie pola danego rekordu przez co samo zapytanie także mogło być bardzo mulące. Teraz prezentuję taką ulepszoną wersje oraz wyjaśnienie co jaki zmienna oznacza:
  22.  
  23. $primary - jest to klucz podstawowy danej tabeli, lub pole które ma najmniej danych, po to aby zapytanie zliczające ilość pól było jak najszybsze;
  24. $table - tabela z której chcemy pobrać dane. Nie należy stosować ``, gdyż sama nazwa jest w nie wstawiana;
  25. $fields - pola które należy pobrać po wylosowaniu pola. Jeżeli wszystkie wstaw *, jeżeli jednak inne to wstawiaj je w sposób `pole1`, `pole2`, ...
  26.  
  27. Oczywiście liczę na to, że nie dasz tu czystego zapytania prosto z danymi pobranymi od użytkownika, należy się chronić, gdyż każde dane są potencjalnie niebezpieczne! Polecam mysql_escape_string.
  28.  
  29. */
  30. function losowy_lepszy($primary, $table, $fields){
  31. $count = mysql_num_rows(mysql_query('SELECT COUNT(`'.$primary.'`) FROM `'.$table.'`'));
  32. $num = rand(0, $count - 1);
  33. return mysql_query('SELECT `'.$fields.'` FROM `'.$table.'` LIMIT '.$num.', 1');
  34. }
  35.  
  36. ?><?php
  37.  
  38. // Wersja pierwsza, bardziej muląca serwer przy dużej ilości rekordów
  39.  
  40. $result = mysql_query('SELECT * FROM `jakas_baza` ORDER BY RAND() LIMIT 1'); // pobiera jeden losowy rekord
  41.  
  42. // A tu funkcja mojej roboty ;)
  43. // uznajemy że $query to treść zapytania z którego mamy zamiar wziąć losowy wpis, czyli np. "SELECT * FROM `cos`", ale nie można dać średnika na końcu !
  44. function losowy($query){
  45.  
  46. $count = mysql_num_rows(mysql_query($query));
  47.  
  48. $num = rand(0, $count - 1);
  49.  
  50. return mysql_query($query . ' LIMIT '.$num.', 1');
  51.  
  52. }
  53.  
  54. /*
  55.  
  56. Trochę bardziej skomplikowana funkcja, ale i najbardziej poprawna, bo we wcześniejszej pobieraliśmy wszystkie pola danego rekordu przez co samo zapytanie także mogło być bardzo mulące. Teraz prezentuję taką ulepszoną wersje oraz wyjaśnienie co jaki zmienna oznacza:
  57.  
  58. $primary - jest to klucz podstawowy danej tabeli, lub pole które ma najmniej danych, po to aby zapytanie zliczające ilość pól było jak najszybsze;
  59. $table - tabela z której chcemy pobrać dane. Nie należy stosować ``, gdyż sama nazwa jest w nie wstawiana;
  60. $fields - pola które należy pobrać po wylosowaniu pola. Jeżeli wszystkie wstaw *, jeżeli jednak inne to wstawiaj je w sposób `pole1`, `pole2`, ...
  61.  
  62. Oczywiście liczę na to, że nie dasz tu czystego zapytania prosto z danymi pobranymi od użytkownika, należy się chronić, gdyż każde dane są potencjalnie niebezpieczne! Polecam mysql_escape_string.
  63.  
  64. */
  65. function losowy_lepszy($primary, $table, $fields){
  66. $count = mysql_num_rows(mysql_query('SELECT COUNT(`'.$primary.'`) FROM `'.$table.'`'));
  67. $num = rand(0, $count - 1);
  68. return mysql_query('SELECT `'.$fields.'` FROM `'.$table.'` LIMIT '.$num.', 1');
  69. }
  70.  
  71. ?>

Obserwacje

Sposób pierwszy (czyli samo zapytanie do bazy danych) jest dobre, gdy mamy mała bazę z małą ilością danych. Można to porównać do pracownika, któremu każemy ułożyć losowa 20 klocków, nie zajmie mu to długo, ale jak damy mu 1000 klocków to będzie gorzej. Tak samo jest z bazą danych. Najpierw musi ona poukładać wszystkie rekordy w tabeli losowo, a dopiero potem wybiera konkretny rekord.

Drugi ze sposobów jest lepszy, lecz nie idealny. To czego brakuje w drugim jest uzupełnione w trzecim. A czego brakuje to macie tam w komentarzu przed trzecim napisane Smiling

Podsumowanie

jest wiele sposobów (można oczywiście w to i CRON'a ingerować, tak aby raz na dzień coś wylosował, a potem tylko ciągle ten wylosowany wybieramy), ale w jaki sposób ktoś to zrobi, to zależy tylko od niego. Oczywiście na wielu blogach nie znalazłem wzmianki o tym, że niektóre sposoby nie są tak szybkie przy większej ilości danych i że można to zrobić inaczej. To mnie zainspirowało do napisania tego całego tekstu. Jeżeli komuś się przydało to dobrze Grinning


Zobacz również

Nic nie pasuje? Polecam użyć wyszukiwarki na górze strony.

Wróć

Komentarze

karzeł reakcji pisze:
a czy "select count(*) from tabela" nie będzie szybsze niż "mysql_num_rows(select kolumna from tabela)" ? w pierwszym wypadku baza sięgnie do indeksów, w drugim będzie musiała przejechać się po całej tabeli
Grzegorz Łuszczek pisze:
Nie wiem, ale muszę przyznać, że przetestuje ;)
Grzegorz Łuszczek pisze:
Masz racje, ok 0,01 s. szybciej wykonuje 100 takich operacji ;)
TI pisze:
Inny artykuł na ten temat http://bazawiedzy.cichaprzystan.org/6/jak-wybrac-losowy-rekord-z-bazy/
Grzegorz Łuszczek pisze:
Podobny, dotyczy tak samo losowego wybierania rekordu z bazy danych :) Pokazuje jednak także inne możliwości rozwiązania tego problemu (np. przez skrypt PHP).

Dodaj komentarz

Nick:
Jak mia� na imi� Juliusz S�owacki?:
0.054