Wybierz N losowych wierszy z tabeli SQL Server

Mam tabelę SQL Server z około 50 000 wierszy w niej. Chcę wybrać około 5000 tych rzędów losowo. Wymyśliłem skomplikowany sposób, tworząc tabelę tymczasową z kolumną "losowa liczba", kopiując do niej tabelę, przechodząc przez tabelę tymczasową i aktualizując każdy wiersz za pomocą RAND(), a następnie wybierając z tej tabeli kolumnę z liczbą losową

Ten artykuł sugeruje użycie funkcja NEWID(). Wygląda to obiecująco, ale nie widzę, jak mogłem niezawodnie wybrać pewien procent wierszy.

Czy ktoś już to robił? Jakieś pomysły?
Author: Peter O., 2009-05-11

15 answers

select top 10 percent * from [yourtable] order by newid()

W odpowiedzi na komentarz "pure trash" dotyczący dużych tabel: możesz to zrobić w ten sposób, aby poprawić wydajność.

select  * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid())

Koszt tego będzie kluczowym skanowaniem wartości plus koszt połączenia, który na dużym stole z małym wyborem procentowym powinien być rozsądny.

 335
Author: Ralph Shillington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-04 21:11:18

W zależności od twoich potrzeb, TABLESAMPLE zapewni Ci prawie tak samo losową i lepszą wydajność. jest to dostępne na MS SQL server 2005 i nowszych.

TABLESAMPLE zwróci dane z losowych stron zamiast losowych wierszy, a zatem deos nawet nie pobierze danych, których nie zwróci.

Na bardzo dużym stole testowałem

select top 1 percent * from [tablename] order by newid()
Zajęło to ponad 20 minut.
select * from [tablename] tablesample(1 percent)
Zajęło to 2 minuty.

Wydajność poprawi się również na mniejszych próbkach w TABLESAMPLE, podczas gdy nie z newid().

Proszę pamiętać, że nie jest to tak losowe jak metoda newid(), ale daje przyzwoite pobieranie próbek.

Patrz strona MSDN.

 65
Author: Patrick Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-08 21:26:33

Nevid () / order by będzie działać, ale będzie bardzo kosztowne dla dużych zestawów wyników, ponieważ musi wygenerować id dla każdego wiersza, a następnie je posortować.

TABLESAMPLE() jest dobre z punktu widzenia wydajności, ale otrzymasz grupowanie wyników(wszystkie wiersze na stronie zostaną zwrócone).

Aby uzyskać lepszą rzeczywistą próbkę losową, najlepszym sposobem jest losowe odfiltrowywanie wierszy. Znalazłem następujący przykład kodu w artykule SQL Server Books Onlineograniczanie zestawów wyników za pomocą TABLESAMPLE:

Jeśli naprawdę chcesz losową próbkę poszczególnych wierszy, zmodyfikuj zapytanie do losowo filtruj wiersze, zamiast za pomocą TABLESAMPLE. Na przykład następujące zapytanie używa nevid funkcja zwracająca około jednego procent rzędów Sprzedaż.Tabela SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

Kolumna SalesOrderID znajduje się w wyrażenie sumy kontrolnej tak, że Nevid () ocenia raz w wierszu do uzyskaj pobieranie próbek na na podstawie rzędu. Wyrażenie CAST(CHECKSUM (NEWID(), SalesOrderID) & 0x7fffffff jako float / CAST (0X7FFFFFFF AS int) losowa wartość zmiennoprzecinkowa między 0 a 1.

Kiedy uruchamiamy tabelę z 1,000,000 wierszy, oto moje wyniki:

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

Jeśli ujdzie ci to na sucho za pomocą TABLESAMPLE, zapewni Ci to najlepszą wydajność. W przeciwnym razie użyj metody nevid () / filter. nevid () / order by powinno być ostatecznością, jeśli masz duży zestaw wyników.

 35
Author: Rob Boek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-28 18:15:18

Wybieranie wierszy losowo z dużej tabeli w MSDN ma proste, dobrze artykułowane rozwiązanie, które rozwiązuje problemy związane z wydajnością na dużą skalę.

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10
 20
Author: Kyle McClellan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-05 22:23:02

Jeśli (w przeciwieństwie do OP) potrzebujesz określonej liczby rekordów (co utrudnia podejście sumy kontrolnej) i pragniesz bardziej losowej próbki niż TABLESAMPLE, a także chcesz lepszej prędkości niż suma kontrolna, możesz zrobić to z połączeniem metod TABLESAMPLE i nevid (), jak to:

DECLARE @sampleCount int = 50
SET STATISTICS TIME ON

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()

SET STATISTICS TIME OFF

W moim przypadku jest to najprostszy kompromis między przypadkowością (nie jest tak naprawdę, wiem) a szybkością. Odpowiednio zmienić procent tabeli (lub wierszy) - w im wyższy procent, tym bardziej losowa próbka, ale spodziewaj się liniowego spadku prędkości. (Zauważ, że TABLESAMPLE nie zaakceptuje zmiennej)

 9
Author: Oskar Austegard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-26 17:23:46

Po prostu Uporządkuj tabelę według losowej liczby i uzyskaj pierwsze 5000 wierszy za pomocą TOP.

SELECT TOP 5000 * FROM [Table] ORDER BY newid();

UPDATE

Po prostu próbowałem i newid() połączenie jest wystarczające - nie ma potrzeby wszystkich odlewów i całej matematyki.

 8
Author: Daniel Brückner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-11 16:31:11

TEN link ma ciekawe porównanie Orderby (nevid ()) i innych metod dla tabel z 1, 7 i 13 milionami wierszy.

Często, gdy w grupach dyskusyjnych zadawane są pytania o wybór losowych wierszy, proponuje się zapytanie nevid; jest to proste i działa bardzo dobrze dla małych tabel.

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()

Jednak zapytanie NEWID ma dużą wadę, gdy używasz go do dużych tabel. Klauzula ORDER BY powoduje skopiowanie wszystkich wierszy w tabeli do tempdb bazy danych, gdzie są sortowane. Powoduje to dwa problemy:

  1. operacja sortowania zwykle wiąże się z wysokimi kosztami. Sortowanie może zużywać dużo dysków I / O i może działać przez długi czas.
  2. w najgorszym przypadku, tempdb może zabraknąć miejsca. W w najlepszym przypadku program tempdb może zająć dużą ilość miejsca na dysku to nigdy nie zostanie odzyskane bez ręcznej komendy zmniejszania.

Potrzebny jest sposób na losowe wybieranie wierszy, które nie będą używane tempdb i nie będzie dużo wolniej, jak tabela staje się większa. Oto nowy pomysł, jak to zrobić:

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

Podstawową ideą tego zapytania jest to, że chcemy wygenerować losową liczbę od 0 do 99 dla każdego wiersza w tabeli, a następnie wybrać wszystkie wiersze, których liczba losowa jest mniejsza niż wartość określonego procentu. W tym przykładzie chcemy około 10 procent wierszy wybranych losowo; dlatego wybieramy wszystkie wiersze, których liczba losowa jest mniejsza niż 10.

Przeczytaj cały artykuł w MSDN.

 7
Author: RJardines,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-22 13:44:41

W MySQL możesz to zrobić:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
 4
Author: Jeff Ferland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-28 14:48:29

Jest to połączenie początkowego pomysłu seed i sumy kontrolnej, która wygląda na to, że da prawidłowo losowe wyniki bez kosztów nevid ():

SELECT TOP [number] 
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())
 3
Author: Nanki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-16 17:03:52

Spróbuj tego:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
 2
Author: Ravi Parashar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-06 13:28:52

Nie widziałem jeszcze tej odmiany w odpowiedziach. Miałem dodatkowe ograniczenie tam, gdzie potrzebowałem, biorąc pod uwagę początkowe ziarno, aby wybrać ten sam zestaw wierszy za każdym razem.

Dla MS SQL:

Przykład minimalny:

select top 10 percent *
from table_name
order by rand(checksum(*))

Znormalizowany czas wykonania: 1.00

Nevid () przykład:

select top 10 percent *
from table_name
order by newid()

Znormalizowany czas wykonania: 1.02

NewId() jest nieznacznie wolniejszy niż rand(checksum(*)), więc możesz nie chcieć go używać przeciwko dużym zestawom rekordów.

Wybór z inicjałem Seed:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */

Jeśli musisz wybrać ten sam zestaw, biorąc pod uwagę ziarno, to wydaje się działać.

 2
Author: klyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-07 17:33:01

To działa dla mnie:

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]
 0
Author: Deep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-06 20:57:50

Wygląda na to, że nevid() nie może być użyte w klauzuli where, więc to rozwiązanie wymaga wewnętrznego zapytania:

SELECT *
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
    FROM MyTable
) vw
WHERE Rnd % 100 < 10        --10%
 0
Author: Hai Phan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 22:17:37

Używałem go w subquery i zwrócił mi te same wiersze w subquery

 SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

Potem rozwiązałem z włączeniem zmiennej tabeli nadrzędnej w Gdzie

SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              Where Mytable.ID>0
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

Zwróć uwagę na conditionum where

 0
Author: VISHMAY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 12:40:18

Używany język przetwarzania po stronie serwera (np. PHP,. NET itp.) nie jest określony, ale jeśli jest to PHP, Pobierz wymaganą liczbę (lub wszystkie rekordy) i zamiast losować w zapytaniu użyj funkcji shuffle PHP. Nie wiem, czy. NET ma równoważną funkcję, ale jeśli tak, to Użyj tego, jeśli używasz. Net

ORDER BY RAND () może mieć sporą karę wydajności, w zależności od liczby rekordów.

 0
Author: SpacePhoenix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 06:05:42