Seleccione una fila aleatoria de una tabla sqlite


Respuestas:


213

Eche un vistazo a Seleccionar una fila aleatoria de una tabla SQLite

SELECT * FROM table ORDER BY RANDOM() LIMIT 1;

1
¿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.
danielson317

FWIW, mi pregunta se responde aquí. Y la respuesta es que no puede sembrar el número aleatorio. stackoverflow.com/questions/24256258/…
danielson317

31

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:

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;

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:

create table random_foo(foo_id);

Luego, periódicamente, vuelva a llenar la tabla random_foo

delete from random_foo;
insert into 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 order by RANDOM();

Veamos un ejemplo completo

  1. Crea una tabla:
CREATE TABLE  quest  (
    id  INTEGER PRIMARY KEY AUTOINCREMENT,
    quest TEXT NOT NULL,
    resp_id INTEGER NOT NULL
);

Insertando algunos valores:

insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);

Una selección predeterminada:

select * from quest;

| id |   quest  | resp_id |
   1     1024/4       6
   2     256/2       12
   3     128/1       24
--

Un selecto aleatorio:

select * from quest order by RANDOM();
| id |   quest  | resp_id |
   3     128/1       24
   1     1024/4       6
   2     256/2       12
--
* Cada vez que seleccione, el orden será diferente.

Si desea devolver solo una fila

select * from quest order by RANDOM() LIMIT 1;
| id |   quest  | resp_id |
   2     256/2       12
--
* Cada vez que selecciones, la devolución será diferente.


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.


1
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.)
Yakov Galka

13
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1

11
Dado que primero seleccionará todo el contenido de la tabla, ¿no consumiría mucho tiempo para tablas grandes?
Alex_coder

1
¿No se puede limitar el alcance utilizando las condiciones "DÓNDE"?
jldupont

11

Aquí hay una modificación de la solución de @ ank:

SELECT * 
FROM table
LIMIT 1 
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)

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.000 user 0.000140 sys 0.000117

sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103

sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208

4

Se me ocurrió la siguiente solución para las grandes bases de datos sqlite3 :

SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1; 

La función abs (X) devuelve el valor absoluto del argumento numérico X.

La función random () devuelve un entero pseudoaleatorio entre -9223372036854775808 y +9223372036854775807.

El operador% genera el valor entero de su operando izquierdo módulo su operando derecho.

Finalmente, agrega +1 para evitar que rowid sea igual a 0.


1
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.
Calicoder
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.