Tengo una tabla de SQL Server con aproximadamente 50,000 filas. Quiero seleccionar alrededor de 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 de esa tabla donde está la columna de números aleatorios < 0.1. Estoy buscando una manera más simple de hacerlo, en una sola declaración si es posible.
Este artículo sugiere usar la NEWID()
función. Eso parece prometedor, pero no puedo ver cómo podría seleccionar de manera confiable un cierto porcentaje de filas.
¿Alguien ha hecho esto antes? ¿Algunas ideas?
sql
sql-server
random
John M Gant
fuente
fuente
Respuestas:
En respuesta al comentario de "basura pura" sobre tablas grandes: podría hacerlo así para mejorar el rendimiento.
El costo de esto será la exploración de valores clave más el costo de unión, que en una tabla grande con una pequeña selección porcentual debería ser razonable.
fuente
[yourPk]
refiere? EDITAR: Nvm, lo descubrí ... Clave primaria. Durrrnewid()
. El costo estimado de E / S será muy alto y afectará el rendimiento.Dependiendo de sus necesidades,
TABLESAMPLE
obtendrá un rendimiento casi tan aleatorio y mejor. Esto está disponible en el servidor MS SQL 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 probé
tomó más de 20 minutos.
tomó 2 minutos
El rendimiento también mejorará en muestras más pequeñas,
TABLESAMPLE
mientras que no lo hará connewid()
.Tenga en cuenta que esto no es tan aleatorio como el
newid()
método, pero le dará una muestra decente.Ver la página de MSDN .
fuente
newid () / order by 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 obtener una muestra aleatoria verdadera con mejor rendimiento, la mejor manera es filtrar las filas al azar. Encontré el siguiente ejemplo de código en el artículo de los Libros en pantalla de SQL Server Limitar los conjuntos de resultados mediante TABLESAMPLE :
Cuando se ejecuta contra una tabla con 1,000,000 de filas, aquí están mis resultados:
Si puede salirse con la suya usando TABLESAMPLE, le dará el mejor rendimiento. De lo contrario, use el método newid () / filter. newid () / order by debería ser el último recurso si tiene un gran conjunto de resultados.
fuente
NewID()
se evalúa solo una vez, en lugar de por fila, lo que no me gusta ...La selección de filas al azar de una tabla grande en MSDN tiene una solución simple y bien articulada que aborda los problemas de rendimiento a gran escala.
fuente
RAND()
no devuelve el mismo valor para cada fila (lo que anularía laBINARY_CHECKSUM()
lógica). ¿Es porque se llama dentro de otra función en lugar de ser parte de la cláusula SELECT?rand()
o una combinación de lo anterior, pero me alejé de esta solución por ese motivo. Además, el número de resultados varió de 1 a 5, por lo que esto podría no ser aceptable en algunos escenarios.RAND()
devuelve el mismo valor para cada fila (razón por la cual esta solución es rápida). Sin embargo, las filas con sumas de verificación binarias que están muy juntas tienen un alto riesgo de generar resultados de suma de verificación similares, lo que causa agrupamiento cuandoRAND()
es pequeño. Por ejemplo,(ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100
==SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100
. Si sus datos padecen este problema, multipliqueBINARY_CHECKSUM
por 9923.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 mesas pequeñas.
Sin embargo, la consulta NEWID tiene un gran inconveniente cuando la usa 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 causa dos problemas:
Lo que necesita es una forma de seleccionar filas al azar que no usen tempdb y no se volverán mucho más lentas a medida que la tabla se agrande. Aquí hay una nueva idea sobre cómo hacer eso:
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 esas 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.
Por favor, lea el artículo completo en MSDN .
fuente
Si (a diferencia del OP) necesita un número específico de registros (lo que dificulta el enfoque CHECKSUM) y desea una muestra más aleatoria que TABLESAMPLE por sí mismo, y también desea una mejor velocidad que CHECKSUM, puede hacerlo con una fusión de Métodos TABLESAMPLE y NEWID (), como este:
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 (o filas) de TABLESAMPLE según corresponda: cuanto mayor sea el porcentaje, más aleatoria será la muestra, pero se espera una disminución lineal de la velocidad. (Tenga en cuenta que TABLESAMPLE no aceptará una variable)
fuente
Simplemente ordene la tabla por un número aleatorio y obtenga las primeras 5,000 filas usando
TOP
.ACTUALIZAR
Solo lo probé y una
newid()
llamada es suficiente: no es necesario tener todos los yesos y todas las matemáticas.fuente
Esta es una combinación de la idea inicial y una suma de verificación, que me parece que da resultados aleatorios sin el costo de NEWID ():
fuente
En MySQL puedes hacer esto:
fuente
Todavía no vi esta variación en las respuestas. Tenía una restricción adicional donde necesitaba, dada una semilla inicial, para seleccionar el mismo conjunto de filas cada vez.
Para MS SQL:
Ejemplo mínimo:
Tiempo de ejecución normalizado: 1.00
Ejemplo de NewId ():
Tiempo de ejecución normalizado: 1.02
NewId()
es insignificantemente más lento querand(checksum(*))
, por lo que es posible que no desee utilizarlo contra grandes conjuntos de registros.Selección con semilla inicial:
Si necesita seleccionar el mismo conjunto dado una semilla, esto parece funcionar.
fuente
Prueba esto:
fuente
Parece que newid () no se puede usar en la cláusula where, por lo que esta solución requiere una consulta interna:
fuente
Lo estaba usando en subconsulta y me devolvió las mismas filas en subconsulta
entonces resolví con incluir la variable de tabla principal en donde
Tenga en cuenta la condición donde
fuente
El lenguaje de procesamiento del lado del servidor en uso (por ejemplo, PHP, .net, etc.) no se especifica, 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 aleatoria de PHP. No sé si .net tiene una función equivalente, pero si la tiene, úsela si está usando .net
ORDER BY RAND () puede tener una gran penalización de rendimiento, dependiendo de cuántos registros estén involucrados.
fuente
Esto funciona para mi:
fuente
select top 10 percent from table_name order by rand()
, pero eso tampoco funciona porque rand () devuelve el mismo valor en todas las filas.