sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo hacer eco de filas aleatorias de la base de datos?

Dos soluciones presentadas aquí. Ambas soluciones propuestas son solo mysql y pueden ser utilizadas por cualquier lenguaje de programación como consumidor. PHP sería demasiado lento para esto, pero podría ser el consumidor.

Solución más rápida :Puedo traer 1000 filas aleatorias de una tabla de 19 millones de filas en aproximadamente 2 décimas de segundo con técnicas de programación más avanzadas.

Solución más lenta :Se tarda unos 15 segundos con técnicas de programación sin energía.

Por cierto ambos usan la generación de datos vista AQUÍ que escribí Así que ese es mi pequeño esquema. Yo uso eso, continuar con DOS más autoinsertos vistos por ahí, hasta tener 19 millones de filas. Así que no voy a mostrar eso de nuevo. Pero para obtener esos 19 millones de filas, ve a ver eso y haz 2 inserciones más, y tendrás 19 millones de filas.

Versión más lenta primero

Primero, el método más lento.

select id,thing from ratings order by rand() limit 1000;

Eso devuelve 1000 filas en 15 segundos.

Solución más rápida

Esto es un poco más complicado de describir. La esencia de esto es que usted calcula previamente sus números aleatorios y genera una cláusula in clause terminando de números aleatorios, separados por comas y envueltos con un par de paréntesis.

Se verá como (1,2,3,4) pero tendrá 1000 números.

Y los almacenas y los usas una vez. Como un bloc de notas de una sola vez para la criptografía. Ok, no es una gran analogía, pero espero que entiendas el punto.

Piense en ello como un final para un in clause y almacenada en una columna de TEXTO (como un blob).

¿Por qué en el mundo uno querría hacer esto? Porque RNG (generadores de números aleatorios) son prohibitivamente lentos. Pero generarlos con unas pocas máquinas puede generar miles relativamente rápido. Por cierto (y verá esto en la estructura de mis llamados apéndices, capturo cuánto tiempo lleva generar una fila. Aproximadamente 1 segundo con mysql. Pero C #, PHP, Java, cualquier cosa puede unir eso. El punto no es cómo lo juntas, sino que lo tienes cuando lo quieres.

Esta estrategia, en pocas palabras, es cuando se combina con obtener una fila que no se ha utilizado como una lista aleatoria, marcarla como utilizada y emitir una llamada como

select id,thing from ratings where id in (a,b,c,d,e, ... )

y la cláusula in tiene 1000 números, los resultados están disponibles en menos de medio segundo. Efectivo empleando mysql CBO (optimizador basado en costos) que lo trata como una unión en un índice PK.

Dejo esto en forma resumida, porque es un poco complicado en la práctica, pero incluye potencialmente las siguientes partículas

  • una tabla que contiene los números aleatorios calculados previamente (Apéndice A)
  • una estrategia mysql create event (Apéndice B)
  • un procedimiento almacenado que utiliza una declaración preparada (Apéndice C)
  • un proceso almacenado solo en mysql para demostrar RNG in cláusula para patadas (Apéndice D)

Apéndice A

Una tabla que contiene los números aleatorios precalculados

create table randomsToUse
(   -- create a table of 1000 random numbers to use
    -- format will be like a long "(a,b,c,d,e, ...)" string

    -- pre-computed random numbers, fetched upon needed for use

    id int auto_increment primary key,
    used int not null,  -- 0 = not used yet, 1= used
    dtStartCreate datetime not null, -- next two lines to eyeball time spent generating this row
    dtEndCreate datetime not null,
    dtUsed datetime null, -- when was it used
    txtInString text not null -- here is your in clause ending like (a,b,c,d,e, ... )
    -- this may only have about 5000 rows and garbage cleaned
    -- so maybe choose one or two more indexes, such as composites
);

Apéndice B

En aras de no convertir esto en un libro, vea mi respuesta AQUÍ para un mecanismo para ejecutar un evento mysql recurrente. Impulsará el mantenimiento de la tabla que se ve en el Apéndice A utilizando las técnicas que se ven en el Apéndice D y otros pensamientos que desee soñar. Como la reutilización de filas, el archivo, la eliminación, lo que sea.

Apéndice C

procedimiento almacenado para obtener simplemente 1000 filas aleatorias.

DROP PROCEDURE if exists showARandomChunk;
DELIMITER $$
CREATE PROCEDURE showARandomChunk
(
)
BEGIN
  DECLARE i int;
  DECLARE txtInClause text;

  -- select now() into dtBegin;

  select id,txtInString into i,txtInClause from randomsToUse where used=0 order by id limit 1;
  -- select txtInClause as sOut; -- used for debugging

  -- if I run this following statement, it is 19.9 seconds on my Dell laptop
  -- with 19M rows
  -- select * from ratings order by rand() limit 1000; -- 19 seconds

  -- however, if I run the following "Prepared Statement", if takes 2 tenths of a second
  -- for 1000 rows

  set @s1=concat("select * from ratings where id in ",txtInClause);

  PREPARE stmt1 FROM @s1;
  EXECUTE stmt1; -- execute the puppy and give me 1000 rows
  DEALLOCATE PREPARE stmt1;
END
$$
DELIMITER ;

Apéndice D

Se puede entrelazar con el concepto del Apéndice B. Como quieras hacerlo. Pero te deja con algo para ver cómo mysql podría hacerlo todo por sí mismo en el lado RNG de las cosas. Por cierto, para que los parámetros 1 y 2 sean 1000 y 19M respectivamente, mi máquina tarda 800 ms.

Esta rutina podría escribirse en cualquier idioma como se mencionó al principio.

drop procedure if exists createARandomInString;
DELIMITER $$
create procedure createARandomInString
(   nHowMany int, -- how many numbers to you want
    nMaxNum int -- max of any one number
)
BEGIN
    DECLARE dtBegin datetime;
    DECLARE dtEnd datetime;
    DECLARE i int;
    DECLARE txtInClause text;
    select now() into dtBegin;

    set i=1;
    set txtInClause="(";
    WHILE i<nHowMany DO
        set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,", "); -- extra space good due to viewing in text editor
        set i=i+1;
    END WHILE;
    set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,")");
    -- select txtInClause as myOutput; -- used for debugging
    select now() into dtEnd;

    -- insert a row, that has not been used yet
    insert randomsToUse(used,dtStartCreate,dtEndCreate,dtUsed,txtInString) values 
       (0,dtBegin,dtEnd,null,txtInClause);
END
$$
DELIMITER ;

Cómo llamar al proceso almacenado anterior:

call createARandomInString(1000,18000000);

Eso genera y guarda 1 fila, de 1000 números envueltos como se describe arriba. Números grandes, 1 a 18M

Como una ilustración rápida, si uno fuera a modificar el proceso almacenado, elimine la línea cerca de la parte inferior que dice "usado para depurar", y tenga eso como la última línea, en el proceso almacenado que se ejecuta, y ejecute esto:

call createARandomInString(4,18000000);

... para generar 4 números aleatorios hasta 18 millones, los resultados podrían parecerse a

+-------------------------------------+
| myOutput                            |
+-------------------------------------+
| (2857561,5076608,16810360,14821977) |
+-------------------------------------+

Apéndice E

Verificación de la realidad. Estas son técnicas algo avanzadas y no puedo instruir a nadie sobre ellas. Pero quería compartirlos de todos modos. Pero no puedo enseñarlo. Cambio y fuera.