Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como ecoar linhas aleatórias do banco de dados?


Duas soluções apresentadas aqui. Ambas as soluções propostas são somente mysql e podem ser usadas por qualquer linguagem de programação como consumidora. O PHP seria muito lento para isso, mas poderia ser o consumidor dele.

Solução mais rápida :posso trazer 1000 linhas aleatórias de uma tabela de 19 milhões de linhas em cerca de 2 décimos de segundo com técnicas de programação mais avançadas.

Solução mais lenta :Leva cerca de 15 segundos com técnicas de programação sem energia.

A propósito, ambos usam a geração de dados vista AQUI que eu escrevi. Então esse é o meu pequeno esquema. Eu uso isso, continue com DOIS mais auto-inserções vistas por lá, até que eu tenha 19 milhões de linhas. Então eu não vou mostrar isso de novo. Mas para obter essas 19 milhões de linhas, veja isso e faça mais 2 dessas inserções, e você terá 19 milhões de linhas.

Versão mais lenta primeiro


Primeiro, o método mais lento.
select id,thing from ratings order by rand() limit 1000;

Isso retorna 1.000 linhas em 15 segundos.

Solução mais rápida


Isso é um pouco mais complicado de descrever. A essência disso é que você pré-computa seus números aleatórios e gera uma in clause final de números aleatórios, separados por vírgulas e entre parênteses.

Será parecido com (1,2,3,4) mas terá 1000 números nele.

E você os armazena e os usa uma vez. Como um bloco de tempo para criptografia. Ok, não é uma grande analogia, mas você entendeu o ponto, espero.

Pense nisso como um final para um in cláusula e armazenada em uma coluna TEXT (como um blob).

Por que no mundo alguém iria querer fazer isso? Porque RNG (geradores de números aleatórios) são proibitivamente lentos. Mas gerá-los com algumas máquinas pode gerar milhares de forma relativamente rápida. A propósito (e você verá isso na estrutura dos meus chamados apêndices, eu capturo quanto tempo leva para gerar uma linha. Cerca de 1 segundo com mysql. Mas C#, PHP, Java, qualquer coisa pode juntar isso. O ponto não é como você monta, mas sim que você tem quando quer.

Essa estratégia, a mais longa e curta, é quando combinada com a busca de uma linha que não foi usada como uma lista aleatória, marcando-a como usada e emitindo uma chamada como
select id,thing from ratings where id in (a,b,c,d,e, ... )

e a cláusula in tiver 1.000 números, os resultados estarão disponíveis em menos de meio segundo. Eficaz empregando o mysql CBO (otimizador baseado em custo) que o trata como uma junção em um índice PK.

Deixo isso de forma resumida, porque é um pouco complicado na prática, mas inclui as seguintes partículas potencialmente
  • uma tabela contendo os números aleatórios pré-computados (Apêndice A)
  • uma estratégia de evento de criação do mysql (Apêndice B)
  • um procedimento armazenado que emprega uma Declaração Preparada (Apêndice C)
  • um proc armazenado somente mysql para demonstrar RNG in cláusula para chutes (Apêndice D)

Apêndice A


Uma tabela contendo os números aleatórios pré-computados
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


Para não transformar isso em um livro, veja minha resposta AQUI para um mecanismo para executar um evento mysql recorrente. Ele conduzirá a manutenção da tabela vista no Apêndice A usando técnicas vistas no Apêndice D e outros pensamentos que você deseja imaginar. Como reutilização de linhas, arquivamento, exclusão, o que for.

Apêndice C


procedimento armazenado para simplesmente obter 1000 linhas aleatórias.
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


Pode ser entrelaçado com o conceito do Apêndice B. No entanto, você quer fazê-lo. Mas deixa você com algo para ver como o mysql poderia fazer tudo sozinho no lado RNG das coisas. A propósito, para os parâmetros 1 e 2 sendo 1000 e 19M respectivamente, leva 800 ms na minha máquina.

Esta rotina pode ser escrita em qualquer idioma como mencionado no início.
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 ;

Como chamar o proc armazenado acima:
call createARandomInString(1000,18000000);

Isso gera e salva 1 linha, de 1000 números agrupados conforme descrito acima. Números grandes, 1 a 18 milhões

Como uma ilustração rápida, se alguém modificar o proc armazenado, remova a linha perto da parte inferior que diz "usado para depuração" e tenha isso como a última linha, no proc armazenado que é executado, e execute isto:
call createARandomInString(4,18000000);

... para gerar 4 números aleatórios de até 18M, os resultados podem parecer
+-------------------------------------+
| myOutput                            |
+-------------------------------------+
| (2857561,5076608,16810360,14821977) |
+-------------------------------------+

Apêndice E


Verificação da realidade. Estas são técnicas um tanto avançadas e eu não posso ensinar ninguém sobre elas. Mas eu queria compartilhá-los de qualquer maneira. Mas não posso ensiná-lo. Fim de transmissão.