Excellente solucion algo parecido estaba intentando, pero como tengo
entendido no tienen mas oid las rows . habria q modificar la tabla para
ponerle
un valor consecutivo . sonaba a serial. pero la idea  q me pasaste con
random , esta perfecta, sencilla y perfecta.
salu2
mdc

2011/6/23 Álvaro Hernández <[email protected]>

> Wed, Jun 22, 2011 at 06:12:16PM -0400, Alvaro Herrera escribió:
>
> >Excerpts from Juan's message of mié jun 22 14:59:12 -0400 2011:
> >> Perdon
> >>
> >> el lenguaje usado es 'plpgsql'
> >> lo que pasa es q estoy haciendo algo usando random para seleccionar
> >> un grupo de registros.
> >
> >me parece que vas por mal camino.  Lee esto
> >
> >
> http://blog.rhodiumtoad.org.uk/2009/03/08/selecting-random-rows-from-a-table/
>
>         Efectivamente, hay una solución muy sencilla (como la que
> comenté yo) pero que es conocido sólo aplica a tablas pequeñas, como el
> link que que envía Álvaro demuestra. No hay soluciones inmediatas para
> tablas grandes, como el post (y muchos otros al respecto) comentan, o
> que requieren añadir columnas, depender de PKs numéricas o columnas
> seriales que no tengan "muchos huecos". Ninguna aparenta, por tanto, una
> buena solución.
>
>        Así que, volviendo a pensar sobre el tema, se me ha ocurrido
> una solución que podría funcionar bien :) La idea básica es generar N
> números aleatorios al principio y luego usar cursores y desplazamientos
> para extraer dichos registros. Así que he escrito una pequeña función
> para probarlo:
>
> CREATE FUNCTION random_rows(table_name text, n_rnd_rows int, n_rows bigint)
> RETURNS SETOF RECORD AS $$
>        DECLARE
>                curs            refcursor;
>                ret             record;
>                rnd_row_pos     int[];
>        BEGIN
>                FOR i IN 1..n_rnd_rows LOOP
>                        rnd_row_pos[i] = floor(random() * n_rows);
>                END LOOP;
>
>                -- Sort the array
>                SELECT INTO rnd_row_pos array_agg(i) FROM (SELECT * FROM
> unnest(rnd_row_pos) i ORDER BY i) AS i;
>
>                OPEN curs FOR EXECUTE 'SELECT * FROM ' || table_name; --
> ORDER is not relevant
>
>                MOVE ABSOLUTE rnd_row_pos[1] - 1 FROM curs;
>                FETCH NEXT FROM curs INTO ret;
>                RETURN NEXT ret;
>
>                FOR i IN 2..n_rnd_rows LOOP
>                        MOVE RELATIVE rnd_row_pos[i] - rnd_row_pos[i - 1] -
> 1 FROM curs;
>                        FETCH NEXT FROM curs INTO ret;
>                        RETURN NEXT ret;
>                END LOOP;
>
>                CLOSE curs;
>
>                RETURN;
>        END;
> $$ LANGUAGE plpgsql;
>
>        Esta función lee los registros de origen "en el orden que están
> en disco". Sin embargo, esto no es un problema porque luego se
> seleccionan aleatoriamente los registros a obtener.
>
>
>        A nivel de demo, probando con una tabla llamada 'random' que
> tiene 10 millones de filas generadas con generate_series:
>
> - Solución trivial (sólo tablas pequeñas):
>
>        aht=> EXPLAIN ANALYZE SELECT * FROM random ORDER BY random() LIMIT
> 20;
>        [...]
>        Total runtime: 7349.974 ms
>
> - Con la función que comento, pero obteniendo el número de filas con
> COUNT(*),
> que requiere un seqscan, ya supone un ahorro importante de tiempo (y
> creciente con el tamaño de la tabla y muy significativamente si work_mem
> limitara):
>
>        aht=> EXPLAIN ANALYZE SELECT * FROM random_rows('random', 20,
> (SELECT COUNT(*) FROM random)) AS (i integer);
>        [...]
>        Total runtime: 5472.431 ms
>
>
> - Si en lugar de hacer COUNT(*) sabemos el número de registros o queremos
> restringir el número de filas random a los primeros N registros, la
> técnica que comento rinde fantásticamente:
>
>        aht=> EXPLAIN ANALYZE SELECT * FROM random_rows('random', 20,
> 10000000) AS (i integer);
>        [...]
>        Total runtime: 843.520 ms
>
> - Otra opción sería fiarnos de las estadísticas de postgres y usarlas para
> estimar el número de registros:
>
>        aht=> EXPLAIN ANALYZE SELECT * FROM random_rows('random', 20,
> (SELECT n_live_tup FROM pg_stat_all_tables WHERE relname = 'random')) AS (i
> integer);
>        [...]
>        Total runtime: 856.390 ms
>
> - Por razones de eficiencia, los resultados de esta función salen
> "ordenados". A su vez se pueden reordenar aleatoriamente si se quiere
> sin mucho costo adicional:
>
>        aht=> EXPLAIN ANALYZE SELECT * FROM random_rows('random', 20,
> 10000000) AS (i integer) ORDER BY random() LIMIT 20;
>        [...]
>        Total runtime: 863.488 ms
>
> Y parece funcionar:
>
>        aht=> SELECT * FROM random_rows('random', 20, 10000000) AS (i
> integer) ORDER BY random() LIMIT 20;
>            i
>        ---------
>         2078060
>         4894086
>         4437251
>         3700397
>          642530
>         7685856
>         2297124
>         5127436
>         2764292
>          376513
>         6676704
>          791899
>          671257
>         1029904
>         2942200
>         1237436
>         9265684
>         1302897
>         7479438
>         3685012
>        (20 filas)
>
>
>        ¿Qué os parece el método? ¿Se os ocurre algún punto flaco que
> pueda tener?
>
>        Saludos,
>
>        Álvaro
>
> --
>
> Álvaro Hernández Tortosa
>
>
> -----------
> NOSYS
> Networked Open SYStems
>

Responder a