Tengo una consulta donde deseo que los registros resultantes se ordenen aleatoriamente. Utiliza un índice agrupado, por lo que si no incluyo uno order by
, es probable que devuelva registros en el orden de ese índice. ¿Cómo puedo asegurar un orden de fila aleatorio?
Entiendo que probablemente no sea "verdaderamente" aleatorio, seudoaleatorio es lo suficientemente bueno para mis necesidades.
sql-server
goric
fuente
fuente
CryptGenRandom
al final. dba.stackexchange.com/a/208069/3690La primera sugerencia de Pradeep Adiga
ORDER BY NEWID()
, está bien y es algo que he usado en el pasado por este motivo.Tenga cuidado con el uso
RAND()
: en muchos contextos, solo se ejecuta una vez por declaración, porORDER BY RAND()
lo que no tendrá ningún efecto (ya que obtiene el mismo resultado de RAND () para cada fila).Por ejemplo:
devuelve cada nombre de nuestra tabla de personas y un número "aleatorio", que es el mismo para cada fila. El número varía cada vez que ejecuta la consulta, pero es el mismo para cada fila cada vez.
Para mostrar que lo mismo es el caso con
RAND()
utilizado en unaORDER BY
cláusula, intento:Los resultados todavía están ordenados por el nombre que indica que el campo de clasificación anterior (el que se espera sea aleatorio) no tiene ningún efecto, por lo que presumiblemente siempre tiene el mismo valor.
NEWID()
Sin embargo, ordenar por funciona, porque si NEWID () no siempre se reevalúa, el propósito de los UUID se rompería al insertar muchas filas nuevas en un estado con identificadores únicos a medida que se introducen, por lo que:no ordenar los nombres "al azar".
Otros DBMS
Lo anterior es cierto para MSSQL (2005 y 2008 al menos, y si recuerdo bien 2000 también). Una función que devuelve un nuevo UUID debe evaluarse cada vez que en todos los DBMS NEWID () esté bajo MSSQL pero vale la pena verificar esto en la documentación y / o por sus propias pruebas. Es más probable que el comportamiento de otras funciones de resultados arbitrarios, como RAND (), varíe entre los DBMS, por lo que debe consultar nuevamente la documentación.
También he visto que se ignora el orden por los valores de UUID en algunos contextos, ya que el DB asume que el tipo no tiene un orden significativo. Si considera que este es el caso, convierta explícitamente el UUID a un tipo de cadena en la cláusula de pedido, o envuelva alguna otra función a su alrededor, como
CHECKSUM()
en SQL Server (puede haber una pequeña diferencia de rendimiento de esto también, ya que el pedido se realizará en un valor de 32 bits no uno de 128 bits, aunque si el beneficio de eso supera el costo de ejecuciónCHECKSUM()
por valor primero, lo dejaré para que lo pruebe).Nota al margen
Si desea un ordenamiento arbitrario pero algo repetible, ordene por un subconjunto relativamente no controlado de los datos en las filas mismas. Por ejemplo, uno o estos devolverán los nombres en un orden arbitrario pero repetible:
Los pedidos arbitrarios pero repetibles a menudo no son útiles en las aplicaciones, aunque pueden ser útiles en las pruebas si desea probar algún código en los resultados en una variedad de pedidos, pero desea poder repetir cada ejecución de la misma manera varias veces (para obtener el tiempo promedio resultados en varias ejecuciones, o probar que una solución que ha realizado en el código elimina un problema o ineficiencia previamente resaltada por un conjunto de resultados de entrada en particular, o simplemente para probar que su código es "estable", es decir, devuelve el mismo resultado cada vez si se envían los mismos datos en un orden dado).
Este truco también se puede usar para obtener resultados más arbitrarios de las funciones, que no permiten llamadas no deterministas como NEWID () dentro de su cuerpo. Nuevamente, esto no es algo que probablemente sea útil en el mundo real, pero podría ser útil si desea que una función devuelva algo aleatorio y "random-ish" es lo suficientemente bueno (pero tenga cuidado de recordar las reglas que determinan cuando se evalúan las funciones definidas por el usuario, es decir, generalmente solo una vez por fila, o sus resultados pueden no ser lo que espera / requiere).
Actuación
Como señala EBarr, puede haber problemas de rendimiento con cualquiera de los anteriores. Para más de unas pocas filas, está casi garantizado de ver el resultado en cola en tempdb antes de que se vuelva a leer el número solicitado de filas en el orden correcto, lo que significa que incluso si está buscando los 10 principales, puede encontrar un índice completo El escaneo (o peor, escaneo de tabla) ocurre junto con un gran bloque de escritura en tempdb. Por lo tanto, puede ser de vital importancia, como con la mayoría de las cosas, comparar con datos realistas antes de usar esto en la producción.
fuente
Esta es una vieja pregunta, pero falta un aspecto de la discusión, en mi opinión: RENDIMIENTO.
ORDER BY NewId()
Es la respuesta general. Cuando la fantasía de que alguien se añaden que realmente debe envolverNewID()
enCheckSum()
, ya sabes, para un rendimiento!El problema con este método es que todavía tiene garantizado un escaneo de índice completo y luego un tipo completo de datos. Si ha trabajado con un volumen de datos serio, esto puede volverse rápidamente costoso. Mire este plan de ejecución típico y observe cómo el ordenamiento toma el 96% de su tiempo ...
Para darle una idea de cómo se escala esto, le daré dos ejemplos de una base de datos con la que trabajo.
Order By newid()
de esta tabla genera 53.700 lecturas y lleva 16 segundos.La moraleja de la historia es que si tiene tablas grandes (piense en miles de millones de filas) o necesita ejecutar esta consulta con frecuencia, el
newid()
método se descompone. Entonces, ¿qué debe hacer un niño?Meet TABLESAMPLE ()
En SQL 2005
TABLESAMPLE
se creó una nueva capacidad llamada . Solo he visto un artículo discutiendo su uso ... debería haber más. Documentos de MSDN aquí . Primero un ejemplo:La idea detrás de la muestra de tabla es darle aproximadamente el tamaño de subconjunto que solicita. SQL numera cada página de datos y selecciona el X por ciento de esas páginas. El número real de filas que obtiene puede variar según lo que exista en las páginas seleccionadas.
Entonces, ¿cómo lo uso? Seleccione un tamaño de subconjunto que cubra más que la cantidad de filas que necesita, luego agregue un
Top()
. La idea es que puede hacer que su mesa descomunal parezca más pequeña antes del tipo costoso.Personalmente, lo he estado usando para limitar el tamaño de mi mesa. Entonces, en esa tabla de millones de filas,
top(20)...TABLESAMPLE(20 PERCENT)
la consulta cae a 5600 lecturas en 1600 ms. También hay unaREPEATABLE()
opción donde puede pasar una "Semilla" para la selección de página. Esto debería dar como resultado una selección de muestra estable.De todos modos, pensé que esto debería agregarse a la discusión. Espero que ayude a alguien.
fuente
TABLESAMPLE()
función de la cantidad de datos que tiene. No creo queTABLESAMPLE(x ROWS)
eso garantice que se devuelvan al menosx
filas porque la documentación dice: “El número real de filas que se devuelven puede variar significativamente. Si especifica un número pequeño, como por ejemplo 5, es posible que no reciba los resultados de la muestra.”- por lo que laROWS
sintaxis realmente todavía es sólo un enmascaradoPERCENT
dentro?Muchas tablas tienen una columna de identificación numérica indexada relativamente densa (pocos valores faltantes).
Esto nos permite determinar el rango de valores existentes y elegir filas usando valores de ID generados aleatoriamente en ese rango. Esto funciona mejor cuando el número de filas a devolver es relativamente pequeño y el rango de valores de ID está densamente poblado (por lo que la posibilidad de generar un valor faltante es lo suficientemente pequeña).
Para ilustrar, el siguiente código elige 100 usuarios aleatorios distintos de la tabla de usuarios de Desbordamiento de pila, que tiene 8,123,937 filas.
El primer paso es determinar el rango de valores de ID, una operación eficiente debido al índice:
El plan lee una fila de cada extremo del índice.
Ahora generamos 100 ID aleatorios distintos en el rango (con filas coincidentes en la tabla de usuarios) y devolvemos esas filas:
El plan muestra que en este caso se necesitaban 601 números aleatorios para encontrar 100 filas coincidentes. Es bastante rápido:
Pruébelo en el Stack Exchange Data Explorer.
fuente
Como expliqué en este artículo , para mezclar el conjunto de resultados de SQL, debe usar una llamada de función específica de la base de datos.
Entonces, suponiendo que tengamos la siguiente tabla de base de datos:
Y las siguientes filas en la
song
tabla:En SQL Server, debe usar la
NEWID
función, como se ilustra en el siguiente ejemplo:Al ejecutar la consulta SQL mencionada anteriormente en SQL Server, obtendremos el siguiente conjunto de resultados:
fuente