Mam tabelę programu SQL Server z około 50 000 wierszy. Chcę wybrać losowo około 5000 z tych wierszy. Pomyślałem o skomplikowanym sposobie, tworząc tabelę tymczasową z kolumną „liczby losowej”, kopiując do niej moją tabelę, przeglądając tabelę tymczasową i aktualizując każdy wiersz RAND()
, a następnie wybierając z tej tabeli, w której kolumna liczb losowych < 0,1 Szukam prostszego sposobu, aby to zrobić, w jednym oświadczeniu, jeśli to możliwe.
W tym artykule sugeruje się użycie NEWID()
funkcji. To wygląda obiecująco, ale nie widzę, jak mógłbym wiarygodnie wybrać określony procent wierszy.
Czy ktoś to kiedyś robił? Jakieś pomysły?
sql
sql-server
random
John M. Gant
źródło
źródło
Odpowiedzi:
W odpowiedzi na komentarz „czysty kosz” dotyczący dużych tabel: możesz to zrobić w ten sposób, aby poprawić wydajność.
Kosztem tego będzie kluczowy skan wartości plus koszt złączenia, który na dużym stole z niewielkim wyborem procentowym powinien być rozsądny.
źródło
[yourPk]
dotyczy? EDYCJA: Nvm, wymyśliłem to ... Klucz podstawowy. Durrrnewid()
Oszacowanie sortowania Koszt we / wy będzie bardzo wysoki i wpłynie na wydajność.W zależności od potrzeb,
TABLESAMPLE
zapewni Ci prawie tak samo losową i lepszą wydajność. jest to dostępne na serwerze MS SQL Server 2005 i nowszych.TABLESAMPLE
zwróci dane z losowych stron zamiast losowych wierszy i dlatego deos nawet nie pobierze danych, których nie zwróci.Testowałem na bardzo dużym stole
zajęło ponad 20 minut.
zajęło 2 minuty.
Wydajność poprawi się również na mniejszych próbkach, podczas
TABLESAMPLE
gdy nie będzienewid()
.Pamiętaj, że nie jest to tak losowe jak
newid()
metoda, ale zapewni przyzwoite próbkowanie.Zobacz stronę MSDN .
źródło
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ład kodu 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 uda Ci się uniknąć użycia 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
NewID()
jest oceniany tylko raz, zamiast na wiersz, który mi się nie podoba ...Losowe wybieranie wierszy z dużej tabeli w MSDN ma proste, dobrze wyartykułowane rozwiązanie, które rozwiązuje problemy związane z wydajnością na dużą skalę.
źródło
RAND()
nie zwraca tej samej wartości dla każdego wiersza (co by pokonałoBINARY_CHECKSUM()
logikę). Czy dlatego, że jest wywoływany w innej funkcji, a nie jest częścią klauzuli SELECT?rand()
kombinacją powyższych lub ich kombinacja - ale z tego powodu odwróciłem się od tego rozwiązania. Również liczba wyników wahała się od 1 do 5, więc może to być również nie do zaakceptowania w niektórych scenariuszach.RAND()
zwraca tę samą wartość dla każdego wiersza (dlatego to rozwiązanie jest szybkie). Jednak wiersze z binarnymi sumami kontrolnymi, które są bardzo blisko siebie, są narażone na wysokie ryzyko generowania podobnych wyników sumy kontrolnej, powodując zbrylanie, gdyRAND()
jest małe. Np .(ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100
==SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100
. Jeśli dane cierpią z powodu tego problemu, pomnóż jeBINARY_CHECKSUM
przez 9923.Ten link ma interesujące porównanie między Orderby (NEWID ()) i innymi metodami dla tabel z 1, 7 i 13 milionami wierszy.
Często, gdy w grupach dyskusyjnych zadawane są pytania dotyczące wyboru losowych wierszy, proponuje się zapytanie NEWID; jest prosty i działa bardzo dobrze na małych stolikach.
Jednak zapytanie NEWID ma dużą wadę, gdy używasz go do dużych tabel. Klauzula ORDER BY powoduje, że wszystkie wiersze w tabeli są kopiowane do bazy danych tempdb, gdzie są sortowane. Powoduje to dwa problemy:
Potrzebny jest sposób losowego wybierania wierszy, które nie będą używać tempdb i nie będą spowalniały, gdy tabela będzie się powiększać. Oto nowy pomysł, jak to zrobić:
Podstawową ideą tego zapytania jest to, że chcemy wygenerować liczbę losową 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 losowo wybrać około 10 procent wierszy; dlatego wybieramy wszystkie wiersze, których liczba losowa jest mniejsza niż 10.
Przeczytaj cały artykuł w MSDN .
źródło
Jeśli potrzebujesz (w przeciwieństwie do OP) określonej liczby rekordów (co utrudnia podejście do CHECKSUM) i pragniesz bardziej losowej próbki niż sama TABLESAMPLE, a także chcesz większej prędkości niż CHECKSUM, możesz zadowolić się połączeniem Metody TABLESAMPLE i NEWID (), takie jak to:
W moim przypadku jest to najprostszy kompromis między losowością (nie wiem, tak naprawdę) a szybkością. Zmieniaj odpowiednio wartość procentową TABLESAMPLE (lub wierszy) - im wyższy odsetek, tym bardziej losowa próbka, ale spodziewaj się liniowego spadku prędkości. (Pamiętaj, że TABLESAMPLE nie zaakceptuje zmiennej)
źródło
Po prostu uporządkuj tabelę według losowej liczby i uzyskaj pierwsze 5000 wierszy za pomocą
TOP
.AKTUALIZACJA
Po prostu spróbowałem, a
newid()
wezwanie jest wystarczające - nie potrzeba wszystkich obsad i całej matematyki.źródło
Jest to połączenie początkowego pomysłu początkowego i sumy kontrolnej, która wydaje mi się dawać odpowiednio losowe wyniki bez kosztu NEWID ():
źródło
W MySQL możesz to zrobić:
źródło
Nie widziałem jeszcze tej zmiany w odpowiedziach. Miałem dodatkowe ograniczenie, gdzie potrzebował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
Spróbuj tego:
źródło
Wygląda na to, że newid () nie może być użyte w klauzuli where, więc to rozwiązanie wymaga wewnętrznego zapytania:
źródło
Użyłem go w podzapytaniu i zwróciło mi to samo wiersze w podzapytaniu
następnie rozwiązałem z włączeniem zmiennej tabeli nadrzędnej gdzie
Zwróć uwagę na to, gdzie warunki
źródło
Używany język przetwarzania po stronie serwera (np. PHP, .net itp.) Nie jest określony, ale jeśli jest to PHP, należy pobrać wymaganą liczbę (lub wszystkie rekordy) i zamiast losowo w zapytaniu użyć funkcji losowego PHP. Nie wiem, czy .net ma równoważną funkcję, ale jeśli tak, użyj jej, jeśli używasz .net
ORDER BY RAND () może mieć dość negatywny wpływ na wydajność, w zależności od liczby rekordów.
źródło
To działa dla mnie:
źródło
select top 10 percent from table_name order by rand()
, ale to również nie działa, ponieważ rand () zwraca tę samą wartość we wszystkich wierszach.