Jak losowo pobierać rekordy z bazy danych Oracle?


82

Muszę losowo wybrać wiersze z bazy danych Oracle.

Przykład: Załóżmy, że tabela zawiera 100 wierszy, jak mogę losowo zwrócić 20 z tych rekordów z całych 100 wierszy.

Odpowiedzi:


111
SELECT *
FROM   (
    SELECT *
    FROM   table
    ORDER BY DBMS_RANDOM.RANDOM)
WHERE  rownum < 21;

1
Pokonaj mnie. Spowoduje to jednak wybranie tylko pierwszych 20 wierszy z tabeli i wybranie ich losowo.
Nishant Sharma

10
Musisz mieć świadomość, że jest to bardzo ciężka operacja na dużych tabelach, ponieważ najpierw przypisze liczbę losową do KAŻDEGO wiersza, następnie sortuje według tej wartości, a następnie pobierze z niej jakieś rekordy.
Roeland Van Heddegem

11
@NishantSharma, wiersze są losowe, a następnie ograniczone - Twój komentarz nie jest poprawny.
Simon MᶜKenzie

6
To podejście jest BARDZO powolne
Evan Kroske

1
@JonBetts, myślę, że próbka jest znacznie szybsza i bardziej wydajna pod względem zasobów: stackoverflow.com/a/9920431/156787
Evan Kroske

50

SAMPLE () nie gwarantuje uzyskania dokładnie 20 wierszy, ale może być odpowiednie (i może działać znacznie lepiej niż pełne zapytanie + sortowanie losowe w przypadku dużych tabel):

SELECT *
FROM   table SAMPLE(20);

Uwaga: 20tutaj jest przybliżona wartość procentowa, a nie liczba żądanych wierszy. W tym przypadku, ponieważ masz 100 wierszy, aby uzyskać około 20 wierszy, należy poprosić o próbkę 20%.


1
próbka jest szybka, ale nie wydaje się być bardzo przypadkowa. rekordy na górze / początku tabeli są zazwyczaj preferowane.
craigrs84

1
stanie się tak, jeśli zatrzymasz zapytanie, zanim przejdzie przez całą tabelę.
Jeffrey Kemp

2
Przepraszam, że popełniłem błąd, Twój post jest w porządku, a wyniki są równomiernie rozłożone. Gdy dodasz „gdzie rownum <= 20” w połączeniu z próbką (20), dane zaczną być mniej losowe.
craigrs84

14
SELECT * FROM table SAMPLE(10) WHERE ROWNUM <= 20;

Jest to bardziej wydajne, ponieważ nie wymaga sortowania tabeli.


7
Zatrzymanie próby po 20 wierszach spowoduje nielosowe wyniki (wiersze znalezione wcześniej w tabeli będą zwracane znacznie częściej niż później). Nie gwarantuje to również zwrócenia 20 wierszy.
Jeffrey Kemp

10
SELECT column FROM
( SELECT column, dbms_random.value FROM table ORDER BY 2 )
where rownum <= 20;

4

Aby losowo wybrać 20 rzędów, myślę, że lepiej byłoby wybrać wiele z nich w losowej kolejności i wybrać pierwsze 20 z tego zestawu.

Coś jak:

Select *
  from (select *
          from table
         order by dbms_random.value) -- you can also use DBMS_RANDOM.RANDOM
 where rownum < 21;

Najlepiej używać do małych tabel, aby uniknąć wybierania dużych fragmentów danych tylko w celu odrzucenia większości z nich.


3

Podsumowując, wprowadzono dwa sposoby

1) using order by DBMS_RANDOM.VALUE clause
2) using sample([%]) function

Pierwszy sposób ma przewagę w „PRAWIDŁOWOŚCI”, co oznacza, że ​​nigdy nie uzyskasz wyniku, jeśli faktycznie istnieje, podczas gdy drugi sposób może nie uzyskać wyniku, nawet jeśli ma przypadki spełniające warunek zapytania, ponieważ informacje są zmniejszane podczas próbkowania.

Drugi sposób ma tę zaletę, że jest „WYDAJNY”, co oznacza, że ​​szybciej uzyskasz wyniki i nie obciążysz bazy danych. Otrzymałem ostrzeżenie od DBA, że moje zapytanie przy użyciu pierwszego sposobu powoduje obciążenie bazy danych

Możesz wybrać jeden z dwóch sposobów w zależności od Twoich zainteresowań!


1

W przypadku dużych tabel standardowy sposób sortowania według dbms_random.value nie jest skuteczny, ponieważ trzeba przeskanować całą tabelę, a dbms_random.value jest funkcją dość powolną i wymaga przełączania kontekstu. W takich przypadkach istnieją 3 dodatkowe metody:


1: sampleKlauzula użycia :

na przykład:

select *
from s1 sample block(1)
order by dbms_random.value
fetch first 1 rows only

tzn. pobierz 1% wszystkich bloków, a następnie posortuj je losowo i zwróć tylko 1 wiersz.


2: jeśli masz indeks / klucz podstawowy w kolumnie z rozkładem normalnym , możesz uzyskać wartości minimalne i maksymalne, uzyskać losową wartość z tego zakresu i otrzymać pierwszy wiersz z wartością większą lub równą tej losowo wygenerowanej wartości.

Przykład:

--big table with 1 mln rows with primary key on ID with normal distribution:
Create table s1(id primary key,padding) as 
   select level, rpad('x',100,'x')
   from dual 
   connect by level<=1e6;

select *
from s1 
where id>=(select 
              dbms_random.value(
                 (select min(id) from s1),
                 (select max(id) from s1) 
              )
           from dual)
order by id
fetch first 1 rows only;

3: pobierz losowy blok tabeli, wygeneruj identyfikator wiersza i pobierz wiersz z tabeli według tego identyfikatora wiersza :

select * 
from s1
where rowid = (
   select
      DBMS_ROWID.ROWID_CREATE (
         1, 
         objd,
         file#,
         block#,
         1) 
   from    
      (
      select/*+ rule */ file#,block#,objd
      from v$bh b
      where b.objd in (select o.data_object_id from user_objects o where object_name='S1' /* table_name */)
      order by dbms_random.value
      fetch first 1 rows only
      )
);

0

Oto jak wybrać losową próbkę z każdej grupy:

SELECT GROUPING_COLUMN, 
       MIN (COLUMN_NAME) KEEP (DENSE_RANK FIRST ORDER BY DBMS_RANDOM.VALUE) 
         AS RANDOM_SAMPLE
FROM TABLE_NAME
GROUP BY GROUPING_COLUMN
ORDER BY GROUPING_COLUMN;

Nie jestem pewien, jak wydajne jest to, ale jeśli masz wiele kategorii i podkategorii, wydaje się, że działa to dobrze.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.