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:
SELECT *
FROM (
SELECT *
FROM table
ORDER BY DBMS_RANDOM.RANDOM)
WHERE rownum < 21;
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: 20
tutaj 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%.
SELECT * FROM table SAMPLE(10) WHERE ROWNUM <= 20;
Jest to bardziej wydajne, ponieważ nie wymaga sortowania tabeli.
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.
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ń!
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: sample
Klauzula 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
)
);
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.
stół
SELECT * FROM
(
SELECT column_name FROM table_name
ORDER BY dbms_random.value
)
WHERE rownum = 1;