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.