Quiero una selección aleatoria de filas en PostgreSQL, probé esto:
select * from table where random() < 0.01;
Pero algunos otros recomiendan esto:
select * from table order by random() limit 1000;
Tengo una mesa muy grande con 500 millones de filas, quiero que sea rápida.
¿Qué enfoque es mejor? ¿Cuáles son las diferencias? ¿Cuál es la mejor manera de seleccionar filas aleatorias?
sql
performance
postgresql
random
nanounanue
fuente
fuente
Respuestas:
Dadas sus especificaciones (más información adicional en los comentarios),
La consulta a continuación no necesita una exploración secuencial de la tabla grande, solo una exploración de índice.
Primero, obtenga estimaciones para la consulta principal:
La única parte posiblemente costosa es la
count(*)
(para tablas enormes). Dadas las especificaciones anteriores, no lo necesita. Una estimación estará bien, disponible casi sin costo ( explicación detallada aquí ):Mientras
ct
no sea mucho más pequeño queid_span
, la consulta superará a otros enfoques.Genera números aleatorios en el
id
espacio. Tiene "pocos espacios", por lo tanto, agregue un 10% (suficiente para cubrir fácilmente los espacios en blanco) al número de filas para recuperar.Cada uno
id
se puede elegir varias veces por casualidad (aunque es muy poco probable con un gran espacio de identificación), por lo tanto, agrupe los números generados (o useDISTINCT
).Únete a la
id
s a la mesa grande. Esto debería ser muy rápido con el índice en su lugar.Finalmente, elimine los excedentes
id
que no hayan sido comidos por engaños y huecos. Cada fila tiene una oportunidad completamente igual de ser elegido.Version corta
Puedes simplificar esta consulta. El CTE en la consulta anterior es solo para fines educativos:
Refina con rCTE
Especialmente si no está tan seguro sobre las brechas y las estimaciones.
Podemos trabajar con un excedente más pequeño en la consulta base. Si hay demasiados espacios, por lo que no encontramos suficientes filas en la primera iteración, el rCTE continúa iterando con el término recursivo. Todavía necesitamos relativamente pocos espacios en el espacio de ID o la recursión puede agotarse antes de que se alcance el límite, o tenemos que comenzar con un búfer lo suficientemente grande que desafíe el propósito de optimizar el rendimiento.
Los duplicados son eliminados por el
UNION
en el rCTE.El exterior
LIMIT
hace que el CTE se detenga tan pronto como tengamos suficientes filas.Esta consulta está cuidadosamente redactada para usar el índice disponible, generar filas realmente aleatorias y no detenerse hasta que cumplamos el límite (a menos que la recursión se seque). Hay una serie de dificultades aquí si va a reescribirlo.
Envolver en función
Para uso repetido con parámetros variables:
Llamada:
Incluso podría hacer que este genérico funcione para cualquier tabla: tome el nombre de la columna PK y la tabla como tipo polimórfico y use
EXECUTE
... Pero eso está más allá del alcance de esta pregunta. Ver:Alternativa posible
SI sus requisitos permiten conjuntos idénticos para llamadas repetidas (y estamos hablando de llamadas repetidas), consideraría una vista materializada . Ejecute la consulta anterior una vez y escriba el resultado en una tabla. Los usuarios obtienen una selección cuasialeatoria a la velocidad del rayo. Actualice su selección aleatoria a intervalos o eventos de su elección.
Postgres 9.5 presenta
TABLESAMPLE SYSTEM (n)
Donde
n
es un porcentaje. El manual:El énfasis en negrita es mío. Es muy rápido , pero el resultado no es exactamente al azar . El manual nuevamente:
El número de filas devueltas puede variar enormemente. Para nuestro ejemplo, para obtener aproximadamente 1000 filas:
Relacionado:
O instale el módulo adicional tsm_system_rows para obtener exactamente el número de filas solicitadas (si hay suficientes) y permitir la sintaxis más conveniente:
Ver la respuesta de Evan para más detalles.
Pero eso todavía no es exactamente al azar.
fuente
JOIN bigtbl t
que es la abreviatura deJOIN bigtbl AS t
.t
es un alias de tabla parabigtbl
. Su propósito es acortar la sintaxis, pero no sería necesaria en este caso particular. Simplifiqué la consulta en mi respuesta y agregué una versión simple.Puede examinar y comparar el plan de ejecución de ambos utilizando
Una prueba rápida en una tabla grande 1 muestra que la
ORDER BY
primera ordena la tabla completa y luego selecciona los primeros 1000 elementos. Ordenar una tabla grande no solo lee esa tabla sino que también implica leer y escribir archivos temporales. Elwhere random() < 0.1
único escanea la tabla completa una vez.Para tablas grandes, esto podría no ser lo que desea, ya que incluso un escaneo completo de la tabla puede llevar mucho tiempo.
Una tercera propuesta sería
Éste detiene el escaneo de la tabla tan pronto como se han encontrado 1000 filas y, por lo tanto, regresa antes. Por supuesto, esto empantana un poco la aleatoriedad, pero tal vez esto sea lo suficientemente bueno en su caso.
Editar: además de estas consideraciones, puede consultar las preguntas ya hechas para esto. El uso de la consulta
[postgresql] random
devuelve bastantes resultados.Y un artículo vinculado de depez que describe varios enfoques más:
1 "grande" como en "la tabla completa no cabe en la memoria".
fuente
random() < 0.02
y luego barajar esa lista, entonceslimit 1000
! El tipo será menos costoso en unos pocos miles de filas (lol).orden postgresql por random (), seleccione filas en orden aleatorio:
orden postgresql por random () con un distintivo:
orden postgresql por límite aleatorio una fila:
fuente
select your_columns from your_table ORDER BY random() limit 1
tómese ~ 2 minutos para ejecutar en 45mil filasComenzando con PostgreSQL 9.5, hay una nueva sintaxis dedicada a obtener elementos aleatorios de una tabla:
Este ejemplo le dará un 5% de elementos de
mytable
.Ver más explicaciones en esta publicación de blog: http://www.postgresql.org/docs/current/static/sql-select.html
fuente
TABLESAMPLE SYSTEM_ROWS(400)
para obtener una muestra de 400 filas aleatorias. Debe habilitar la extensión integradatsm_system_rows
para usar esta declaración.El que tenga ORDER BY será el más lento.
select * from table where random() < 0.01;
va registro por registro y decide filtrarlo al azar o no. Esto va a serO(N)
porque solo necesita verificar cada registro una vez.select * from table order by random() limit 1000;
va a ordenar toda la mesa, luego elegir las primeras 1000. Aparte de cualquier magia vudú detrás de escena, el orden esO(N * log N)
.La desventaja de
random() < 0.01
esto es que obtendrá un número variable de registros de salida.Tenga en cuenta que hay una mejor manera de barajar un conjunto de datos que la ordenación aleatoria: The Fisher-Yates Shuffle , que se ejecuta
O(N)
. Sin embargo, implementar el shuffle en SQL parece un gran desafío.fuente
Aquí hay una decisión que me funciona. Supongo que es muy simple de entender y ejecutar.
fuente
ORDER BY random()
que funciona, pero podría no ser eficiente cuando se trabaja con una tabla grande.Si sabe cuántas filas desea, consulte
tsm_system_rows
.tsm_system_rows
Primero instala la extensión
Entonces tu consulta,
fuente
SYSTEM
método incorporado .tsm_system_rows
ytsm_system_time
. Hasta donde puedo ver, son prácticamente inútiles para cualquier cosa que no sea una selección absolutamente mínima de filas aleatorias. Le agradecería que pudiera echar un vistazo rápido y comentar sobre la validez o no de mi análisis.Si desea solo una fila, puede usar un
offset
derivado calculado decount
.fuente
Una variación de la vista materializada "Posible alternativa" esbozada por Erwin Brandstetter .
Digamos, por ejemplo, que no desea duplicados en los valores aleatorios que se devuelven. Por lo tanto, deberá establecer un valor booleano en la tabla primaria que contenga su conjunto de valores (no aleatorio).
Asumiendo que esta es la tabla de entrada:
Rellene la
ID_VALUES
tabla según sea necesario. Luego, como lo describe Erwin, cree una vista materializada que aleatorice laID_VALUES
tabla una vez:Tenga en cuenta que la vista materializada no contiene la columna utilizada, ya que rápidamente quedará desactualizada. La vista tampoco necesita contener otras columnas que puedan estar en el
id_values
tabla.Para obtener (y "consumir") valores aleatorios, use un ACTUALIZACIÓN-RETORNO activado
id_values
, seleccionandoid_values
desdeid_values_randomized
con una combinación y aplicando los criterios deseados para obtener solo las posibilidades relevantes. Por ejemplo:Cambie
LIMIT
según sea necesario: si solo necesita un valor aleatorio a la vez, cambieLIMIT
a1
.Con los índices adecuados
id_values
, creo que UPDATE-RETURNING debería ejecutarse muy rápidamente con poca carga. Devuelve valores aleatorios con una base de datos de ida y vuelta. Los criterios para las filas "elegibles" pueden ser tan complejos como sea necesario. Se pueden agregar nuevas filas a laid_values
tabla en cualquier momento, y la aplicación podrá acceder a ellas tan pronto como se actualice la vista materializada (que probablemente pueda ejecutarse en un momento de poca actividad). La creación y actualización de la vista materializada será lenta, pero solo debe ejecutarse cuando se agreguen nuevos ID a laid_values
tabla.fuente
Una lección de mi experiencia:
offset floor(random() * N) limit 1
No es más rápido queorder by random() limit 1
.Pensé que el
offset
enfoque sería más rápido porque debería ahorrar el tiempo de ordenar en Postgres. Resulta que no lo fue.fuente
Agregue una columna llamada
r
con tiposerial
. Índicer
.Supongamos que tenemos 200,000 filas, vamos a generar un número aleatorio
n
, donde 0 <n
<<= 200, 000.Seleccione filas con
r > n
, ordénelasASC
y seleccione la más pequeña.Código:
El código se explica por sí mismo. La subconsulta en el medio se usa para estimar rápidamente los recuentos de filas de la tabla desde https://stackoverflow.com/a/7945274/1271094 .
En el nivel de aplicación, debe ejecutar la instrucción nuevamente si
n
> el número de filas o necesita seleccionar varias filas.fuente
Sé que llego un poco tarde a la fiesta, pero acabo de encontrar esta increíble herramienta llamada pg_sample :
Intenté esto con una base de datos de 350M filas y fue realmente rápido, no sé sobre la aleatoriedad .
fuente