Jak pobrać wydajną, prostą próbkę losową w języku SQL? Wspomniana baza danych korzysta z MySQL; moja tabela ma co najmniej 200 000 wierszy, a potrzebuję prostej losowej próbki około 10 000.
„Oczywista” odpowiedź brzmi:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
W przypadku dużych tabel jest to zbyt wolne: wywołuje RAND()
każdy wiersz (który już umieszcza go w pozycji O (n)) i sortuje je, tworząc w najlepszym przypadku O (n lg n). Czy istnieje sposób, aby to zrobić szybciej niż O (n)?
Uwaga : Jak wskazuje Andrew Mao w komentarzach, jeśli używasz tego podejścia na serwerze SQL, powinieneś użyć funkcji T-SQL NEWID()
, ponieważ RAND () może zwrócić tę samą wartość dla wszystkich wierszy .
EDYCJA: 5 LAT PÓŹNIEJ
Ponownie natknąłem się na ten problem z większą tabelą i ostatecznie użyłem wersji rozwiązania @ ignorant, z dwoma poprawkami:
- Wypróbuj wiersze, aby uzyskać 2-5x żądany rozmiar próbki, aby tanio
ORDER BY RAND()
- Zapisz wynik w
RAND()
indeksowanej kolumnie przy każdym wstawieniu / aktualizacji. (Jeśli zestaw danych nie wymaga dużej ilości aktualizacji, może być konieczne znalezienie innego sposobu, aby zachować aktualność tej kolumny).
Aby pobrać próbkę tabeli zawierającą 1000 pozycji, liczę wiersze i próbuję wynik do średnio 10000 wierszy z kolumną frozen_rand:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(Moja rzeczywista implementacja wymaga więcej pracy, aby upewnić się, że nie zaniżam próbki, i aby ręcznie zawijać rand_high, ale podstawową ideą jest „losowe zmniejszenie liczby N do kilku tysięcy”).
Chociaż wymaga to pewnych poświęceń, pozwala mi na próbkowanie bazy danych za pomocą skanowania indeksu, dopóki nie będzie wystarczająco mała, aby ORDER BY RAND()
ponownie.
RAND()
zwraca tę samą wartość przy każdym kolejnym wywołaniu.