¿Cómo obtener registros aleatoriamente de la base de datos de Oracle?

82

Necesito seleccionar filas al azar de una base de datos de Oracle.

Ej .: Supongamos una tabla con 100 filas, cómo puedo devolver aleatoriamente 20 de esos registros de las 100 filas completas.

Bhadra
fuente

Respuestas:

111
SELECT *
FROM   (
    SELECT *
    FROM   table
    ORDER BY DBMS_RANDOM.RANDOM)
WHERE  rownum < 21;
cagcowboy
fuente
1
Golpéame. Sin embargo, esto solo seleccionará las primeras 20 filas de la tabla y las ordenará al azar.
Nishant Sharma
10
Debe tener en cuenta que esta es una operación muy pesada en tablas grandes, porque primero asignará un número aleatorio a CADA fila, luego ordenará este valor y luego tomará algunos registros.
Roeland Van Heddegem
11
@NishantSharma, las filas son aleatorias, luego limitadas; su comentario no es correcto.
Simon MᶜKenzie
5
Este enfoque es MUY lento
Evan Kroske
1
@JonBetts, creo que la muestra es mucho más rápida y más eficiente en recursos: stackoverflow.com/a/9920431/156787
Evan Kroske
50

No se garantiza que SAMPLE () le proporcione exactamente 20 filas, pero podría ser adecuado (y puede funcionar significativamente mejor que una consulta completa + clasificación aleatoria para tablas grandes):

SELECT *
FROM   table SAMPLE(20);

Nota: 20aquí es un porcentaje aproximado, no el número de filas deseadas. En este caso, dado que tiene 100 filas, para obtener aproximadamente 20 filas solicita una muestra del 20%.

Jeffrey Kemp
fuente
1
La muestra es rápida pero no parece ser muy aleatoria. tienden a favorecerse los registros hacia la parte superior / comienzo de la tabla.
craigrs84
1
eso sucederá si detiene la consulta antes de que pase por toda la tabla.
Jeffrey Kemp
2
Lo siento, cometí un error, tu publicación está bien y los resultados se distribuyen por igual. Es cuando agrega "where rownum <= 20" en combinación con sample (20) que los datos comienzan a volverse menos aleatorios.
craigrs84
13
SELECT * FROM table SAMPLE(10) WHERE ROWNUM <= 20;

Esto es más eficiente ya que no necesita ordenar la tabla.

grokster
fuente
7
Detener la muestra después de 20 filas dará como resultado resultados no aleatorios (las filas que se encuentran antes en la tabla se devolverán con mucha más frecuencia que las posteriores). Además, no se garantiza que devuelva 20 filas.
Jeffrey Kemp
10
SELECT column FROM
( SELECT column, dbms_random.value FROM table ORDER BY 2 )
where rownum <= 20;
Bishan
fuente
4

Para seleccionar 20 filas al azar, creo que sería mejor seleccionar todas las filas ordenadas al azar y seleccionar las primeras 20 de ese conjunto.

Algo como:

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

Se utiliza mejor para tablas pequeñas para evitar seleccionar grandes cantidades de datos solo para descartar la mayor parte de ellos.

Nishant Sharma
fuente
3

En resumen, se introdujeron dos formas

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

La primera forma tiene la ventaja de 'CORRECCIÓN', lo que significa que nunca fallará en la obtención de resultados si realmente existe, mientras que en la segunda forma puede que no obtenga ningún resultado aunque tenga casos que satisfagan la condición de consulta, ya que la información se reduce durante el muestreo.

La segunda forma tiene la ventaja de "EFICIENTE", lo que significa que obtendrá resultados más rápido y le dará una carga ligera a su base de datos. Recibí una advertencia del DBA de que mi consulta usando la primera forma da cargas a la base de datos

¡Puede elegir una de dos formas según su interés!

Jinwu Seo
fuente
1

En el caso de tablas grandes, la forma estándar de ordenar por dbms_random.value no es efectiva porque necesita escanear la tabla completa y dbms_random.value es una función bastante lenta y requiere cambios de contexto. Para tales casos, existen 3 métodos adicionales:


1: samplecláusula de uso :

por ejemplo:

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

es decir, obtenga el 1% de todos los bloques, luego ordénelos al azar y devuelva solo 1 fila.


2: si tiene un índice / clave principal en la columna con distribución normal , puede obtener valores mínimos y máximos, obtener un valor aleatorio en este rango y obtener la primera fila con un valor mayor o igual que ese valor generado aleatoriamente.

Ejemplo:

--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: obtenga un bloque de tabla aleatorio, genere rowid y obtenga una fila de la tabla por este 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
      )
);
Sayan Malakshinov
fuente
0

A continuación, se explica cómo elegir una muestra aleatoria de cada grupo:

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;

No estoy seguro de cuán eficiente es, pero si tiene muchas categorías y subcategorías, esto parece funcionar bien.

SMerrill8
fuente