2010-02-17 39 views
87

Tengo una tabla sqlite con el siguiente esquema:Seleccionar fila al azar de una tabla SQLite

CREATE TABLE foo (bar VARCHAR) 

estoy usando esta tabla como el almacenamiento de una lista de cadenas.

¿Cómo selecciono una fila al azar de esta tabla?

+0

múltiples http://stackoverflow.com/questions/4114940/select-random-rows-in-sqlite –

Respuesta

161

Tenga una mirada en Selecting a Random Row from an SQLite Table

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

Su respuesta parece ser similar al proporcionado por Svetlozar. Entonces tengo la misma pregunta para ti. –

+1

¿Cómo extender esta solución a una unión? Cuando se usa '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. –

8
SELECT bar 
FROM  foo 
ORDER BY Random() 
LIMIT 1 
+9

Ya que seleccionará primero el contenido de la tabla completa, ¿no sería muy lento para las tablas grandes? –

+1

¿No puedes limitar el alcance utilizando las condiciones "DONDE"? – jldupont

13

¿Qué hay de:

SELECT COUNT(*) AS n FROM foo; 

continuación, 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 solo actualizarlo cuando cambie el recuento de la base de datos. De esta manera, no tiene que hacer el COUNT SELECTO cada vez.

+0

Ese es un buen método rápido. No se generaliza muy bien seleccionar más de 1 fila, pero el OP solo pidió 1, así que supongo que está bien. –

+0

Una cosa curiosa a tener en cuenta es que el tiempo necesario para encontrar el 'OFFSET' parece aumentar según el tamaño del desplazamiento: la fila 2 es rápida, la fila 2 millones tarda un rato, incluso cuando se fijan todos los datos en el tamaño y debería poder buscarlo directamente. Al menos, eso es lo que parece en SQLite 3.7.13. –

+0

@KenWilliams Casi 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 a pesar de que solo devolverá 1. –

26

Las siguientes soluciones son mucho más rápidas que las de anktastic (el recuento (*) cuesta mucho, pero si puede almacenarlo en caché, entonces la diferencia no debería ser tan grande), que es mucho más rápido que el "orden por random() "cuando tienes una gran cantidad de filas, aunque tienen algunos inconvenientes. (. Es decir, unos supresiones)

Si sus ROWIDs están bien embalados, entonces usted puede hacer lo siguiente (utilizando (select max(rowid) from foo)+1 en lugar de max(rowid)+1 da 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 hoyos, a veces intentarás seleccionar un rowid no existente, y el select devolverá un conjunto de resultados vacío. Si esto no es aceptable, puede proporcionar un valor por defecto de esta manera:

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 la más alta rowid), pero si a menudo agrega cosas a la mesa, se convertirá en un objetivo en movimiento y la distribución de probabilidades debería ser mucho mejor.

Sin embargo, otra solución, si a menudo seleccionar cosas al azar de una tabla con una gran cantidad de agujeros, entonces es posible que desee crear una tabla que contiene las filas de la tabla original, ordenados en orden aleatorio:

create table random_foo(foo_id); 

entonces, periodicalliy, vuelva a llenar la tabla random_foo

delete from random_foo; 
insert into random_foo select id from foo; 

Y para seleccionar una fila al azar, se puede usar mi primer método (no hay agujeros aquí). 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 muy a menudo.

Sin embargo, otra forma, que encontré recientemente en un mailing list, es poner un disparador en eliminar para mover la fila con el rowid más grande en la fila eliminada actual, para que no queden agujeros.

Por último, tenga en cuenta que el comportamiento de rowid y un autoincrement clave primaria número entero no es idéntico (con rowid, cuando se inserta una nueva fila, max (rowid) se elige 1, wheras es higest-valor en constante visto + 1 para una clave principal), por lo que la última solución no funcionará con una autoincrementación en random_foo, pero los otros métodos lo harán.

+0

Como acabo de ver en una lista de correo, en lugar de tener el método alternativo (método 2), puede simplemente usar rowid> = [random] en lugar de =, pero en realidad es slugissingly lento en comparación con el método 2. –

+1

Esta es una gran respuesta; sin embargo, tiene un problema. 'SELECT max (rowid) + 1' será una consulta lenta, requiere un escaneo completo de la tabla. sqlite solo optimiza la consulta 'SELECT max (rowid)'. Por lo tanto, esta respuesta sería mejorado por: 'seleccionar * desde foo donde rowid = (abs (random())% (seleccionar (SELECT MAX (rowid) de foo) 1));' ver esto por más información: http://sqlite.1065341.n5.nabble.com/performance-question-SELECT-max-rowid-1-td24311.html – dasl

2

Aquí es 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 lagunas, ya que aleatorizar un desplazamiento en un rango [0, count). MAX se usa para manejar una caja con una mesa vacía.

Éstos son los resultados de pruebas sencillas en una mesa 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 
2

Usted necesita poner "orden al azar()" en su consulta.

Ejemplo:

select * from quest order by RANDOM(); 

Veamos un ejemplo completo

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

Insertar algunos valores:

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

Un defecto seleccione:

select * from quest; 

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

Una seleccionar al azar:

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 se selecciona, 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 seleccione, el retorno será diferente.

+0

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 fuente de crowdfunding, y que, por lo general, si el OP entendía que el código se publica como respuesta, él/ella habría encontrado una solución similar por sí mismo, 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

me ocurrió con la siguiente solución para los 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.

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

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

Por último, se agrega 1 para evitar rowid igual a 0.

Cuestiones relacionadas