Mam zapytanie, w którym chcę, aby wynikowe rekordy były uporządkowane losowo. Używa indeksu klastrowanego, więc jeśli go nie uwzględnię order by
, prawdopodobnie zwróci rekordy w kolejności tego indeksu. Jak mogę zapewnić losową kolejność wierszy?
Rozumiem, że prawdopodobnie nie będzie to „prawdziwie” losowy, pseudolosowy jest wystarczający dla moich potrzeb.
sql-server
goric
źródło
źródło
CryptGenRandom
na końcu. dba.stackexchange.com/a/208069/3690Pierwsza sugestia Pradeepa Adigi
ORDER BY NEWID()
jest w porządku i z tego powodu korzystałem w przeszłości.Zachowaj ostrożność przy użyciu
RAND()
- w wielu kontekstach jest wykonywany tylko raz na instrukcję, więc nieORDER BY RAND()
przyniesie żadnego efektu (ponieważ otrzymujesz taki sam wynik z RAND () dla każdego wiersza).Na przykład:
zwraca każde nazwisko z naszej tabeli osób i „losową” liczbę, która jest taka sama dla każdego wiersza. Liczba zmienia się przy każdym uruchomieniu zapytania, ale jest taka sama dla każdego wiersza za każdym razem.
Aby pokazać, że to samo ma miejsce w przypadku
RAND()
stosuje się wORDER BY
punkcie, próbuję:Wyniki są nadal uporządkowane według nazwy wskazującej, że wcześniejsze pole sortowania (to, które ma być losowe), nie działa, więc przypuszczalnie zawsze ma tę samą wartość.
Porządkowanie według
NEWID()
działa, ponieważ jeśli NEWID () nie zawsze był ponownie oceniany, cel UUIDs zostałby zepsuty podczas wstawiania wielu nowych wierszy w jednej pozycji z unikatowymi identyfikatorami, ponieważ klucz:nie zamówić nazw „losowo”.
Inne DBMS
Powyższe odnosi się do MSSQL (przynajmniej 2005 i 2008, i jeśli dobrze pamiętam również 2000). Funkcja zwracająca nowy UUID powinna być oceniana za każdym razem we wszystkich DBMS-ach NEWID () znajduje się w MSSQL, ale warto to zweryfikować w dokumentacji i / lub przez własne testy. Zachowanie innych funkcji o dowolnym wyniku, takich jak RAND (), jest bardziej prawdopodobne, że różnią się między DBMS, więc ponownie sprawdź dokumentację.
Widziałem też, że porządkowanie według wartości UUID jest ignorowane w niektórych kontekstach, ponieważ DB zakłada, że typ nie ma znaczącego uporządkowania. Jeśli okaże się, że jest to przypadek jawnie rzutuj identyfikator UUID na typ łańcucha w klauzuli kolejności lub owiń wokół niego jakąś inną funkcję, jak
CHECKSUM()
w SQL Server (może występować niewielka różnica w wydajności, ponieważ kolejność zostanie wykonana na wartości 32-bitowe, a nie 128-bitowe, ale czy korzyść z tego przewyższy koszt uruchomieniaCHECKSUM()
według wartości najpierw, zostawię cię do przetestowania).Dygresja
Jeśli chcesz dowolne, ale nieco powtarzalne porządkowanie, uporządkuj według względnie niekontrolowanego podzbioru danych w samych wierszach. Na przykład jedno lub drugie zwróci nazwy w dowolnej, ale powtarzalnej kolejności:
Arbitralne, ale powtarzalne porządki nie są często przydatne w aplikacjach, chociaż mogą być przydatne w testowaniu, jeśli chcesz przetestować jakiś kod na wynikach w różnych zamówieniach, ale chcesz móc powtarzać każde uruchomienie kilka razy w ten sam sposób (aby uzyskać średni czas wyniki z kilku przebiegów lub testowanie, czy poprawka dokonana w kodzie usuwa problem lub nieefektywność poprzednio zaznaczoną przez określony zestaw wyników wejściowych, lub po prostu do testowania, czy kod jest „stabilny”, czyli zwraca ten sam wynik za każdym razem jeśli wysłane te same dane w danej kolejności).
Tej sztuczki można także użyć do uzyskania bardziej dowolnych wyników z funkcji, które nie pozwalają na wywołania niedeterministyczne, takie jak NEWID () w ich ciele. Ponownie, nie jest to coś, co może być często przydatne w prawdziwym świecie, ale może się przydać, jeśli chcesz, aby funkcja zwróciła coś losowego, a „losowe ish” jest wystarczająco dobre (ale pamiętaj, aby pamiętać o regułach, które określają kiedy ewaluowane są funkcje zdefiniowane przez użytkownika, tj. zwykle tylko raz na wiersz lub wyniki mogą nie być zgodne z oczekiwaniami / wymaganiami).
Wydajność
Jak zauważa EBarr, mogą wystąpić problemy z wydajnością w każdym z powyższych. W przypadku więcej niż kilku wierszy masz prawie gwarancję, że dane wyjściowe są buforowane do tempdb, zanim żądana liczba wierszy zostanie odczytana w odpowiedniej kolejności, co oznacza, że nawet jeśli szukasz pierwszej 10, możesz znaleźć pełny indeks skanowanie (lub, co gorsza, skanowanie tabeli) odbywa się wraz z ogromnym blokiem zapisu do tempdb. Dlatego niezwykle ważne może być, podobnie jak w przypadku większości rzeczy, porównywanie realistycznych danych przed użyciem ich w produkcji.
źródło
To stare pytanie, ale moim zdaniem brakuje jednego aspektu dyskusji - WYDAJNOŚĆ.
ORDER BY NewId()
to ogólna odpowiedź. Kiedy ktoś ochotę dostać za dodają, że naprawdę należy owinąćNewID()
wCheckSum()
, wiesz, do wykonania!Problem z tą metodą polega na tym, że nadal masz zagwarantowane pełne skanowanie indeksu, a następnie kompletny rodzaj danych. Jeśli pracujesz z jakimkolwiek poważnym wolumenem danych, może to szybko stać się kosztowne. Spójrz na ten typowy plan wykonania i zwróć uwagę, że sortowanie zajmuje 96% twojego czasu ...
Aby dać ci wyobrażenie o tym, jak to się skaluje, podam dwa przykłady z bazy danych, z którą pracuję.
Order By newid()
tej tabeli generuje 53 700 odczytów i zajmuje 16 sekund.Morał tej historii jest taki, że jeśli masz duże tabele (pomyśl miliardy wierszy) lub musisz często uruchamiać to zapytanie,
newid()
metoda się psuje. Więc co robić chłopiec?Poznaj TABLESAMPLE ()
W SQL 2005 utworzono nową
TABLESAMPLE
funkcję o nazwie . Widziałem tylko jeden artykuł omawiający jego użycie ... powinno być ich więcej. Dokumenty MSDN tutaj . Najpierw przykład:Ideą próbki tabeli jest podanie przybliżonego rozmiaru podzbioru, o który prosisz. SQL numeruje każdą stronę danych i wybiera X procent tych stron. Rzeczywista liczba odzyskanych wierszy może się różnić w zależności od tego, co istnieje na wybranych stronach.
Jak mam z tego korzystać? Wybierz rozmiar podzestawu, który przekracza liczbę potrzebnych wierszy, a następnie dodaj
Top()
. Chodzi o to, że możesz sprawić, by twój gigantyczny stół wydawał się mniejszy przed kosztownym sortowaniem.Osobiście używałem go, aby w efekcie ograniczyć rozmiar mojego stołu. Tak więc w tabeli milionów wierszy wykonanie
top(20)...TABLESAMPLE(20 PERCENT)
zapytania spada do 5600 odczytów w 1600ms. Istnieje równieżREPEATABLE()
opcja, w której możesz przekazać „Ziarno” do wyboru strony. Powinno to doprowadzić do stabilnego doboru próbki.W każdym razie pomyślałem, że należy to dodać do dyskusji. Mam nadzieję, że to komuś pomoże.
źródło
TABLESAMPLE()
na podstawie ilości posiadanych danych. Nie sądzę,TABLESAMPLE(x ROWS)
że zapewni to nawet zwrot co najmniejx
wierszy, ponieważ dokumentacja mówi „Rzeczywista liczba zwracanych wierszy może się znacznie różnić. Jeśli określisz małą liczbę, na przykład 5, możesz nie otrzymać wyników w próbce. ”- czy więcROWS
składnia nadal jest po prostu zamaskowanaPERCENT
?Wiele tabel ma stosunkowo gęstą (kilka brakujących wartości) indeksowaną kolumnę z numerycznym identyfikatorem.
To pozwala nam określić zakres istniejących wartości i wybrać wiersze przy użyciu losowo generowanych wartości ID w tym zakresie. Działa to najlepiej, gdy liczba zwracanych wierszy jest stosunkowo niewielka, a zakres wartości ID jest gęsto zapełniany (więc szansa na wygenerowanie brakującej wartości jest wystarczająco mała).
Aby to zilustrować, poniższy kod wybiera 100 różnych losowych użytkowników z tabeli przepełnienia stosu użytkowników, która ma 8 123 937 wierszy.
Pierwszym krokiem jest określenie zakresu wartości ID, wydajna operacja dzięki indeksowi:
Plan odczytuje jeden wiersz z każdego końca indeksu.
Teraz generujemy 100 różnych losowych identyfikatorów w zakresie (z pasującymi wierszami w tabeli użytkowników) i zwracamy te wiersze:
Plan pokazuje, że w tym przypadku potrzebnych było 601 liczb losowych, aby znaleźć 100 pasujących wierszy. To jest dość szybkie:
Wypróbuj go w Eksploratorze stosów wymiany danych.
źródło
Jak wyjaśniłem w tym artykule , aby przetasować zestaw wyników SQL, musisz użyć wywołania funkcji specyficznej dla bazy danych.
Zakładając, że mamy następującą tabelę bazy danych:
I następujące wiersze w
song
tabeli:W SQL Server musisz użyć
NEWID
funkcji, jak pokazano w poniższym przykładzie:Podczas uruchamiania wspomnianego zapytania SQL na SQL Server otrzymamy następujący zestaw wyników:
źródło