Tengo una tabla en postgres que contiene un par de millones de filas. He comprobado en Internet y he encontrado lo siguiente
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
Funciona, pero es muy lento ... ¿hay otra forma de hacer esa consulta, o una forma directa de seleccionar una fila aleatoria sin leer toda la tabla? Por cierto, 'myid' es un número entero pero puede ser un campo vacío.
postgresql
random
Juan
fuente
fuente
Respuestas:
Es posible que desee experimentar con
OFFSET
, como enSELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
El
N
es el número de filas enmytable
. Es posible que primero deba hacer unaSELECT COUNT(*)
para averiguar el valor deN
.Actualización (por Antony Hatchkins)
Debes usar
floor
aquí:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Considere una tabla de 2 filas;
random()*N
genera0 <= x < 2
y, por ejemplo,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
devuelve 0 filas debido al redondeo implícito al int más cercano.fuente
SELECT COUNT(*)
?, quiero decir, no usar todos los valores en la tabla sino solo una parte de ellos?EXPLAIN SELECT ...
con diferentes valores de N da el mismo costo para la consulta, entonces supongo que es mejor ir por el valor máximo de N.PostgreSQL 9.5 introdujo un nuevo enfoque para una selección de muestras mucho más rápida: TABLESAMPLE
La sintaxis es
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage); SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
Esta no es la solución óptima si solo desea seleccionar una fila, porque necesita saber el CONTADOR de la tabla para calcular el porcentaje exacto.
Para evitar un RECUENTO lento y usar TABLESAMPLE rápido para tablas de 1 fila a miles de millones de filas, puede hacer:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1; ...
Puede que esto no parezca tan elegante, pero probablemente sea más rápido que cualquiera de las otras respuestas.
Para decidir si desea utilizar BERNULLI oder SYSTEM, lea acerca de la diferencia en http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/
fuente
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?SELECT reltuples FROM pg_class WHERE relname = 'my_table'
para estimar el recuento.Intenté esto con una subconsulta y funcionó bien. Offset, al menos en Postgresql v8.4.4 funciona bien.
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
fuente
Necesitas usar
floor
:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
fuente
random()*N
genera 0 <= x <2 y, por ejemplo,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
devuelve 0 filas debido al redondeo implícito al int más cercano.order by random()
, aproximadamente como3*O(N) < O(NlogN)
: las cifras reales serán ligeramente diferentes debido a los índices.WHERE myid NOT IN (1st-myid)
yWHERE myid NOT IN (1st-myid, 2nd-myid)
no funcionaría ya que la decisión es tomada por la compensación. Hmmm ... Creo que podría reducir N en 1 y 2 en el segundo y tercer SELECT.floor()
? ¿Qué ventaja ofrece?Consulte este enlace para ver algunas opciones diferentes. http://www.depesz.com/index.php/2007/09/16/my- Thoughts-on-getting-random-row/
Actualizar: (A. Hatchkins)
El resumen del (muy) extenso artículo es el siguiente.
El autor enumera cuatro enfoques:
1)
ORDER BY random() LIMIT 1;
- lento2)
ORDER BY id where id>=random()*N LIMIT 1
- no uniforme si hay espacios3) columna aleatoria: debe actualizarse de vez en cuando
4) agregado aleatorio personalizado - método astuto, podría ser lento: random () debe generarse N veces
y sugiere mejorar el método n. ° 2 utilizando
5)
ORDER BY id where id=random()*N LIMIT 1
con consultas posteriores si el resultado está vacío.fuente
La forma más fácil y rápida de obtener una fila aleatoria es usar la
tsm_system_rows
extensión:CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
Luego puede seleccionar el número exacto de filas que desea:
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
Está disponible con PostgreSQL 9.5 y versiones posteriores.
Ver: https://www.postgresql.org/docs/current/static/tsm-system-rows.html
fuente
ORDER BY random() LIMIT 1;
debería ser lo suficientemente rápido.Se me ocurrió una solución muy rápida sin
TABLESAMPLE
. Mucho más rápido queOFFSET random()*N LIMIT 1
. Ni siquiera requiere contar la mesa.La idea es crear un índice de expresión con datos aleatorios pero predecibles, por ejemplo
md5(primary key)
.Aquí hay una prueba con datos de muestra de 1 millón de filas:
create table randtest (id serial primary key, data int not null); insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000); create index randtest_md5_id_idx on randtest (md5(id::text)); explain analyze select * from randtest where md5(id::text)>md5(random()::text) order by md5(id::text) limit 1;
Resultado:
Esta consulta puede a veces (con una probabilidad de 1 / Number_of_rows) devolver 0 filas, por lo que es necesario verificarla y volver a ejecutarla. Además, las probabilidades no son exactamente las mismas: algunas filas son más probables que otras.
Para comparacion:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
Los resultados varían ampliamente, pero pueden ser bastante malos:
fuente