Как случайным образом получить записи из базы данных Oracle?


82

Мне нужно случайным образом выбрать строки из базы данных Oracle.

Пример: предположим, что таблица со 100 строками, как я могу случайным образом вернуть 20 таких записей из всех 100 строк.

Ответы:


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

1
Обыграй меня. Однако при этом будут выбраны только первые 20 строк из таблицы и они будут расположены в случайном порядке.
Нишант Шарма,

10
Вы должны знать, что это очень тяжелая операция с большими таблицами, потому что она сначала присваивает случайное число КАЖДОЙ строке, затем сортирует по этому значению, а затем берет из него несколько записей.
Руланд Ван Хеддегем,

11
@NishantSharma, строки рандомизированы, затем ограничены - ваш комментарий неверен .
Саймон MᶜKenzie

6
Этот подход ОЧЕНЬ медленный
Эван Кроске

1
@JonBetts, я думаю, что этот образец намного быстрее и ресурсоэффективнее: stackoverflow.com/a/9920431/156787
Эван Кроске

50

SAMPLE () не гарантирует получение ровно 20 строк, но может быть подходящим (и может работать значительно лучше, чем полный запрос + сортировка случайным образом для больших таблиц):

SELECT *
FROM   table SAMPLE(20);

Примечание: 20это приблизительный процент, а не желаемое количество строк. В этом случае, поскольку у вас 100 строк, чтобы получить примерно 20 строк, вы запрашиваете 20% выборку.


1
выборка выполняется быстро, но не кажется случайной. записи, расположенные вверху / в начале таблицы, имеют тенденцию к предпочтению.
craigrs84

1
это произойдет, если вы остановите запрос до того, как он пройдет через всю таблицу.
Джеффри Кемп

2
Извините, я допустил ошибку, ваш пост в порядке, и результаты распределены поровну. Когда вы добавляете «where rownum <= 20» в сочетании с sample (20), данные начинают становиться менее случайными.
craigrs84

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

Это более эффективно, так как не нужно сортировать таблицу.


7
Остановка выборки после 20 строк приведет к неслучайным результатам (строки, найденные ранее в таблице, будут возвращаться гораздо чаще, чем более поздние). Кроме того, не гарантируется возврат 20 строк.
Джеффри Кемп


4

Чтобы случайным образом выбрать 20 строк, я думаю, вам лучше выбрать их в случайном порядке и выбрать первые 20 из этого набора.

Что-то вроде:

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

Лучше всего использовать для небольших таблиц, чтобы не выбирать большие фрагменты данных только для того, чтобы отбросить большую их часть.


3

Таким образом, были представлены два способа

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

Первый способ имеет преимущество в «ПРАВИЛЬНОСТЬ», что означает, что вы никогда не потерпите неудачу в получении результата, если он действительно существует, в то время как второй способ может не получить результата, даже если у него есть случаи, удовлетворяющие условию запроса, поскольку информация уменьшается во время выборки.

Второй способ имеет преимущество в «ЭФФЕКТИВНОСТИ», что означает, что вы получите результат быстрее и уменьшите нагрузку на базу данных. Мне было дано предупреждение от администратора базы данных, что мой запрос, использующий первый способ, загружает базу данных.

Вы можете выбрать один из двух способов по вашему желанию!


1

В случае больших таблиц стандартный способ с сортировкой по dbms_random.value неэффективен, потому что вам нужно сканировать всю таблицу, а dbms_random.value - довольно медленная функция и требует переключения контекста. Для таких случаев есть 3 дополнительных метода:


1: Использование sampleпредложения:

например:

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

т.е. получить 1% всех блоков, затем отсортировать их случайным образом и вернуть только 1 строку.


2: если у вас есть индекс / первичный ключ в столбце с нормальным распределением , вы можете получить минимальные и максимальные значения, получить случайное значение в этом диапазоне и получить первую строку со значением, большим или равным этому случайно сгенерированному значению.

Пример:

--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: получить случайный блок таблицы, сгенерировать rowid и получить строку из таблицы по этому rowid :

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

Вот как выбрать случайную выборку из каждой группы:

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;

Я не уверен, насколько это эффективно, но если у вас много категорий и подкатегорий, это, кажется, отлично справляется со своей задачей.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.