¿Cómo extender esta solución a una combinación? Cuando lo uso SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;, siempre obtengo la misma fila.
Helmut Grohne
¿Es posible sembrar el número aleatorio? Por ejemplo, Libro del día sembrado con unix epoc para hoy al mediodía, por lo que muestra el mismo libro todo el día, incluso si la consulta se ejecuta varias veces. Sí, sé que el almacenamiento en caché es más eficiente para este caso de uso, solo un ejemplo.
Las siguientes soluciones son mucho más rápidas que las de anktastic (el recuento (*) cuesta mucho, pero si puede almacenarlo en caché, la diferencia no debería ser tan grande), que en sí mismo es mucho más rápido que el "orden aleatorio ()" cuando tienes una gran cantidad de filas, aunque tienen algunos inconvenientes.
Si sus rowids están bastante empaquetados (es decir, pocas eliminaciones), puede hacer lo siguiente (usar en (select max(rowid) from foo)+1lugar de max(rowid)+1ofrece un mejor rendimiento, como se explica en los comentarios):
select*from foo where rowid =(abs(random())%(select(select max(rowid)from foo)+1));
Si tiene huecos, a veces intentará seleccionar un ID de fila inexistente y la selección devolverá un conjunto de resultados vacío. Si esto no es aceptable, puede proporcionar un valor predeterminado como este:
Esta segunda solución no es perfecta: la distribución de probabilidad es más alta en la última fila (la que tiene el ID de fila más alto), pero si a menudo agrega cosas a la tabla, se convertirá en un objetivo móvil y la distribución de probabilidades debería ser mucho mejor.
Otra solución más, si a menudo selecciona elementos aleatorios de una tabla con muchos huecos, es posible que desee crear una tabla que contenga las filas de la tabla original ordenadas en orden aleatorio:
createtable random_foo(foo_id);
Luego, periódicamente, vuelva a llenar la tabla random_foo
deletefrom random_foo;insertinto random_foo select id from foo;
Y para seleccionar una fila aleatoria, puede usar mi primer método (aquí no hay agujeros). Por supuesto, este último método tiene algunos problemas de concurrencia, pero la reconstrucción de random_foo es una operación de mantenimiento que no es probable que suceda con mucha frecuencia.
Sin embargo, otra forma más, que encontré recientemente en una lista de correo , es poner un disparador en eliminar para mover la fila con el ID de fila más grande a la fila eliminada actual, de modo que no queden huecos.
Por último, tenga en cuenta que el comportamiento de rowid y un autoincrement de clave primaria entera no es idéntico (con rowid, cuando se inserta una nueva fila, se elige max (rowid) +1, donde es higest-value-ever-seen + 1 para una clave primaria), por lo que la última solución no funcionará con un autoincremento en random_foo, pero los otros métodos sí.
Como acabo de ver en una lista de correo, en lugar de usar el método alternativo (método 2), puedes usar rowid> = [aleatorio] en lugar de =, pero en realidad es muy lento en comparación con el método 2.
Suzanne Dupéron
3
Esta es una respuesta genial; sin embargo, tiene un problema. SELECT max(rowid) + 1será una consulta lenta; requiere un escaneo completo de la tabla. sqlite solo optimiza la consulta SELECT max(rowid). Por lo tanto, esta respuesta se mejoraría: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)); Vea esto para obtener más información: sqlite.1065341.n5.nabble.com/…
dasl
19
Necesita poner "orden por RANDOM ()" en su consulta.
Ejemplo:
select*from quest orderby RANDOM();
Veamos un ejemplo completo
Crea una tabla:
CREATETABLE quest (
id INTEGER PRIMARYKEY AUTOINCREMENT,
quest TEXT NOTNULL,
resp_id INTEGER NOTNULL);
Si bien las respuestas de solo código no están prohibidas, comprenda que esta es una comunidad de preguntas y respuestas, en lugar de una de colaboración colectiva, y que, por lo general, si el OP entendiera el código que se publica como una respuesta, habría surgido con una solución similar por su cuenta, y no habría publicado una pregunta en primer lugar. Como tal, proporcione contexto a su respuesta y / o código explicando cómo y / o por qué funciona.
XenoRo
2
Prefiero esta solución, ya que me permite buscar n líneas. En mi caso, necesitaba 100 muestras aleatorias de la base de datos: ORDER BY RANDOM () combinado con LIMIT 100 hace exactamente eso.
lunes
17
Qué pasa:
SELECT COUNT(*)AS n FROM foo;
luego elija un número aleatorio m en [0, n) y
SELECT*FROM foo LIMIT 1 OFFSET m;
Incluso puede guardar el primer número ( n ) en algún lugar y actualizarlo solo cuando cambie el recuento de la base de datos. De esa manera, no tendrá que hacer SELECT COUNT cada vez.
Ese es un buen método rápido. No se generaliza muy bien para seleccionar más de 1 fila, pero el OP solo pidió 1, así que supongo que está bien.
Ken Williams
Una cosa curiosa a tener en cuenta es que el tiempo necesario para encontrar OFFSETparece aumentar según el tamaño del desplazamiento: la fila 2 es rápida, la fila 2 millones lleva un tiempo, incluso cuando todos los datos en el son de tamaño fijo y debería poder buscarlo directamente. Al menos, eso es lo que parece en SQLite 3.7.13.
Ken Williams
@KenWilliams Prácticamente todas las bases de datos tienen el mismo problema con `` OFFSET ''. Es una forma muy ineficiente de consultar una base de datos porque necesita leer tantas filas aunque solo devolverá 1.
Jonathan Allen
1
Sin embargo, tenga en cuenta que estaba hablando de / tamaño fijo / registros: debería ser fácil escanear directamente al byte correcto en los datos ( sin leer tantas filas), pero tendrían que implementar la optimización explícitamente.
Ken Williams
@KenWilliams: no hay registros de tamaño fijo en SQLite, se escribe dinámicamente y los datos no tienen que coincidir con las afinidades declaradas ( sqlite.org/fileformat2.html#section_2_1 ). Todo se almacena en páginas de árbol b, por lo que de cualquier manera tiene que hacer al menos una búsqueda de árbol b hacia la hoja. Para lograr esto de manera eficiente, necesitaría almacenar el tamaño del subárbol junto con cada puntero secundario. Sería demasiado de una sobrecarga de poco beneficio, ya que todavía no será capaz de optimizar el desplazamiento de uniones, por fin, etc ... (y sin orden por el orden es indefinido.)
Esta solución también funciona para índices con espacios, porque aleatorizamos un desplazamiento en un rango [0, cuenta). MAXse utiliza para manejar un caso con mesa vacía.
Aquí hay resultados de pruebas simples en una tabla con 16k filas:
sqlite>.timer on
sqlite>select count(*)from payment;16049
Run Time: real 0.000user0.000140 sys 0.000117
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);14746
Run Time: real 0.002user0.000899 sys 0.000132
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);12486
Run Time: real 0.001user0.000952 sys 0.000103
sqlite>select payment_id from payment orderby random() limit 1;3134
Run Time: real 0.015user0.014022 sys 0.000309
sqlite>select payment_id from payment orderby random() limit 1;9407
Run Time: real 0.018user0.013757 sys 0.000208
Buen intento, pero no creo que esto funcione. ¿Qué sucede si se eliminó una fila con rowId = 5, pero rowIds 1,2,3,4,6,7,8,9,10 todavía existe? Entonces, si el rowId aleatorio elegido es 5, esta consulta no devolverá nada.
Respuestas:
Eche un vistazo a Seleccionar una fila aleatoria de una tabla SQLite
fuente
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
, siempre obtengo la misma fila.Las siguientes soluciones son mucho más rápidas que las de anktastic (el recuento (*) cuesta mucho, pero si puede almacenarlo en caché, la diferencia no debería ser tan grande), que en sí mismo es mucho más rápido que el "orden aleatorio ()" cuando tienes una gran cantidad de filas, aunque tienen algunos inconvenientes.
Si sus rowids están bastante empaquetados (es decir, pocas eliminaciones), puede hacer lo siguiente (usar en
(select max(rowid) from foo)+1
lugar demax(rowid)+1
ofrece un mejor rendimiento, como se explica en los comentarios):Si tiene huecos, a veces intentará seleccionar un ID de fila inexistente y la selección devolverá un conjunto de resultados vacío. Si esto no es aceptable, puede proporcionar un valor predeterminado como este:
Esta segunda solución no es perfecta: la distribución de probabilidad es más alta en la última fila (la que tiene el ID de fila más alto), pero si a menudo agrega cosas a la tabla, se convertirá en un objetivo móvil y la distribución de probabilidades debería ser mucho mejor.
Otra solución más, si a menudo selecciona elementos aleatorios de una tabla con muchos huecos, es posible que desee crear una tabla que contenga las filas de la tabla original ordenadas en orden aleatorio:
Luego, periódicamente, vuelva a llenar la tabla random_foo
Y para seleccionar una fila aleatoria, puede usar mi primer método (aquí no hay agujeros). Por supuesto, este último método tiene algunos problemas de concurrencia, pero la reconstrucción de random_foo es una operación de mantenimiento que no es probable que suceda con mucha frecuencia.
Sin embargo, otra forma más, que encontré recientemente en una lista de correo , es poner un disparador en eliminar para mover la fila con el ID de fila más grande a la fila eliminada actual, de modo que no queden huecos.
Por último, tenga en cuenta que el comportamiento de rowid y un autoincrement de clave primaria entera no es idéntico (con rowid, cuando se inserta una nueva fila, se elige max (rowid) +1, donde es higest-value-ever-seen + 1 para una clave primaria), por lo que la última solución no funcionará con un autoincremento en random_foo, pero los otros métodos sí.
fuente
SELECT max(rowid) + 1
será una consulta lenta; requiere un escaneo completo de la tabla. sqlite solo optimiza la consultaSELECT max(rowid)
. Por lo tanto, esta respuesta se mejoraría:select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Vea esto para obtener más información: sqlite.1065341.n5.nabble.com/…Necesita poner "orden por RANDOM ()" en su consulta.
Ejemplo:
Veamos un ejemplo completo
Insertando algunos valores:
Una selección predeterminada:
Un selecto aleatorio:
* Cada vez que seleccione, el orden será diferente.Si desea devolver solo una fila
* Cada vez que selecciones, la devolución será diferente.fuente
Qué pasa:
luego elija un número aleatorio m en [0, n) y
Incluso puede guardar el primer número ( n ) en algún lugar y actualizarlo solo cuando cambie el recuento de la base de datos. De esa manera, no tendrá que hacer SELECT COUNT cada vez.
fuente
OFFSET
parece aumentar según el tamaño del desplazamiento: la fila 2 es rápida, la fila 2 millones lleva un tiempo, incluso cuando todos los datos en el son de tamaño fijo y debería poder buscarlo directamente. Al menos, eso es lo que parece en SQLite 3.7.13.fuente
Aquí hay una modificación de la solución de @ ank:
Esta solución también funciona para índices con espacios, porque aleatorizamos un desplazamiento en un rango [0, cuenta).
MAX
se utiliza para manejar un caso con mesa vacía.Aquí hay resultados de pruebas simples en una tabla con 16k filas:
fuente
Se me ocurrió la siguiente solución para las grandes bases de datos sqlite3 :
Finalmente, agrega +1 para evitar que rowid sea igual a 0.
fuente