Jak losowo wybierać wiersze w SQL?

226

Korzystam z MSSQL Server 2005. W mojej bazie danych mam tabelę „customerNames”, która ma dwie kolumny „Id” i „Name” oraz około. 1000 wyników.

Tworzę funkcjonalność, w której za każdym razem muszę losowo wybierać 5 klientów. Czy ktoś może mi powiedzieć, jak utworzyć zapytanie, które otrzyma losowe 5 wierszy (identyfikator i nazwa) za każdym razem, gdy zapytanie zostanie wykonane?

Prashant
źródło
Losowe nie jest częstym wymogiem dla bazy danych, byłem zaskoczony, że znalazłem link do jakiegoś SQL
Paxic
2
Zależy od tego, ile chcesz losowości. Zobacz: msdn.microsoft.com/en-us/library/aa175776(SQL.80).aspx dla porównania NEW_ID w porównaniu z RAND ()
Shannon Severance

Odpowiedzi:

639
SELECT TOP 5 Id, Name FROM customerNames
ORDER BY NEWID()

To powiedziawszy, wydaje się, że wszyscy odwiedzają tę stronę w celu uzyskania bardziej ogólnej odpowiedzi na twoje pytanie:

Wybieranie losowego wiersza w SQL

Wybierz losowy wiersz za pomocą MySQL:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

Wybierz losowy wiersz za pomocą PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Wybierz losowy wiersz za pomocą Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

Wybierz losowy wiersz za pomocą IBM DB2

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Wybierz losowy rekord z Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

Wybierz losowy wiersz z sqlite:

SELECT column FROM table 
ORDER BY RANDOM() LIMIT 1
Curtis Tasker
źródło
3
+1 za opublikowanie odpowiedzi bezpośrednio w SO zamiast linku do strony zewnętrznej (takiej jak zaakceptowana odpowiedź), która mogła spaść, gdy przyszli użytkownicy spojrzą na to pytanie.
Ray Zhou,
17
Czy staje się to bardzo kosztowne w przypadku dużych tabel, w których każdy wiersz otrzymuje liczbę losową, a następnie sortowany jest duży nieindeksowany zestaw liczb losowych?
Andrey
Być może jest to oczywiste dla większości ludzi, ale nie było dla mnie oczywiste ... następujące zapytanie nie otrzyma nowej losowej wartości dla każdego wiersza: update tbl_vouchers set tbl_UsersID = (select top(1) id from tbl_Users order by NEWID()) - edycja: nie mogę uzyskać formatowania do pracy w komentarzach :(
Mir
Ty geniuszu! Tak bardzo cię nienawidzę, ponieważ nie widziałem tego, dopóki nie odszedłem i nie napisałem niesamowicie długiego zapytania z pod-zapytaniami i numerami wierszy.
greenkode
5
Ostrzeżenie: w przypadku dużych baz danych ta metoda będzie miała niską wydajność. Czy możesz sobie wyobrazić, ile czasu zajmie wygenerowanie losowej wartości dla każdego wiersza, jeśli baza danych ma milion wpisów? Możesz uzyskać więcej informacji i lepszą alternatywę tutaj .
Francis Ngueukam,
35
SELECT TOP 5 Id, Name FROM customerNames ORDER BY NEWID()
Cody Caughlan
źródło
11

W przypadku, gdy ktoś chce rozwiązania PostgreSQL:

select id, name
from customer
order by random()
limit 5;
Barry Brown
źródło
Ta odpowiedź jest dobra dla PostgreSQL, nie potrzebuje limitu.
aliasbody
9

Może ta strona będzie pomocna.

Dla tych, którzy nie chcą klikać:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

źródło
2
powinien co najmniej wymienić 1 na 5 :)
roman m
5

Jeśli masz tabelę z milionami wierszy i zależy Ci na wydajności, może to być lepsza odpowiedź:

SELECT * FROM Table1
WHERE (ABS(CAST(
  (BINARY_CHECKSUM
  (keycol1, NEWID())) as int))
  % 100) < 10

https://msdn.microsoft.com/en-us/library/cc441928.aspx

Tohid
źródło
Pamiętaj, że spowoduje to wybranie około 10% wierszy w tabeli. Jeśli musisz wybrać dokładną liczbę wierszy lub co najmniej N wierszy, to podejście nie zadziała.
LarsH
4

To stare pytanie, ale próba zastosowania nowego pola (NEWID () lub ORDER BY rand ()) do tabeli z dużą liczbą wierszy byłaby zbyt droga. Jeśli masz przyrostowe, unikalne identyfikatory (i nie masz żadnych otworów), bardziej wydajne będzie obliczenie X # identyfikatorów, które mają zostać wybrane, zamiast stosowania identyfikatora GUID lub podobnego do każdego wiersza, a następnie biorąc górny X #.

DECLARE @minValue int;
DECLARE @maxValue int;
SELECT @minValue = min(id), @maxValue = max(id) from [TABLE];

DECLARE @randomId1 int, @randomId2 int, @randomId3 int, @randomId4 int, @randomId5 int
SET @randomId1 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId2 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId3 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId4 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId5 = ((@maxValue + 1) - @minValue) * Rand() + @minValue

--select @maxValue as MaxValue, @minValue as MinValue
--  , @randomId1 as SelectedId1
--  , @randomId2 as SelectedId2
--  , @randomId3 as SelectedId3
--  , @randomId4 as SelectedId4
--  , @randomId5 as SelectedId5

select * from [TABLE] el
where el.id in (@randomId1, @randomId2, @randomId3, @randomId4, @randomId5)

Jeśli chcesz wybrać więcej wierszy, przyjrzałbym się zapełnieniu #tempTable identyfikatorem i szeregiem wartości rand (), a następnie użycie każdej wartości rand () do skalowania do wartości min-max. W ten sposób nie musisz definiować wszystkich parametrów @ randomId1 ... n. Podałem przykład poniżej, używając CTE do wypełnienia początkowej tabeli.

DECLARE @NumItems int = 100;

DECLARE @minValue int;
DECLARE @maxValue int;
SELECT @minValue = min(id), @maxValue = max(id) from [TABLE];
DECLARE @range int = @maxValue+1 - @minValue;

with cte (n) as (
   select 1 union all
   select n+1 from cte
   where n < @NumItems
)
select cast( @range * rand(cast(newid() as varbinary(100))) + @minValue as int) tp
into #Nt
from cte;

select * from #Nt ntt
inner join [TABLE] i on i.id = ntt.tp;

drop table #Nt;
RIanGillis
źródło
@Protiguous, proponowana przez Ciebie edycja przerwała losowy wybór. Użycie min () i max () zastosowanych do dbo. Tabela Tally64k nie pozwoliłaby użytkownikowi wybrać wiersza z pk id> 65556.
RIanGillis 23.0919
Zmiana nazwy tabeli była po prostu artefaktem z testów. Rzeczywista nazwa tabeli nie ma znaczenia, o ile używana jest poprawna tabela. Zarówno min (), jak i max () można zapytać w jednym zapytaniu, a nie w dwóch, co próbowałem pokazać.
Protiguous
@Protiguous Ach, widzę, że teraz byłem zdezorientowany, ponieważ użyłeś 0-65k, robiąc min-max, ale nie później. Po ostatniej edycji naprawdę chciałem zapytać cię o wpływ wprowadzonych zmian na wydajność, ponieważ dostrajanie wydajności jest jednym z moich zainteresowań i pozornie nic nie znaczące decyzje, na przykład po której stronie znaku równości umieścisz coś, co może mieć znaczący wpływ - - Czy to samo dotyczyłoby 5 połączeń SET @ randomId ##? Czy może jest inaczej, ponieważ nie jest WYBIERANIE Z rzeczywistej tabeli?
RIanGillis
Nie jestem pewien, czy rozumiem twoje pytanie. Zastanawiasz się, dlaczego istnieje 5 ZESTAW zamiast 1 WYBIERZ @ id1 = rand (), @ id2 = rand () ..? Jest tak, ponieważ wiele wywołań funkcji rand () w instrukcji 1 daje ten sam wynik, stąd oddzielny zestaw. (rand () na SQL Server jest, jak sądzę, funkcją deterministyczną). Domyślam się, że zestaw 1 selekcja kontra 5 jest pod względem wydajności w zakresie nanosekund.
Protiguous
4
SELECT * FROM TABLENAME ORDER BY random() LIMIT 5; 
Narendra
źródło
Stare pytanie, ale ta odpowiedź nie działała dla mnie w Wyroczni.
Niedźwiedź
SELECT * FROM (SELECT * FROM table ORDER BY DBMS_RANDOM.VALUE) GDZIE rownum <liczba; @Bear spróbuj tego
Narendra
3

Przekonałem się, że najlepiej sprawdza się w przypadku dużych zbiorów danych.

SELECT TOP 1 Column_Name FROM dbo.Table TABLESAMPLE(1 PERCENT);

TABLESAMPLE(n ROWS) or TABLESAMPLE(n PERCENT)jest losowy, ale należy dodać, TOP naby uzyskać prawidłowy rozmiar próbki.

Używanie NEWID()jest bardzo wolne na dużych stołach.

Pałka policjanta
źródło
0

Jak wyjaśniłem w tym artykule , aby przetasować zestaw wyników SQL, musisz użyć wywołania funkcji specyficznej dla bazy danych.

Zauważ, że sortowanie dużego zestawu wyników za pomocą funkcji RANDOM może okazać się bardzo wolne, więc upewnij się, że robisz to na małych zestawach wyników.

Jeśli masz shuffle duży zestaw wyników i ograniczyć je potem, to lepiej użyć coś jak OracleSAMPLE(N) albo TABLESAMPLEw SQL Server lub PostgreSQL zamiast losowej funkcji w klauzuli ORDER BY.

Zakładając, że mamy następującą tabelę bazy danych:

wprowadź opis zdjęcia tutaj

I następujące wiersze w songtabeli:

| id | artist                          | title                              |
|----|---------------------------------|------------------------------------|
| 1  | Miyagi & Эндшпиль ft. Рем Дигга | I Got Love                         |
| 2  | HAIM                            | Don't Save Me (Cyril Hahn Remix)   |
| 3  | 2Pac ft. DMX                    | Rise Of A Champion (GalilHD Remix) |
| 4  | Ed Sheeran & Passenger          | No Diggity (Kygo Remix)            |
| 5  | JP Cooper ft. Mali-Koa          | All This Love                      |

Wyrocznia

W Oracle należy użyć DBMS_RANDOM.VALUEfunkcji, jak pokazano w poniższym przykładzie:

SELECT
    artist||' - '||title AS song
FROM song
ORDER BY DBMS_RANDOM.VALUE

Podczas uruchamiania wyżej wspomnianego zapytania SQL w Oracle otrzymamy następujący zestaw wyników:

| song                                              |
|---------------------------------------------------|
| JP Cooper ft. Mali-Koa - All This Love            |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |

Zauważ, że utwory są wyświetlane w kolejności losowej, dzięki DBMS_RANDOM.VALUEwywołaniu funkcji stosowanemu w klauzuli ORDER BY.

SQL Server

W SQL Server musisz użyć NEWIDfunkcji, jak pokazano w poniższym przykładzie:

SELECT
    CONCAT(CONCAT(artist, ' - '), title) AS song
FROM song
ORDER BY NEWID()

Podczas uruchamiania wspomnianego zapytania SQL na serwerze SQL Server otrzymamy następujący zestaw wyników:

| song                                              |
|---------------------------------------------------|
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |
| JP Cooper ft. Mali-Koa - All This Love            |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |

Zauważ, że utwory są wyświetlane w kolejności losowej, dzięki NEWIDwywołaniu funkcji stosowanemu w klauzuli ORDER BY.

PostgreSQL

W PostgreSQL musisz użyć randomfunkcji, co ilustruje poniższy przykład:

SELECT
    artist||' - '||title AS song
FROM song
ORDER BY random()

Podczas uruchamiania wyżej wspomnianego zapytania SQL na PostgreSQL otrzymamy następujący zestaw wyników:

| song                                              |
|---------------------------------------------------|
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| JP Cooper ft. Mali-Koa - All This Love            |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |

Zauważ, że utwory są wyświetlane w kolejności losowej, dzięki randomwywołaniu funkcji stosowanemu w klauzuli ORDER BY.

MySQL

W MySQL musisz użyć RANDfunkcji, co ilustruje poniższy przykład:

SELECT
  CONCAT(CONCAT(artist, ' - '), title) AS song
FROM song
ORDER BY RAND()

Podczas uruchamiania wyżej wspomnianego zapytania SQL na MySQL otrzymamy następujący zestaw wyników:

| song                                              |
|---------------------------------------------------|
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| JP Cooper ft. Mali-Koa - All This Love            |

Zauważ, że utwory są wyświetlane w kolejności losowej, dzięki RANDwywołaniu funkcji stosowanemu w klauzuli ORDER BY.

Vlad Mihalcea
źródło
0

Jeśli używasz dużej tabeli i chcesz uzyskać dostęp do 10 procent danych, uruchom następujące polecenie: SELECT TOP 10 PERCENT * FROM Table1 ORDER BY NEWID();

Palash Mondal
źródło