Seleccionar n filas aleatorias de la tabla SQL Server


Tengo una tabla SQL Server con unas 50.000 filas. Quiero seleccionar alrededor de 5.000 de esas filas al azar. He pensado en una forma complicada, creando una tabla temporal con una columna de" número aleatorio", copiando mi tabla en esa, recorriendo la tabla temporal y actualizando cada fila con RAND(), y luego seleccionando de esa tabla donde la columna de número aleatorio

Este artículo sugiere usar la función NEWID(). Eso parece prometedor, pero no puedo ver cómo podría seleccionar confiablemente un cierto porcentaje de filas.

¿Alguien ha hecho esto antes? Alguna idea?

Author: Peter O., 2009-05-11

15 answers

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

En respuesta al comentario "pure trash" sobre tablas grandes: podría 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á el escaneo clave de los valores más el costo de unión, que en una tabla grande con una selección de porcentaje pequeño debería ser razonable.

 335
Author: Ralph Shillington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-04 21:11:18

Dependiendo de sus necesidades, TABLESAMPLE le dará un rendimiento casi igual de aleatorio y mejor. esto está disponible en MS SQL server 2005 y versiones posteriores.

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

En una mesa muy grande probé

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

Tomó más de 20 minutos.

select * from [tablename] tablesample(1 percent)

Tomó 2 minutos.

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

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

Ver la página MSDN.

 65
Author: Patrick Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-08 21:26:33

Newid()/order by funcionará, pero será muy caro para conjuntos de resultados grandes porque tiene que generar un id para cada fila, y luego ordenarlas.

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

Para un mejor rendimiento de la muestra aleatoria verdadera, la mejor manera es filtrar las filas al azar. Encontré el siguiente ejemplo de código en el artículo de SQL Server Books Online Limitar los Conjuntos de Resultados mediante el uso de TABLAS:

Si realmente quieres una muestra aleatoria de filas individuales, modifique su consulta a filtrar filas aleatoriamente, en lugar de usando TABLES-MUESTRA. Por ejemplo, el la siguiente consulta utiliza el NEWID función para devolver aproximadamente uno porcentaje de las filas de la Venta.Tabla del detalle del pedido:

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

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

Cuando se ejecuta contra una tabla con 1.000.000 de filas, estos son 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 puede salirse con la suya usando TABLES-MUESTRA, le dará el mejor rendimiento. De lo contrario, utilice el método newid()/filter. newid () / order by debería ser el último recurso si tienes un conjunto de resultados grande.

 35
Author: Rob Boek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-28 18:15:18

Seleccionar filas Aleatoriamente de una Tabla grande en MSDN tiene una solución simple y bien articulada que aborda las preocupaciones de rendimiento a gran escala.

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10
 20
Author: Kyle McClellan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-05 22:23:02

Si usted (a diferencia del OP) necesita un número específico de registros (lo que dificulta el enfoque de SUMA de COMPROBACIÓN) y desea una muestra más aleatoria que la que proporciona TABLES-SAMPLE por sí mismo, y también desea una mejor velocidad que CHECKSUM, puede conformarse con una fusión de los métodos TABLES-SAMPLE y NEWID (), como esto:

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 (no es realmente, lo sé) y la velocidad. Varíe el porcentaje de la muestra de TABLAS (o filas) según corresponda-el mayor es el porcentaje, más aleatoria es la muestra, pero esperar una caída lineal en la velocidad. (Tenga en cuenta que TABLES-MUESTRA no aceptará una variable)

 9
Author: Oskar Austegard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-26 17:23:46

Simplemente ordene la tabla por un número aleatorio y obtenga las primeras 5.000 filas usando TOP.

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

UPDATE

Acaba de intentarlo y una llamada newid() es suficiente - no hay necesidad de todos los moldes y todas las matemáticas.

 8
Author: Daniel Brückner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-11 16:31:11

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 aleatorias 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 usa para tablas grandes. La cláusula ORDER BY hace que todas las filas de la tabla se copien en tempdb base de datos, donde se ordenan. Esto causa dos problemas:

  1. La operación de clasificación generalmente tiene un alto costo asociado con ella. La ordenación puede utilizar una gran cantidad de E/S de disco y puede ejecutarse durante mucho tiempo.
  2. En el peor de los casos, tempdb puede quedarse sin espacio. En el en el mejor de los casos, tempdb puede ocupar una gran cantidad de espacio en disco eso nunca será reclamado sin un comando manual de encogimiento.

Lo que necesita es una forma de seleccionar filas aleatoriamente que no usarán tempdb y no será mucho más lento a medida que la tabla se hace más grande. Aquí hay una nueva idea sobre cómo hacer eso:

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 todas aquellas filas cuyo número aleatorio sea menor que el valor del porcentaje especificado. En este ejemplo, queremos aproximadamente el 10 por ciento de las filas seleccionadas aleatoriamente; por lo tanto, elegimos todas las filas cuyo número aleatorio es menor que 10.

Por favor, lea el artículo completo en MSDN.

 7
Author: RJardines,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-22 13:44:41

En MySQL puedes hacer esto:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
 4
Author: Jeff Ferland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-28 14:48:29

Esta es una combinación de la idea inicial de semilla y una suma de verificación, que me parece dar resultados aleatorios correctamente sin el costo de NEWID ():

SELECT TOP [number] 
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())
 3
Author: Nanki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-16 17:03:52

Prueba esto:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
 2
Author: Ravi Parashar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-06 13:28:52

Todavía no he visto esta variación en las respuestas. Tenía una restricción adicional en la que necesitaba, dada una semilla inicial, seleccionar el mismo conjunto de filas cada vez.

Para MS SQL:

Ejemplo mínimo:

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

Tiempo de ejecución normalizado: 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 usarlo contra grandes conjuntos de registros.

Selección con Inicial Semilla:

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 necesita seleccionar el mismo conjunto dado una semilla, esto parece funcionar.

 2
Author: klyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-07 17:33:01

Esto funciona para mí:

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]
 0
Author: Deep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-06 20:57:50

Parece que newid () no se puede usar 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%
 0
Author: Hai Phan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-07 22:17:37

Lo estaba usando en subconsulta y me devolvió las mismas filas en subconsulta

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

Luego resolví con la inclusión de la variable de la tabla principal en donde

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

Tenga en cuenta la condtición donde

 0
Author: VISHMAY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-10 12:40:18

El lenguaje de procesamiento del lado del servidor en uso (por ejemplo, PHP,. net, etc.) no está especificado, pero si es PHP, tome el número requerido (o todos los registros) y en lugar de aleatorizar en la consulta use la función shuffle de PHP. No sé si .net tiene una función equivalente, pero si lo hace, entonces el uso que si usted está utilizando .net

ORDER BY RAND() puede tener una penalización de rendimiento, dependiendo de cuántos registros estén involucrados.

 0
Author: SpacePhoenix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 06:05:42