2009-05-11 71 views
256

Tengo una tabla de SQL Server con aproximadamente 50,000 filas en ella. Quiero seleccionar unas 5,000 de esas filas al azar. He pensado en una forma complicada, crear una tabla temporal con una columna de "número aleatorio", copiar mi tabla en eso, recorrer la tabla temporal y actualizar cada fila con RAND(), y luego seleccionar desde esa tabla donde está la columna de número aleatorio < 0.1. Estoy buscando una manera más simple de hacerlo, en una sola declaración si es posible.Seleccionar n filas aleatorias de la tabla de SQL Server

This article sugieren utilizar la función NEWID(). Parece prometedor, pero no veo cómo podría seleccionar de manera confiable un determinado porcentaje de filas.

¿Alguien ha hecho esto antes? ¿Algunas ideas?

+2

MSDN tiene un buen artículo que cubre una gran cantidad de estos temas: [Seleccionar filas al azar de una mesa grande] (https://msdn.microsoft.com/en-us/library/cc441928.aspx) – KyleMit

+0

Posible duplicado de [¿Cómo solicitar una fila aleatoria en SQL?] (http://stackoverflow.com/questions/19412/how- to-request-a-random-row-in-sql) –

Respuesta

316
select top 10 percent * from [yourtable] order by newid() 

En respuesta al comentario de "basura pura" sobre tablas grandes: puede hacerlo así para mejorar el rendimiento.

select * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid()) 

El costo de esto será la exploración de los valores clave, más el costo, que en una mesa grande con una pequeña selección porcentaje debe ser razonable unirse.

+1

Me gusta este enfoque mucho mejor que usar el artículo al que hace referencia. – JoshBerke

+10

Siempre es bueno tener en cuenta que newid() no es un generador de números pseudoaleatorio realmente bueno, al menos no tan bueno como rand(). Pero si solo necesitas algunas muestras vagamente aleatorias y no te importan las cualidades matemáticas, será suficiente. De lo contrario, necesita: http://stackoverflow.com/questions/249301/simple-random-samples-from-a-mysql-database – user12861

+0

Um, lo siento si esto es obvio ... pero ¿a qué se refiere '[yourPk]'? EDITAR: Nvm, lo descubrió ... Clave principal. Durrr – Snailer

4

En MySQL puede hacer esto:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000; 
+3

Esto no funcionará. Como la declaración de selección es atómica, solo toma un número aleatorio y lo duplica para cada fila. Debería resembrarlo en cada fila para obligarlo a cambiar. –

+4

Mmm ... me encantan las diferencias entre vendedores. Select es atómico en MySQL, pero supongo que de una manera diferente. Esto funcionará en MySQL. –

8

orden justo de la mesa por un número aleatorio y obtener las primeras 5.000 filas usando TOP.

SELECT TOP 5000 * FROM [Table] ORDER BY newid(); 

ACTUALIZACIÓN

Sólo probé y es una llamada newid() sufficent - sin necesidad de que todos los moldes y todas las matemáticas.

+5

La razón por la cual se usan todos los moldes y todas las matemáticas es para un mejor rendimiento. – hkf

62

Dependiendo de sus necesidades, TABLESAMPLE obtendrá casi un rendimiento aleatorio y mejor. esto está disponible en MS SQL server 2005 y posterior.

TABLESAMPLE devolverá datos de páginas aleatorias en lugar de filas aleatorias y, por lo tanto, ni siquiera recuperará datos que no devolverá.

En una mesa muy grande que probé

select top 1 percent * from [tablename] order by newid() 

tomó más de 20 minutos.

select * from [tablename] tablesample(1 percent) 

tomaron 2 minutos.

El rendimiento también mejorará en muestras más pequeñas en TABLESAMPLE mientras que no con newid().

Tenga en cuenta que esto no es tan aleatorio como el método newid(), pero le dará un muestreo decente.

Ver el MSDN page.

+7

Según lo señalado por Rob Boek a continuación, tablesampling agrupa los resultados, y por lo tanto no es una buena forma de obtener un * pequeño * número de resultados aleatorios –

+0

Le importa la pregunta de cómo funciona esto: seleccione el 1 por ciento superior * del orden [tablename] por newid() ya que newid() no es una columna en [tablename]. ¿El servidor SQL se agrega internamente a la columna newid() en cada fila y luego hace una ordenación? – FrenkyB

+0

La muestra de tabla fue la mejor respuesta para mí ya que estaba haciendo una consulta compleja en una tabla muy grande. Sin dudas que fue notablemente rápido. Obtuve una variación en los registros de números devueltos, ya que ejecuté esto varias veces, pero todos ellos estaban dentro de un margen de error aceptable. – jessier3

33

newid()/ordenar por va a funcionar, pero será muy costoso para grandes conjuntos de resultados porque tiene que generar una identificación para cada fila, y luego ordenarlos.

TABLESAMPLE() es bueno desde el punto de vista del rendimiento, pero obtendrá una agrupación de resultados (se devolverán todas las filas de una página).

Para una mejor muestra aleatoria real, la mejor manera es filtrar las filas al azar. He encontrado el siguiente código de ejemplo en el artículo de SQL Server Libros en Limiting Results Sets by Using TABLESAMPLE:

Si realmente quieres una muestra aleatoria de filas individuales, modifique la consulta para filtro cabo filas al azar, en lugar de usando TABLESAMPLE . Por ejemplo, el siguiente consulta utiliza la función NEWID para volver aproximadamente un por ciento de las filas de la tabla Sales.SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail 
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) 
      /CAST (0x7fffffff AS int) 

La columna SalesOrderID está incluido en la expresión CHECKSUM para que NEWID() evalúa una vez por fila a para lograr el muestreo por fila. El REPARTO expresión (CHECKSUM (NEWID(), SalesOrderID) & 0x7fffffff AS flotador/ CAST (0x7fffffff AS int) se evalúa como un valor flotante aleatorio entre 0 y 1.

Cuando se ejecuta en una tabla con 1.000.000 de filas, aquí están mis resultados:..

SET STATISTICS TIME ON 
SET STATISTICS IO ON 

/* newid() 
    rows returned: 10000 
    logical reads: 3359 
    CPU time: 3312 ms 
    elapsed time = 3359 ms 
*/ 
SELECT TOP 1 PERCENT Number 
FROM Numbers 
ORDER BY newid() 

/* TABLESAMPLE 
    rows returned: 9269 (varies) 
    logical reads: 32 
    CPU time: 0 ms 
    elapsed time: 5 ms 
*/ 
SELECT Number 
FROM Numbers 
TABLESAMPLE (1 PERCENT) 

/* Filter 
    rows returned: 9994 (varies) 
    logical reads: 3359 
    CPU time: 641 ms 
    elapsed time: 627 ms 
*/  
SELECT Number 
FROM Numbers 
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
      /CAST (0x7fffffff AS int) 

SET STATISTICS IO OFF 
SET STATISTICS TIME OFF 

Si usted puede conseguir lejos con el uso TABLESAMPLE, se le dará el mejor rendimiento lo contrario usar la newid()/método de filtro newid()/orden por el debería ser último recurso si tiene un gran conjunto de resultados.

+0

También vi ese artículo y lo intenté en mi código, parece que 'NewID()' se evalúa solo una vez, en lugar de por fila, lo cual no me gusta ... –

0

Esto funciona para mí:

SELECT * FROM table_name 
ORDER BY RANDOM() 
LIMIT [number] 
+8

@ user537824, ¿lo intentó en SQL? ¿Servidor? RANDOM no es una función y LIMIT no es una palabra clave. La sintaxis de SQL Server para lo que está haciendo sería 'seleccionar el 10 por ciento superior de la orden table_name por rand()', pero eso tampoco funciona porque rand() devuelve el mismo valor en todas las filas. –

2

Prueba esto:

SELECT TOP 10 Field1, ..., FieldN 
FROM Table1 
ORDER BY NEWID() 
18

Selecting Rows Randomly from a Large Table en MSDN tiene una solución sencilla, bien articulado que se ocupa de los problemas de rendimiento a gran escala.

SELECT * FROM Table1 
    WHERE (ABS(CAST(
    (BINARY_CHECKSUM(*) * 
    RAND()) as int)) % 100) < 10 
+0

Muy interesante. Después de leer el artículo, realmente no entiendo por qué 'RAND()' no devuelve el mismo valor para cada fila (lo que vencería a la lógica 'BINARY_CHECKSUM()'). ¿Es porque se llama dentro de otra función en lugar de ser parte de la cláusula SELECT? –

+0

Esta consulta se ejecutó en una tabla con filas de 6MM en menos de un segundo. –

+1

He ejecutado esta consulta en una tabla con 35 entradas y seguí teniendo dos de ellas en el conjunto de resultados muy a menudo. Esto podría ser un problema con 'rand()' o una combinación de los anteriores, pero me alejé de esta solución por esa razón. Además, el número de resultados varió de 1 a 5, por lo que también podría no ser aceptable en algunos escenarios. – Oliver

8

Si (a diferencia de la OP) necesita un número específico de registros (lo que hace que el enfoque CHECKSUM difícil) y el deseo de una muestra más al azar que TABLESAMPLE ofrece por sí mismo, y también desea una mejor velocidad de suma de comprobación, que puede conformarse con una fusión de la NEWID métodos (TABLESAMPLE) y, de esta manera:

DECLARE @sampleCount int = 50 
SET STATISTICS TIME ON 

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT) 
ORDER BY NEWID() 

SET STATISTICS TIME OFF 

En mi caso este es el compromiso más directo entre la aleatoriedad (en realidad no es, lo sé) y la velocidad. Varíe el porcentaje (o las filas) TABLESAMPLE según corresponda: cuanto mayor sea el porcentaje, más aleatoria será la muestra, pero se espera un descenso lineal en la velocidad. (Tenga en cuenta que TABLESAMPLE no aceptará una variable)

2

Aún no veo esta variación en las respuestas. Tenía una restricción adicional donde necesitaba, dada una semilla inicial, seleccionar el mismo conjunto de filas cada vez.

Para MS SQL:

ejemplo mínima:

select top 10 percent * 
from table_name 
order by rand(checksum(*)) 

Normalizado tiempo de ejecución: 1,00

NEWID() ejemplo:

select top 10 percent * 
from table_name 
order by newid() 

tiempo de ejecución normalizado: 1,02

NewId() es insignificantemente más lento que rand(checksum(*)), por lo que es posible que no desee utilizarlo contra grandes conjuntos de registros.

Selección con el germen inicial:

declare @seed int 
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */ 

select top 10 percent * 
from table_name 
order by rand(checksum(*) % @seed) /* any other math function here */ 

Si es necesario seleccionar el mismo conjunto dado una semilla, esto parece funcionar.

+0

¿Hay alguna ventaja de usar @seed especial contra RAND()? – QMaster

+0

¿No estás 100% seguro de lo que estás preguntando, aclarando la mente? – klyd

+0

Absolutamente, Usaste el parámetro semilla y lo llenaste por parámetro de fecha, la función RAND() hizo lo mismo excepto que usaste el valor de tiempo completo, quiero saber si hay alguna ventaja de usar un parámetro creado útil como inicializar RAND() o no? – QMaster

5

Este enlace tiene una comparación interesante entre Orderby (NEWID()) y otros métodos para tablas con 1, 7 y 13 millones de filas.

A menudo, cuando se hacen preguntas sobre cómo seleccionar filas al azar en grupos de discusión, se propone la consulta NEWID; es simple y funciona muy bien para tablas pequeñas.

SELECT TOP 10 PERCENT * 
    FROM Table1 
    ORDER BY NEWID() 

Sin embargo, la consulta NEWID tiene un gran inconveniente cuando se utiliza para tablas grandes. La cláusula ORDER BY hace que todas las filas de la tabla se copien en la base de datos tempdb, donde se ordenan. Esto ocasiona dos problemas:

  1. La operación de clasificación generalmente tiene un alto costo asociado. La ordenación puede utilizar muchas E/S de disco y puede funcionar durante un tiempo prolongado.
  2. En el peor de los casos, tempdb puede quedarse sin espacio. En el mejor escenario de , tempdb puede ocupar una gran cantidad de espacio en disco que nunca será recuperado sin un comando de contracción manual.

Lo que necesita es una forma de seleccionar filas al azar que no utilizará tempdb y no se hará mucho más lenta a medida que la tabla se agranda. He aquí una nueva idea sobre cómo hacerlo:

SELECT * FROM Table1 
    WHERE (ABS(CAST(
    (BINARY_CHECKSUM(*) * 
    RAND()) as int)) % 100) < 10 

La idea básica detrás de esta consulta es que queremos generar un número aleatorio entre 0 y 99 para cada fila de la tabla, y luego elegir todos los filas cuyo número aleatorio es menor que el valor del porcentaje especificado. En este ejemplo, queremos aproximadamente el 10 por ciento de las filas seleccionadas al azar; por lo tanto, elegimos todas las filas cuyo número aleatorio es menor que 10.

Lea el artículo completo en MSDN.

+2

Hola Deumber, buen hallazgo, es posible que lo completes ya que es probable que se eliminen las respuestas del enlace. – bummi

+1

@bummi Lo cambié para evitar ser solo el enlace respuesta :) – QMaster

0

Parece newid() no se puede utilizar en la cláusula WHERE, por lo que esta solución requiere una consulta interna:

SELECT * 
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd 
    FROM MyTable 
) vw 
WHERE Rnd % 100 < 10  --10% 
3

Esta es una combinación de la idea inicial de la semilla y una suma de comprobación, que se parece a mí para dar resultados adecuadamente al azar sin el costo de NEWID():

SELECT TOP [number] 
FROM table_name 
ORDER BY RAND(CHECKSUM(*) * RAND()) 
0

lo estaba utilizando en la sub consulta y me regresado mismas filas en subconsulta

SELECT ID , 
      (SELECT TOP 1 
         ImageURL 
       FROM  SubTable 
       ORDER BY NEWID() 
      ) AS ImageURL, 
      GETUTCDATE() , 
      1 
    FROM Mytable 

entonces he resuelto con la inclusión de la variable tabla primaria en donde

SELECT ID , 
      (SELECT TOP 1 
         ImageURL 
       FROM  SubTable 
       Where Mytable.ID>0 
       ORDER BY NEWID() 
      ) AS ImageURL, 
      GETUTCDATE() , 
      1 
    FROM Mytable 

Nota el dónde condtition

Cuestiones relacionadas