Selecção rápida de uma linha aleatória de uma tabela grande no mysql
Pergunta
O que é uma maneira rápida de selecionar uma linha aleatória de uma tabela mysql grande?
Eu estou trabalhando em php, mas eu estou interessado em qualquer solução, mesmo que seja em outro idioma.
Solução
Grab tudo do id, escolher um aleatório a partir dele, e recuperar a linha completa.
Se você sabe do id são sequenciais, sem buracos, você pode simplesmente pegar o máximo e calcular um id aleatório.
Se há buracos aqui e ali, mas principalmente seqüenciais valores, e você não se preocupam com a aleatoriedade ligeiramente inclinada, pegue o valor máximo, calcular um id, e selecione a primeira linha com um id igual ou superior a um você calculou. A razão para a inclinação é seguir esses buracos que de id terá uma maior chance de ser escolhido do que os que seguem outro id.
Se você pedir por acaso, você vai ter uma terrível mesa-scan em suas mãos, ea palavra rápida não se aplica a uma solução deste tipo.
Não faça isso, nem você deve pedir por um GUID, ele tem o mesmo problema.
Outras dicas
Eu sabia que tinha que haver uma maneira de fazê-lo em uma única consulta de uma forma rápida. E aqui está:
Uma maneira rápida, sem envolvimento de código externo, parabéns a
http://jan.kneschke.de/projects/mysql/order -de-rand /
SELECT name
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
MediaWiki usa um truque interessante (para especial da Wikipedia: recurso Random): a tabela com os artigos tem uma coluna extra com um número aleatório (gerado quando o artigo é criado). Para obter um artigo aleatório, gerar um número aleatório e obter o artigo com a próxima maior ou menor (não me lembro qual) valor na coluna de números aleatórios. Com um índice, isso pode ser muito rápido. (E MediaWiki é escrito em PHP e desenvolvido para MySQL.)
Esta abordagem pode causar um problema se os números resultantes são mal distribuída; IIRC, isso foi corrigido em MediaWiki, por isso, se você decidir fazê-lo desta forma, você deve dar uma olhada no código para ver como é feito atualmente (provavelmente eles periodicamente regenerar a coluna de número aleatório).
Aqui está uma solução que é executado rapidamente, e torna-se uma melhor distribuição aleatória, sem depender de valores id sendo contígua ou a partir de 1.
SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*) FROM mytable)));
SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1');
PREPARE stmt1 FROM @sql;
EXECUTE stmt1;
Talvez você poderia fazer algo como:
SELECT * FROM table
WHERE id=
(FLOOR(RAND() *
(SELECT COUNT(*) FROM table)
)
);
Esta é assumindo os seus números de identificação são todos sequencial sem lacunas.
Adicione uma coluna que contém um valor aleatório calculado para cada linha, e usar isso na cláusula de ordenação, limitando a um resultado sobre a seleção. Isso funciona mais rápido do que ter a mesa de digitalização que causa ORDER BY RANDOM()
.
Update:. Você ainda precisa calcular algum valor aleatório antes de emitir a declaração SELECT
sobre a recuperação, é claro, por exemplo
SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1
Um fácil, mas lento caminho seria (bom para tabelas smallish)
SELECT * from TABLE order by RAND() LIMIT 1
No código pseudo:
sql "select id from table"
store result in list
n = random(size of list)
sql "select * from table where id=" + list[n]
Isso pressupõe que id
é uma chave única (primário).
Há uma outra maneira de produzir linhas aleatórias usando apenas uma consulta e sem ordem por rand (). Envolve as variáveis ??definidas pelo usuário. Consulte como produzir linhas aleatórias de uma tabela
A fim de encontrar linhas aleatórias de uma tabela, não use ORDER BY RAND (), porque obriga MySQL para fazer um arquivo completo de classificação e só então recuperar o número linhas limite necessário. Para evitar esse tipo de arquivo completo, use a função RAND () apenas na cláusula where. Ele vai parar assim que alcança o número necessário de linhas. Vejo http://www.rndblog.com/how-to- select-random-linhas-em-mysql /
Se você fizer linha não de exclusão nesta tabela, a forma mais eficiente é:
(se você sabe o id mininum apenas ignorá-lo)
SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1
$randId=mt_rand((int)$row['minId'], (int)$row['maxId']);
SELECT id,name,... FROM table WHERE id=$randId LIMIT 1
Para selecionar várias linhas aleatórias de uma determinada tabela (digamos 'palavras'), nossa equipe veio com essa beleza:
SELECT * FROM
`words` AS r1 JOIN
(SELECT MAX(`WordID`) as wid_c FROM `words`) as tmp1
WHERE r1.WordID >= (SELECT (RAND() * tmp1.wid_c) AS id) LIMIT n
O clássico "id SELECT FROM tabela ORDER BY RAND () LIMIT 1" é realmente OK.
Veja o trecho acompanhamento do manual do MySQL:
Se você usar LIMIT row_count com ORDER BY, MySQL termina a ordenação logo que ele encontrar os primeiros row_count linhas do resultado ordenada, em vez de ordenar o resultado inteiro.
Com um yo ordem vai fazer uma tabela de verificação completa. Sua melhor se você fizer um select count (*) e, posteriormente, obter uma linha aleatória = rownum entre 0 e o último registro
Dê uma olhada este link por Jan Kneschke ou esta resposta modo ambos discutir a mesma questão. A resposta SO vai sobre várias opções e também tem algumas sugestões boas, dependendo de suas necessidades. Jan vai sobre todas as várias opções e as características de cada performance. Ele acaba com o seguinte para o método mais otimizado pelo qual fazer isso dentro de um MySQL selecione:
SELECT name
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
HTH,
-Dipin
Eu sou um pouco novo para SQL, mas como sobre a geração de um número aleatório em PHP e usando
SELECT * FROM the_table WHERE primary_key >= $randNr
isto não resolve o problema com furos na tabela.
Mas aqui está uma torção no lassevks sugestão:
SELECT primary_key FROM the_table
Use mysql_num_rows () em PHP criar um número aleatório com base no resultado acima:
SELECT * FROM the_table WHERE primary_key = rand_number
Em uma nota lateral quão lenta é SELECT * FROM the_table
:
Criando um número aleatório baseado em mysql_num_rows()
e, em seguida, mover o ponteiro de dados para que mysql_data_seek()
ponto. Quão lento este vai ser em grandes mesas com dizer um milhão de linhas?
Eu corri para o problema onde meus IDs não foram seqüencial. O que eu vim com isso.
SELECT * FROM products WHERE RAND()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1
As linhas retornadas são aproximadamente 5, mas limitá-lo a 1.
Se você quiser adicionar outra cláusula em que se torna um pouco mais interessante. Digamos que você deseja procurar produtos em desconto.
SELECT * FROM products WHERE RAND()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1
O que você tem a fazer é se certificar de que estão voltando resultado suficiente que é por isso que eu tê-lo definido como 100. Ter um ONDE desconto <0,2 cláusula na subconsulta era 10x mais lento, por isso é melhor para retornar mais resultados e limite .
Eu vejo aqui um monte de solução. Um ou dois parece ok, mas outras soluções têm algumas restrições. Mas a seguinte solução irá trabalhar para todos situação
select a.* from random_data a, (select max(id)*rand() randid from random_data) b
where a.id >= b.randid limit 1;
Aqui, id, não precisam ser sequenciais. Poderia ser qualquer coluna de chave primária / exclusiva / auto incremento. Consulte o seguinte maneira mais rápida de selecionar uma linha aleatória de uma tabela MySQL grande
Graças Zillur - www.techinfobest.com
Use o abaixo consulta para obter a linha aleatória
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 1
No meu caso a minha mesa tem um id como chave primária, auto-incremento sem intervalos, para que eu possa usar COUNT(*)
ou MAX(id)
para obter o número de linhas.
Eu fiz este script para testar a operação mais rápida:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
Os resultados são:
- Conde:
36.8418693542479 ms
- Max:
0.241041183472 ms
- Ordem :
0.216960906982 ms
Resposta com o método ordem:
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 1
...
SELECT * FROM tbl WHERE id = $result;
Eu tenho usado essa e o trabalho foi feito a referência href="https://www.warpconduit.net/2011/03/23/selecting-a-random-record-using-mysql-benchmark-results/" aqui
SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30;
Criar uma função para fazer isso provavelmente a melhor resposta e resposta mais rápida aqui!
A Favor -. Funciona mesmo com lacunas e extremamente rápidos
<?
$sqlConnect = mysqli_connect('localhost','username','password','database');
function rando($data,$find,$max = '0'){
global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL
if($data == 's1'){
$query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1");
$fetched_data = mysqli_fetch_assoc($query);
if(mysqli_num_rows($fetched_data>0){
return $fetch_$data;
}else{
rando('','',$max); // Start Over the results returned nothing
}
}else{
if($max != '0'){
$irand = rand(0,$max);
rando('s1',$irand,$max); // Start rando with new random ID to fetch
}else{
$query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1");
$fetched_data = mysqli_fetch_assoc($query);
$max = $fetched_data['id'];
$irand = rand(1,$max);
rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return
}
}
}
$your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY
?>
Tenha em mente este código como não foi testado, mas é um conceito de trabalho para retornar entradas aleatórias mesmo com lacunas .. Enquanto as lacunas não são grandes o suficiente para causar um problema de tempo de carregamento.
método rápido e sujo:
SET @COUNTER=SELECT COUNT(*) FROM your_table;
SELECT PrimaryKey
FROM your_table
LIMIT 1 OFFSET (RAND() * @COUNTER);
A complexidade da primeira consulta é O (1) para tabelas MyISAM.
A segunda consulta acompanha uma varredura completa da tabela. Complexidade = O (n)
sujo e método rápido:
Mantenha uma tabela separada por apenas esta finalidade. Você também deve inserir as mesmas linhas para esta tabela, sempre inserindo para a tabela original. Assunção:. Não DELETEs
CREATE TABLE Aux(
MyPK INT AUTO_INCREMENT,
PrimaryKey INT
);
SET @MaxPK = (SELECT MAX(MyPK) FROM Aux);
SET @RandPK = CAST(RANDOM() * @MaxPK, INT)
SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK);
Se DELETEs são permitidos,
SET @delta = CAST(@RandPK/10, INT);
SET @PrimaryKey = (SELECT PrimaryKey
FROM Aux
WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta
LIMIT 1);
A complexidade global é O (1).
SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;