Zobacz ten post: SQL, aby wybrać losowy wiersz z tabeli bazy danych . Omówiono metody wykonywania tego w MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 i Oracle (z tego łącza kopiowane są następujące elementy):
Wybierz losowy wiersz za pomocą MySQL:
SELECTcolumnFROMtableORDERBY RAND()
LIMIT 1
Wybierz losowy wiersz za pomocą PostgreSQL:
SELECTcolumnFROMtableORDERBY RANDOM()
LIMIT 1
Wybierz losowy wiersz za pomocą Microsoft SQL Server:
SELECTTOP1columnFROMtableORDERBY NEWID()
Wybierz losowy wiersz za pomocą IBM DB2
SELECTcolumn, RAND()as IDX
FROMtableORDERBY IDX FETCH FIRST 1ROWS ONLY
ORDER BY NEWID () wydaje się być znacznie wolniejszy na SQL Server. Moje zapytanie wygląda następująco: wybierz najlepsze 1000 C.CustomerId, CL.LoginName z połączenia wewnętrznego klienta C LinkedAccount LA na C.CustomerId = LA.CustomerId wewnętrzne dołączenie CustomerLogin CL na C.CustomerId = CL.CustomerId grupy przez C.CustomerId, CL. LoginName o liczbie (*)> 1 zamówienie według NEWID () Usunięcie wiersza „zamówienie według NEWID ()” powoduje zwrócenie wyników znacznie szybciej.
Ben Power
3
Do SQLite użyj funkcji RANDOM ().
Slam
10
Te rozwiązania nie są skalowane. Są O(n)z nliczbą rekordów w tabeli. Wyobraź sobie, że masz milion rekordów, czy naprawdę chcesz wygenerować milion losowych liczb lub unikatowych identyfikatorów? Wolałbym użyć COUNT()i włączyć to w nowym LIMITwyrażeniu z pojedynczą liczbą losową.
Christian Hujer
174
Rozwiązania takie jak Jeremies:
SELECT*FROMtableORDERBY RAND() LIMIT 1
działają, ale potrzebują sekwencyjnego skanowania całej tabeli (ponieważ należy obliczyć losową wartość związaną z każdym wierszem - aby można było ustalić najmniejszą), co może być dość powolne w przypadku tabel nawet średnich. Moja rekomendacja to użycie indeksowanej kolumny numerycznej (wiele tabel ma je jako klucze podstawowe), a następnie napisanie czegoś takiego:
SELECT*FROMtableWHERE num_value >= RAND()*(SELECT MAX (num_value )FROMtable)ORDERBY num_value LIMIT 1
Działa to w czasie logarytmicznym, niezależnie od wielkości tabeli, jeśli num_valuejest indeksowane. Jedno zastrzeżenie: zakłada się, że num_valuejest on równomiernie rozłożony w zakresie 0..MAX(num_value). Jeśli zestaw danych silnie odbiega od tego założenia, otrzymasz wypaczone wyniki (niektóre wiersze pojawią się częściej niż inne).
Druga sugestia nie jest przypadkowa. Nie możesz przewidzieć, który rząd zostanie wybrany, ale gdybyś musiał postawić, postawiłbyś na drugi rząd. I nigdy nie postawiłbyś na ostatni rząd, jest mniej prawdopodobne, że zostaniesz wybrany bez względu na rozkład wartości_wartości i jak duży jest twój stół.
Etienne Racine
1
Wiem, że zwykle funkcje RAND () nie są bardzo wysokiej jakości, ale poza tym możesz wyjaśnić, dlaczego wybór nie byłby przypadkowy?
Gray Panther
13
Pierwszy jest NIEPRAWIDŁOWY w SQL Server. Funkcja RAND () jest wywoływana tylko raz na zapytanie, a nie raz na wiersz. Więc zawsze wybiera pierwszy wiersz (spróbuj).
Jeff Walker Code Ranger
3
Drugi zakłada również, że wszystkie wiersze są rozliczone: możliwe jest, że wybierze wiersz, który został usunięty.
Sam Rueby,
3
@ Sam.Rueby W rzeczywistości liczba_wartości> = RAND () ... limit 1 zapewnia, że puste wiersze będą pomijane, dopóki nie znajdzie się wiersz opuszczający.
ghord
62
Nie wiem, jak to jest wydajne, ale używałem go wcześniej:
SELECTTOP1*FROM MyTable ORDERBY newid()
Ponieważ identyfikatory GUID są dość losowe, kolejność oznacza, że otrzymujesz losowy wiersz.
Korzystam z serwera MS SQL, WYBIERZ TOP 1 * FROM some_table_name ORDER BY NEWID () działał dla mnie świetnie, dzięki za porady chłopaki!
To dokładnie to samo, coORDER BY RAND() LIMIT 1
Ken Bloom
6
Jest to również bardzo specyficzne dla bazy danych, ponieważ używa TOP 1i newid().
Gray
12
To jest zły pomysł. Ta metoda nie używa indeksu, chyba że każda kolumna jest indeksowana indywidualnie. Tabela z 100 milionami rekordów może zająć bardzo dużo czasu, aby uzyskać jeden rekord.
Switch
1
@ Przełącz i jakie rozwiązanie byś zaproponował?
Akmal Salikhov,
31
ORDERBY NEWID()
trwa 7.4 milliseconds
WHERE num_value >= RAND()*(SELECT MAX(num_value)FROMtable)
Druga opcja nie wybiera ostatniego rzędu. Nie wiem dlaczego - po prostu to zaznaczam.
Voldemort
7
@Voldemort: rand()zwraca liczbę zmiennoprzecinkową ngdzie 0 < n < 1. Zakładając, że num_valuejest liczbą całkowitą, zwracana wartość rand() * max(num_value)zostanie również wymuszona na liczbę całkowitą, co spowoduje obcięcie wszystkiego po przecinku. Stąd rand() * max(num_value)będzie zawsze być mniejsza niż max(num_value), dlatego nigdy nie zostanie wybrany ostatni wiersz.
Ian Kemp
Nie będę efektywny, jeśli moje dane będą często usuwane - jeśli znajdę lukę, będę musiał ponownie uruchomić całe zapytanie.
Loic Coenen
1
@IanKemp Głupie pytanie, więc dlaczego po prostu nie użyć SELECT MAX (wartość_wartości) + 1 ?? Ponieważ rand (lub w większości przypadków RANDOM) zwraca [0,1), otrzymasz pełny zakres wartości. Poza tym tak, masz rację, musisz naprawić zapytanie.
tekHedd,
13
Nie powiedziałeś, którego serwera używasz. W starszych wersjach SQL Server możesz użyć tego:
selecttop1*from mytable orderby newid()
W SQL Server 2005 i nowszych można użyć TABLESAMPLElosowej próbki, która jest powtarzalna:
SELECT FirstName, LastName
FROM Contact
TABLESAMPLE (1ROWS);
@Andrew Hedges: ZAMÓWIENIE BY NEWID () jest zbyt kosztowne
Andrei Rînea
10
Dla SQL Server
Funkcja newid () / order by będzie działać, ale będzie bardzo kosztowna dla dużych zestawów wyników, ponieważ musi wygenerować identyfikator dla każdego wiersza, a następnie je posortować.
TABLESAMPLE () jest dobry z punktu widzenia wydajności, ale dostaniesz zbijanie wyników (wszystkie wiersze na stronie zostaną zwrócone).
Aby uzyskać lepszą skuteczność prawdziwej próbki losowej, najlepszym sposobem jest losowe odfiltrowanie wierszy. Znalazłem następujący przykładowy kod w artykule SQL Server Books Online Ograniczanie zestawów wyników za pomocą TABLESAMPLE :
Jeśli naprawdę chcesz losowej próbki pojedynczych wierszy, zmodyfikuj zapytanie, aby odfiltrować wiersze losowo, zamiast używać TABLESAMPLE. Na przykład w poniższym zapytaniu użyto funkcji NEWID do zwrócenia około jednego procenta wierszy tabeli Sales.SalesOrderDetail:
Kolumna SalesOrderID jest zawarta w wyrażeniu CHECKSUM, dzięki czemu NEWID () ocenia raz na wiersz, aby uzyskać próbkowanie na podstawie wiersza. Wyrażenie CAST (CHECKSUM (NEWID (), SalesOrderID) i 0x7fffffff AS float / CAST (0x7fffffff AS int) zwraca losową wartość od 0 do 1.
Po uruchomieniu z tabelą zawierającą 1 000 000 wierszy, oto moje wyniki:
SETSTATISTICS TIME ONSETSTATISTICS IO ON/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/SELECTTOP1PERCENT Number
FROM Numbers
ORDERBY newid()/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/SELECT Number
FROM Numbers
TABLESAMPLE (1PERCENT)/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/SELECT Number
FROM Numbers
WHERE0.01>= CAST(CHECKSUM(NEWID(), Number)&0x7fffffffAS float)/ CAST (0x7fffffffAS int)SETSTATISTICS IO OFFSETSTATISTICS TIME OFF
Jeśli możesz uciec od używania TABLESAMPLE, zapewni to najlepszą wydajność. W przeciwnym razie użyj metody newid () / filter. newid () / order by powinno być ostatecznością, jeśli masz duży zestaw wyników.
To rozwiązanie zajmuje się także zwracaniem losowych wierszy, gdy indeksowana wartość liczbowa stosowana w powyższej klauzuli where nie jest równo rozłożona; więc nawet jeśli zajmuje to prawie taki sam (stały) czas, jak użycie gdzie id_value> = RAND () * MAX (id_value), to lepiej.
guido
O ile wiem, nie działa to w stałym czasie, działa w czasie liniowym. W najgorszym przypadku @n jest równy liczbie wierszy w tabeli, a „WYBIERZ * Z LIMITU tabeli? 1” ocenia @n - 1 wierszy, aż dojdzie do ostatniego.
Andres Riofrio,
3
Najlepszym sposobem jest umieszczenie losowej wartości w nowej kolumnie tylko w tym celu i użycie czegoś takiego (kod pseude + SQL):
randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")
Jest to rozwiązanie zastosowane w kodzie MediaWiki. Oczywiście istnieje pewne odchylenie w stosunku do mniejszych wartości, ale stwierdzono, że wystarczyło zawinąć losową wartość do zera, gdy nie zostaną pobrane żadne wiersze.
Rozwiązanie newid () może wymagać pełnego skanowania tabeli, aby do każdego wiersza można było przypisać nowy identyfikator GUID, który będzie znacznie mniej wydajny.
rozwiązanie rand () może w ogóle nie działać (tj. z MSSQL), ponieważ funkcja zostanie oceniona tylko raz, a do każdego wiersza zostanie przypisany ten sam „losowy” numer.
Zawijanie, gdy otrzymasz 0 wyników, zapewnia próbkę o możliwej do udowodnienia losowości (nie tylko „wystarczająco dobrą”). To rozwiązanie prawie skaluje się do zapytań wielowierszowych (pomyśl „przetasowanie partii”). Problem polega na tym, że wyniki są często wybierane w tych samych grupach. Aby obejść ten problem, musisz ponownie rozdzielić losowe liczby, których właśnie użyłeś. Możesz oszukiwać, śledząc randomNo i ustawiając go na maksimum (losowość) z wyników, ale następnie p (wiersz i w zapytaniu 1 ORAZ wiersz i w zapytaniu 2) == 0, co jest niesprawiedliwe. Pozwól mi zrobić matematykę, a wrócę do ciebie z naprawdę uczciwym planem.
alsuren
3
W przypadku SQL Server 2005 i 2008, jeśli chcemy losową próbkę pojedynczych wierszy (z Books Online ):
SELECT ID FROMTABLEWHERE ID >= My_Generated_Random ORDERBY ID LIMIT 1
Zauważ, że sprawdzi, czy wszystkie wiersze są identyczne lub WYŻSZE niż wybrana wartość. Możliwe jest również wyszukanie wiersza w dół tabeli i uzyskanie identycznego lub niższego identyfikatora niż My_Generated_Random, a następnie zmodyfikowanie zapytania w następujący sposób:
SELECT ID FROMTABLEWHERE ID <= My_Generated_Random ORDERBY ID DESC LIMIT 1
Co by się stało, gdyby wygenerowany losowy identyfikator nie istniał już w tabeli? Usunięte lub pasywne wiersze, których nie chcesz pokazywać użytkownikowi, spowodowałyby problemy.
Ebleme
Nic. Otrzymasz NAJBLIŻSZY, nie dokładny numer identyfikacyjny. Jeśli uważasz, że id = 1 należy usunąć, wymień 1 na minimum.
forsberg,
2
Jak wskazano w komentarzu @ BillKarwin do odpowiedzi @ cnu ...
Kiedy kombinuję z LIMITEM, odkryłem, że działa znacznie lepiej (przynajmniej w PostgreSQL 9.1) do ŁĄCZENIA z losowym porządkowaniem, a nie bezpośrednio porządkować rzeczywiste wiersze: np.
SELECT*FROM tbl_post AS t
JOIN...JOIN(SELECT id, CAST(-2147483648* RANDOM()AS integer)AS rand
FROM tbl_post
WHERE create_time >=1349928000) r ON r.id = t.id
WHERE create_time >=1349928000AND...ORDERBY r.rand
LIMIT 100
Upewnij się tylko, że „r” generuje wartość „rand” dla każdej możliwej wartości klucza w złożonym zapytaniu, które jest z nią połączone, ale nadal ogranicza liczbę wierszy „r”, jeśli to możliwe.
CAST as Integer jest szczególnie pomocny dla PostgreSQL 9.2, który ma specyficzną optymalizację sortowania dla liczb całkowitych i zmiennoprzecinkowych o pojedynczej precyzji.
Większość rozwiązań tutaj ma na celu uniknięcie sortowania, ale nadal muszą wykonać sekwencyjne skanowanie tabeli.
Istnieje również sposób na uniknięcie skanowania sekwencyjnego poprzez przełączenie na skanowanie indeksu. Jeśli znasz wartość indeksu losowego wiersza, możesz uzyskać wynik niemal natychmiast. Problem polega na tym, jak odgadnąć wartość indeksu.
Następujące rozwiązanie działa na PostgreSQL 8.4:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))
limit 1;
Powyżej rozwiązania odgadniesz 10 różnych losowych wartości indeksu z zakresu 0 .. [ostatnia wartość id].
Liczba 10 jest dowolna - możesz użyć 100 lub 1000, ponieważ (zadziwiająco) nie ma to dużego wpływu na czas reakcji.
Jest też jeden problem - jeśli masz rzadkie identyfikatory, których możesz przegapić . Rozwiązaniem jest mieć plan tworzenia kopii zapasowych :) W tym przypadku czyste stare zamówienie przez losowe () zapytanie. Po połączeniu id wygląda następująco:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))unionall(select*from cms_refs orderby random() limit 1)
limit 1;
Nie klauzula unii ALL . W takim przypadku, jeśli pierwsza część zwróci jakiekolwiek dane, NIGDY nie zostanie wykonana!
Późno, ale dotarłem tutaj przez Google, więc dla potomności dodam alternatywne rozwiązanie.
Innym podejściem jest dwukrotne użycie TOP, przy naprzemiennych zamówieniach. Nie wiem, czy jest to „czysty SQL”, ponieważ wykorzystuje zmienną w TOP, ale działa w SQL Server 2008. Oto przykład, którego używam w stosunku do tabeli słów słownikowych, jeśli chcę losowe słowo.
SELECTTOP1
word
FROM(SELECTTOP(@idx)
word
FROM
dbo.DictionaryAbridged WITH(NOLOCK)ORDERBY
word DESC)AS D
ORDERBY
word ASC
Oczywiście @idx to losowo generowana liczba całkowita, która zawiera się w przedziale od 1 do COUNT (*) w tabeli docelowej, włącznie. Jeśli Twoja kolumna jest zaindeksowana, również z niej skorzystasz. Kolejną zaletą jest to, że można go używać w funkcji, ponieważ NEWID () jest niedozwolony.
Na koniec powyższe zapytanie działa w około 1/10 czasu wykonania zapytania typu NEWID () w tej samej tabeli. RRMV.
Po przetestowaniu wielu odpowiedzi uważam, że jest to najlepsza. Wydaje się być szybki i za każdym razem wybiera dobrą liczbę losową. Wydaje się, że jest podobny do drugiej sugestii @GreyPanther powyżej, ale ta odpowiedź wybiera więcej liczb losowych.
Jeff Baker
1
Nie widziałem jeszcze tej zmiany w odpowiedziach. Miałem dodatkowe ograniczenie, gdzie musiałem, biorąc pod uwagę początkowe ziarno, aby wybrać ten sam zestaw wierszy za każdym razem.
NewId()jest nieznacznie wolniejszy niż rand(checksum(*)), więc możesz nie chcieć używać go do dużych zestawów płyt.
Wybór z początkowym nasionem:
declare@seed int
set@seed = Year(getdate())* month(getdate())/* any other initial seed here */selecttop10percent*from table_name
orderby rand(checksum(*)% seed)/* any other math function here */
Jeśli musisz wybrać ten sam zestaw dla danego ziarna, wydaje się, że to działa.
W SQL Server możesz łączyć TABLESAMPLE z NEWID (), aby uzyskać całkiem dobrą losowość i nadal mieć szybkość. Jest to szczególnie przydatne, jeśli naprawdę chcesz tylko 1 lub niewielką liczbę wierszy.
W SQL Server 2012+ można użyć kwerendy FETCH OFFSET, aby zrobić to dla pojedynczego losowego wiersza
select*from MyTable ORDERBY id OFFSET n ROWFETCH NEXT 1ROWS ONLY
gdzie id to kolumna tożsamości, a n to żądany wiersz - obliczany jako liczba losowa między 0 a count () - 1 tabeli (offset 0 to przecież pierwszy wiersz)
Działa to z otworami w danych tabeli, o ile masz indeks do pracy dla klauzuli ORDER BY. Jest to również bardzo dobre dla losowości - gdy ćwiczysz, że się poddajesz, ale w innych metodach nie ma drobiazgów. Ponadto wydajność jest całkiem dobra, na mniejszym zestawie danych dobrze się trzyma, chociaż nie próbowałem poważnych testów wydajności w stosunku do kilku milionów wierszy.
Dziesięć lat temu (2005) jakiś facet powiedział, że używanie ORDER BY RAND()jest złe ...
trejder
0
Muszę się zgodzić z CD-MaN: użycie „ORDER BY RAND ()” będzie działało dobrze na małych stolikach lub gdy wykonasz SELECT tylko kilka razy.
Używam również techniki „num_value> = RAND () * ...”, a jeśli naprawdę chcę uzyskać losowe wyniki, mam specjalną „losową” kolumnę w tabeli, którą aktualizuję raz dziennie. To pojedyncze uruchomienie UPDATE zajmie trochę czasu (zwłaszcza, że będziesz musiał mieć indeks w tej kolumnie), ale jest znacznie szybsze niż tworzenie liczb losowych dla każdego wiersza za każdym razem, gdy uruchamiany jest wybór.
Bądź ostrożny, ponieważ TableSample nie zwraca losowej próbki wierszy. Kieruje zapytanie do losowej próbki stron o wielkości 8 KB, które składają się na wiersz. Następnie zapytanie jest wykonywane na podstawie danych zawartych na tych stronach. Ze względu na sposób grupowania danych na tych stronach (kolejność wstawiania itp.) Może to prowadzić do danych, które w rzeczywistości nie są przypadkową próbką.
Wygląda na to, że wiele z wymienionych pomysłów nadal korzysta z funkcji zamawiania
Jeśli jednak używasz tabeli tymczasowej, możesz przypisać losowy indeks (jak sugeruje wiele rozwiązań), a następnie pobrać pierwszy, który jest większy niż dowolna liczba z zakresu od 0 do 1.
Na przykład (dla DB2):
WITH TEMP AS(SELECT COMLUMN, RAND()AS IDX FROMTABLE)SELECTCOLUMNFROMTABLEWHERE IDX >.5FETCH FIRST 1ROW ONLY
Po rozważeniu tego rozwiązania znalazłem zasadniczą wadę w mojej logice. Spowodowałoby to konsekwentnie zwracanie tych samych małych wartości ustawień, blisko początku tabeli, ponieważ zakładam, że jeśli byłby równomierny rozkład między 0 a 1, istnieje 50% szans, że pierwszy rząd spełni te kryteria.
Jest lepsze rozwiązanie dla Oracle zamiast korzystania z dbms_random.value, podczas gdy wymaga pełnego skanowania, aby uporządkować wiersze według dbms_random.value, a dla dużych tabel jest dość wolne.
W przypadku programu SQL Server 2005 i nowszych rozszerzenie odpowiedzi @ GreyPanther w przypadkach, gdy num_valuenie ma ciągłych wartości. Działa to również w przypadkach, gdy nie rozprowadziliśmy równomiernie zestawów danych i gdy num_valuenie jest liczbą, ale unikalnym identyfikatorem.
WITH CTE_Table (SelRow, num_value)AS(SELECT ROW_NUMBER()OVER(ORDERBY ID)AS SelRow, num_value FROMtable)SELECT*FROMtableWhere num_value =(SELECTTOP1 num_value FROM CTE_Table WHERE SelRow >= RAND()*(SELECT MAX(SelRow)FROM CTE_Table))
Odpowiedzi:
Zobacz ten post: SQL, aby wybrać losowy wiersz z tabeli bazy danych . Omówiono metody wykonywania tego w MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 i Oracle (z tego łącza kopiowane są następujące elementy):
Wybierz losowy wiersz za pomocą MySQL:
Wybierz losowy wiersz za pomocą PostgreSQL:
Wybierz losowy wiersz za pomocą Microsoft SQL Server:
Wybierz losowy wiersz za pomocą IBM DB2
Wybierz losowy rekord z Oracle:
źródło
order by rand()
lub równoważne we wszystkich dbs: | wspomniano również tutaj .ORDER BY RAND()
jest złe ...O(n)
zn
liczbą rekordów w tabeli. Wyobraź sobie, że masz milion rekordów, czy naprawdę chcesz wygenerować milion losowych liczb lub unikatowych identyfikatorów? Wolałbym użyćCOUNT()
i włączyć to w nowymLIMIT
wyrażeniu z pojedynczą liczbą losową.Rozwiązania takie jak Jeremies:
działają, ale potrzebują sekwencyjnego skanowania całej tabeli (ponieważ należy obliczyć losową wartość związaną z każdym wierszem - aby można było ustalić najmniejszą), co może być dość powolne w przypadku tabel nawet średnich. Moja rekomendacja to użycie indeksowanej kolumny numerycznej (wiele tabel ma je jako klucze podstawowe), a następnie napisanie czegoś takiego:
Działa to w czasie logarytmicznym, niezależnie od wielkości tabeli, jeśli
num_value
jest indeksowane. Jedno zastrzeżenie: zakłada się, żenum_value
jest on równomiernie rozłożony w zakresie0..MAX(num_value)
. Jeśli zestaw danych silnie odbiega od tego założenia, otrzymasz wypaczone wyniki (niektóre wiersze pojawią się częściej niż inne).źródło
Nie wiem, jak to jest wydajne, ale używałem go wcześniej:
Ponieważ identyfikatory GUID są dość losowe, kolejność oznacza, że otrzymujesz losowy wiersz.
źródło
ORDER BY RAND() LIMIT 1
TOP 1
inewid()
.trwa
7.4 milliseconds
bierze
0.0065 milliseconds
!Zdecydowanie wybiorę tę drugą metodę.
źródło
rand()
zwraca liczbę zmiennoprzecinkowąn
gdzie0 < n < 1
. Zakładając, żenum_value
jest liczbą całkowitą, zwracana wartośćrand() * max(num_value)
zostanie również wymuszona na liczbę całkowitą, co spowoduje obcięcie wszystkiego po przecinku. Stądrand() * max(num_value)
będzie zawsze być mniejsza niżmax(num_value)
, dlatego nigdy nie zostanie wybrany ostatni wiersz.Nie powiedziałeś, którego serwera używasz. W starszych wersjach SQL Server możesz użyć tego:
W SQL Server 2005 i nowszych można użyć
TABLESAMPLE
losowej próbki, która jest powtarzalna:źródło
Dla SQL Server
Funkcja newid () / order by będzie działać, ale będzie bardzo kosztowna dla dużych zestawów wyników, ponieważ musi wygenerować identyfikator dla każdego wiersza, a następnie je posortować.
TABLESAMPLE () jest dobry z punktu widzenia wydajności, ale dostaniesz zbijanie wyników (wszystkie wiersze na stronie zostaną zwrócone).
Aby uzyskać lepszą skuteczność prawdziwej próbki losowej, najlepszym sposobem jest losowe odfiltrowanie wierszy. Znalazłem następujący przykładowy kod w artykule SQL Server Books Online Ograniczanie zestawów wyników za pomocą TABLESAMPLE :
Po uruchomieniu z tabelą zawierającą 1 000 000 wierszy, oto moje wyniki:
Jeśli możesz uciec od używania TABLESAMPLE, zapewni to najlepszą wydajność. W przeciwnym razie użyj metody newid () / filter. newid () / order by powinno być ostatecznością, jeśli masz duży zestaw wyników.
źródło
Jeśli to możliwe, użyj przechowywanych instrukcji, aby uniknąć nieskuteczności obu indeksów w RND () i tworząc pole numeru rekordu.
źródło
Najlepszym sposobem jest umieszczenie losowej wartości w nowej kolumnie tylko w tym celu i użycie czegoś takiego (kod pseude + SQL):
Jest to rozwiązanie zastosowane w kodzie MediaWiki. Oczywiście istnieje pewne odchylenie w stosunku do mniejszych wartości, ale stwierdzono, że wystarczyło zawinąć losową wartość do zera, gdy nie zostaną pobrane żadne wiersze.
Rozwiązanie newid () może wymagać pełnego skanowania tabeli, aby do każdego wiersza można było przypisać nowy identyfikator GUID, który będzie znacznie mniej wydajny.
rozwiązanie rand () może w ogóle nie działać (tj. z MSSQL), ponieważ funkcja zostanie oceniona tylko raz, a do każdego wiersza zostanie przypisany ten sam „losowy” numer.
źródło
W przypadku SQL Server 2005 i 2008, jeśli chcemy losową próbkę pojedynczych wierszy (z Books Online ):
źródło
Natychmiastowe użycie RAND (), ponieważ nie jest to zalecane , możesz po prostu uzyskać max ID (= Max):
uzyskaj losowość między 1..Max (= My_Generated_Random)
a następnie uruchom ten SQL:
Zauważ, że sprawdzi, czy wszystkie wiersze są identyczne lub WYŻSZE niż wybrana wartość. Możliwe jest również wyszukanie wiersza w dół tabeli i uzyskanie identycznego lub niższego identyfikatora niż My_Generated_Random, a następnie zmodyfikowanie zapytania w następujący sposób:
źródło
Jak wskazano w komentarzu @ BillKarwin do odpowiedzi @ cnu ...
Kiedy kombinuję z LIMITEM, odkryłem, że działa znacznie lepiej (przynajmniej w PostgreSQL 9.1) do ŁĄCZENIA z losowym porządkowaniem, a nie bezpośrednio porządkować rzeczywiste wiersze: np.
Upewnij się tylko, że „r” generuje wartość „rand” dla każdej możliwej wartości klucza w złożonym zapytaniu, które jest z nią połączone, ale nadal ogranicza liczbę wierszy „r”, jeśli to możliwe.
CAST as Integer jest szczególnie pomocny dla PostgreSQL 9.2, który ma specyficzną optymalizację sortowania dla liczb całkowitych i zmiennoprzecinkowych o pojedynczej precyzji.
źródło
Większość rozwiązań tutaj ma na celu uniknięcie sortowania, ale nadal muszą wykonać sekwencyjne skanowanie tabeli.
Istnieje również sposób na uniknięcie skanowania sekwencyjnego poprzez przełączenie na skanowanie indeksu. Jeśli znasz wartość indeksu losowego wiersza, możesz uzyskać wynik niemal natychmiast. Problem polega na tym, jak odgadnąć wartość indeksu.
Następujące rozwiązanie działa na PostgreSQL 8.4:
Powyżej rozwiązania odgadniesz 10 różnych losowych wartości indeksu z zakresu 0 .. [ostatnia wartość id].
Liczba 10 jest dowolna - możesz użyć 100 lub 1000, ponieważ (zadziwiająco) nie ma to dużego wpływu na czas reakcji.
Jest też jeden problem - jeśli masz rzadkie identyfikatory, których możesz przegapić . Rozwiązaniem jest mieć plan tworzenia kopii zapasowych :) W tym przypadku czyste stare zamówienie przez losowe () zapytanie. Po połączeniu id wygląda następująco:
Nie klauzula unii ALL . W takim przypadku, jeśli pierwsza część zwróci jakiekolwiek dane, NIGDY nie zostanie wykonana!
źródło
Późno, ale dotarłem tutaj przez Google, więc dla potomności dodam alternatywne rozwiązanie.
Innym podejściem jest dwukrotne użycie TOP, przy naprzemiennych zamówieniach. Nie wiem, czy jest to „czysty SQL”, ponieważ wykorzystuje zmienną w TOP, ale działa w SQL Server 2008. Oto przykład, którego używam w stosunku do tabeli słów słownikowych, jeśli chcę losowe słowo.
Oczywiście @idx to losowo generowana liczba całkowita, która zawiera się w przedziale od 1 do COUNT (*) w tabeli docelowej, włącznie. Jeśli Twoja kolumna jest zaindeksowana, również z niej skorzystasz. Kolejną zaletą jest to, że można go używać w funkcji, ponieważ NEWID () jest niedozwolony.
Na koniec powyższe zapytanie działa w około 1/10 czasu wykonania zapytania typu NEWID () w tej samej tabeli. RRMV.
źródło
Możesz także spróbować użyć
new id()
funkcji.Wystarczy napisać zapytanie i użyć kolejności według
new id()
funkcji. To dość losowe.źródło
Aby MySQL uzyskał losowy rekord
Więcej szczegółów http://jan.kneschke.de/projects/mysql/order-by-rand/
źródło
Nie widziałem jeszcze tej zmiany w odpowiedziach. Miałem dodatkowe ograniczenie, gdzie musiałem, biorąc pod uwagę początkowe ziarno, aby wybrać ten sam zestaw wierszy za każdym razem.
W przypadku MS SQL:
Minimalny przykład:
Znormalizowany czas wykonania: 1,00
Przykład NewId ():
Znormalizowany czas wykonania: 1,02
NewId()
jest nieznacznie wolniejszy niżrand(checksum(*))
, więc możesz nie chcieć używać go do dużych zestawów płyt.Wybór z początkowym nasionem:
Jeśli musisz wybrać ten sam zestaw dla danego ziarna, wydaje się, że to działa.
źródło
W MSSQL (testowany na 11.0.5569) przy użyciu
jest znacznie szybszy niż
źródło
W SQL Server możesz łączyć TABLESAMPLE z NEWID (), aby uzyskać całkiem dobrą losowość i nadal mieć szybkość. Jest to szczególnie przydatne, jeśli naprawdę chcesz tylko 1 lub niewielką liczbę wierszy.
źródło
W SQL Server 2012+ można użyć kwerendy FETCH OFFSET, aby zrobić to dla pojedynczego losowego wiersza
gdzie id to kolumna tożsamości, a n to żądany wiersz - obliczany jako liczba losowa między 0 a count () - 1 tabeli (offset 0 to przecież pierwszy wiersz)
Działa to z otworami w danych tabeli, o ile masz indeks do pracy dla klauzuli ORDER BY. Jest to również bardzo dobre dla losowości - gdy ćwiczysz, że się poddajesz, ale w innych metodach nie ma drobiazgów. Ponadto wydajność jest całkiem dobra, na mniejszym zestawie danych dobrze się trzyma, chociaż nie próbowałem poważnych testów wydajności w stosunku do kilku milionów wierszy.
źródło
źródło
ORDER BY RAND()
jest złe ...Muszę się zgodzić z CD-MaN: użycie „ORDER BY RAND ()” będzie działało dobrze na małych stolikach lub gdy wykonasz SELECT tylko kilka razy.
Używam również techniki „num_value> = RAND () * ...”, a jeśli naprawdę chcę uzyskać losowe wyniki, mam specjalną „losową” kolumnę w tabeli, którą aktualizuję raz dziennie. To pojedyncze uruchomienie UPDATE zajmie trochę czasu (zwłaszcza, że będziesz musiał mieć indeks w tej kolumnie), ale jest znacznie szybsze niż tworzenie liczb losowych dla każdego wiersza za każdym razem, gdy uruchamiany jest wybór.
źródło
Bądź ostrożny, ponieważ TableSample nie zwraca losowej próbki wierszy. Kieruje zapytanie do losowej próbki stron o wielkości 8 KB, które składają się na wiersz. Następnie zapytanie jest wykonywane na podstawie danych zawartych na tych stronach. Ze względu na sposób grupowania danych na tych stronach (kolejność wstawiania itp.) Może to prowadzić do danych, które w rzeczywistości nie są przypadkową próbką.
Zobacz: http://www.mssqltips.com/tip.asp?tip=1308
Ta strona MSDN dla TableSample zawiera przykład generowania faktycznie losowej próbki danych.
http://msdn.microsoft.com/en-us/library/ms189108.aspx
źródło
Wygląda na to, że wiele z wymienionych pomysłów nadal korzysta z funkcji zamawiania
Jeśli jednak używasz tabeli tymczasowej, możesz przypisać losowy indeks (jak sugeruje wiele rozwiązań), a następnie pobrać pierwszy, który jest większy niż dowolna liczba z zakresu od 0 do 1.
Na przykład (dla DB2):
źródło
Prosty i wydajny sposób z http://akinas.com/pages/en/blog/mysql_random_row/
źródło
Jest lepsze rozwiązanie dla Oracle zamiast korzystania z dbms_random.value, podczas gdy wymaga pełnego skanowania, aby uporządkować wiersze według dbms_random.value, a dla dużych tabel jest dość wolne.
Zamiast tego użyj tego:
źródło
W przypadku Firebird:
źródło
W przypadku programu SQL Server 2005 i nowszych rozszerzenie odpowiedzi @ GreyPanther w przypadkach, gdy
num_value
nie ma ciągłych wartości. Działa to również w przypadkach, gdy nie rozprowadziliśmy równomiernie zestawów danych i gdynum_value
nie jest liczbą, ale unikalnym identyfikatorem.źródło
Pomocna może być funkcja losowa z sql. Również jeśli chcesz ograniczyć się do jednego wiersza, po prostu dodaj go na końcu.
źródło