+2

Resultados Aleatórios com o MySQL

criado por Celso Endo em 10/08/2003 4:27pm
OBS: Escrevi esse artigo pois eu passei por esse problema e num outro artigo aqui do phpbrasil.com eu vi que teve gente que estava tendo o mesmo problema. Não tenho certeza se isso ocorre com todos os servidores, mas em muitos... =)

Introdução

Muitos já devem ter notado que ao enviar a query "select * from tabela order by rand() limit 1" no MySQL, em certos casos não retorna o resultado desejado. Primeiro irei explicar o problema dessa query, depois darei um exemplo de um sistema em que essa query não irá funcionar corretamente e por fim irei apresentar um código que eu uso justamente para ultrapassar esse problema.

O Problema

Olhe bem para essa query abaixo:

Select * from tabela order by rand() limit 1

Sim, realmente essa consulta irá retornar um dado aleatório da tabela, mas se num script PHP você precisar, por exemplo, executar a mesma query em um intervalo muito pequeno de tempo ele irá retornar sempre os mesmos dados!

O problema que ocorre é que a maioria dos servidores tem um tipo de clock interno que é utilizado para escolher a entrada aleatória da tabela. Agora se, por exemplo, você abrir uma página que tenha essa query e em seguida abrir a mesma página com outro browser, isso irá funcionar sem problemas, afinal você estará abrindo outra sessão do browser.

Exemplo prático (e real!!)

Imagine que você tenha um sistema de banners aleatórios que em toda página de seu site você irá exibir um banner aleatório no topo E/OU no final da página. Se sua intenção for mostrar um banner aleatório no topo e outro ao final da página, essa query já será "inútil", pois irá sempre mostrar os 2 banners iguais.

Mas irei mostrar só um banner por página!

Tudo bem, mas é muito óbvio que seu visitante não ficará por muito tempo numa mesma página, ou seja, quando ele clicar num link para outra página, se não tiver passado tempo suficiente na primeira página, irá mostrar novamente o mesmo banner!!! E só após um certo período de tempo que o visitante irá ver um banner diferente.

Agora pense numa situação em que alguém está te pagando para divulgar o banner em seu sistema aleatório e quando a pessoa irá visitar seu site para comprovar que o banner dele está dentro do sistema, ele irá sempre ver o banner de outro anunciante, não importando o quanto de "reloads" que ele venha a fazer na página. E como você irá explicar isso a ele? Ou melhor... como ele conseguirá entender isso?

Comentários:

Mostrando 1 - 10 de 14 comentários
Thiago Benko disse:
Boa Tarde.

Primeiramente gostaria de parabeniza-lo pelo post, com certeza de grande valia.

Tenho uma dúvida e preciso da ajuda dos colegas. Estou desenvolvendo um QUIZ (Perguntas e respostas) e preciso listar os Campos da tabela aleatoriamente.
Tabela Questões
codigo
pergunta
resposta1
resposta2
resposta3
respostac (correta)

Preciso que a ordem dos campos ao executar a query no php mude aleatoriamente, para que as posições das perguntas não sejam sempre as mesmas.

Desde já, agradeço pela atenção de todos.
02/12/2015 1:46pm (~1 ano atrás)

ops... faltou fechar o comente acima... heehe

?>

pronto fechado;
13/03/2007 8:35pm (~9 anos atrás)

apos ter postado o comentário acima, fui atraz de como trabalhar com os ARRAY que pra ser sincero ainda nao sei direito hehehhe... mas enfim.. a lógica acima funcionou perfeitamente. e segue abaixo o código.

// nao comentei muito o código pq sinceramente nao me arrisco comentar sobre os array, apenas sei que funiona.

como pra mim tudo que se vende é produto... entao considerei o banner como um produto. e ai vai o codigo

#########################################################

<?php //require("conectabanco.php"); ?>
<?

// busca os id (pd_id)da tabela
$sql_sort = mysql_query("SELECT pd_id FROM produto order by pd_id ASC");
// conta quantos registros existem
$total = mysql_num_rows($sql_sort);
// defino que $total será 1 a menos doque o numero de registro contado no banco, pq o array começa a contar do ZERO.
$total = $total-1;
// aqui to colocando os dados de ID do bando no ARRAY
// no exemplo: digamos que temos 3 registro com os id´s "5" e "8" e "9"
// no array ficará mais ou menos assim, 1=>5 e 2=>8 e3=> 9

while($res_sort = mysql_fetch_array($sql_sort)){
$id = $res_sort["pd_id"];
$array[] = $id;
}
// aqui com o rand eu faço um sorteio entre ZERO e o total de registros
$sortiado = rand(0,$total);
// aqui to pegando o ID sorteado, pra entender melhor... seguindo a lógica
// que no array ficou assim 1=>5 e 2=>8 e 3=> 9
// entao se o rand ali sorteou "2" a linha abaixo verá o equivalente no array que é o ID "8"
$id_sortiado = $array[$sortiado];
// faz a busca com base no ID... onde o id do produto é igual ao ID sorteado.
$prod = mysql_query("SELECT * FROM produto WHERE pd_id = '".$id_sortiado."'") or die(mysql_error("Erro com o Banco de Dados"));
$row_prod = mysql_fetch_assoc($prod);
// abaixo to escrevendo a imagem referente ao ID sorteado lincando p exibir ele...
echo "<a href=\"index.php?l=exibir_prod_escolhido&p=produtos&e=".$row_prod['pd_id']."\" ><img src=\"produtos/produto/".$row_prod['pd_fotopq']."\" alt=\"".$row_prod['pd_nome']."\" border=\"0\" class\"input\" width=\"204\" height=\"150\"/ ></a>";
13/03/2007 8:34pm (~9 anos atrás)

Otimo artigo, mas como o Emanuel Fonseca citou acima, (colado logo aki abaixo), terá esse problema de caso o id tenha sido excluido...

pas isso eu tenho uma lógica que daria perfeitamente, usando ARRAY. a logica é a seguinte

iremos contar o o numero de registros.
$total = mysql_num_rows($query);

ok, tendo o numero de registro, iremos fazer uma busca pelos "numeros existentes" no ID.

depois disso, usando while ou DO ou FOR, iremos relacionar os numeros existentes ao numero de contagem

exemplo...
digamos que nosso banco tem 5 registros, e como esse banco foi bem manipulado com insert e delete etc.

nossos id´s que tem no banco são...

6, 9, 14, 33, 99

entao com while iriamos relacionar com uso de ARRAY
em uma matriz que faria o segui..
$reg = 0;
while($total <= $reg){

// aki usando array coloca os id´s assim:

1 = 6
2 = 9
3 = 14
4 = 33
5 = 99

$reg = $reg++
}

depois disso, faz o sorteio e pega o id equivalente ao numero sorteado e dai sim faz o query do banner baseado no id.

Bom a lógica é essa, espero que tenham entendido. mas eu nao sei trabalhar com array, sei que funciona pela lógica, mas eu nunca sei como por o dado dentro da array e depois tirar.


Quote [
Uma correção importante [ responder ]
publicado por Emanuel Fonseca (usuário autenticado) - 2003-08-28 19:32:14

Essa rotina é muito boa, porém tem falhas. Ele faz a contagem do total de records, ou seja, ele da um numero inteiro. Se vc esta usando auto increment, logo terá problemas. Pq? Pq no dia que você excluir um record da tabela vc pode retornar um record inexistente. Exemplo:

total q vc tem 51 records
Remove id 15
Logo vc tem 50 records
Usando a função vc poderá gerar um valor aleatório de 0 a 50, ou seja, pode retornar o id que foi excluido.

Para solucionar basta testar a existencia do id retornado. Caso não exista um location com PHP_SELF resolve, ok!? É uma solução porém pode tornar o script lento.

]
12/03/2007 8:06pm (~9 anos atrás)

Jorge Jardim disse:
E no caso de vc ter q utilizar algum tipo de filtro no banner (por idade, sexo, região...)?
Coloquei um rand no select e um limite maximo de 20 banners. Para q no caso, se meu sistema tiver muitos registros a execução ñ se torne lenta. Assim, mesmo q o mysql me retorne sempre os mesmos 20 banners durante um tempo, ainda assim, terei 1 entre 20 pra escolher com um outro rand, só q dessa vez fora do select.

<?
$sqlb = mysql_query("SELECT id_banner FROM banner [filtro] order by rand() limit 20");
$tt = mysql_num_rows($sqlb);
$num = rand(0,$tt-1);
while($resultado = mysql_fetch_array($sqlb)) {
$banner[] = $resultado[id_banner];
}
$sqlb = mysql_query("SELECT * FROM banner where id_banner = '".$banner[$num]."'");
?>
29/09/2005 7:41am (~11 anos atrás)

Usando o exemplo do Celso, combinado com o exemplo do assombração funcionou perfeitamente.
09/12/2003 11:51am (~13 anos atrás)

Essa rotina é muito boa, porém tem falhas. Ele faz a contagem do total de records, ou seja, ele da um numero inteiro. Se vc esta usando auto increment, logo terá problemas. Pq? Pq no dia que você excluir um record da tabela vc pode retornar um record inexistente. Exemplo:

total q vc tem 51 records
Remove id 15
Logo vc tem 50 records
Usando a função vc poderá gerar um valor aleatório de 0 a 50, ou seja, pode retornar o id que foi excluido.

Para solucionar basta testar a existencia do id retornado. Caso não exista um location com PHP_SELF resolve, ok!? É uma solução porém pode tornar o script lento.
28/08/2003 7:32pm (~13 anos atrás)

Miguel Lohan disse:
Estou tendo este mesmo problema, estou gerando 6 números aleatórios e vou buscar através de uma select, na verdade 6, hehehhe

<?
$condicao = 0;
for ($x=0;$x<6;$x++)
{
mt_srand ((double) microtime() * 1000000);
do
{
$bloqueia = 0;
$numerorand = mt_rand(1,48);
$numerorandarray[$x] = $numerorand;
if ($condicao != 0)
{
for ($loop=0;$loop<sizeof($numerorandarray)-1;$loop++)
{
if ($numerorandarray[$loop] == $numerorand)
{
$bloqueia++;
}
}
}
$condicao=1;
}
while ($bloqueia > 0);
echo $numerorandarray[$x]."<br>";
}

?>
26/08/2003 7:09pm (~13 anos atrás)

Bom digamos que ja tenha sido feito a conexao com o banco de dados....o rand tem uma pequena alteracao

<?
$sqlb = mysql_query("SELECT * FROM banner_pequeno");
$tt = mysql_num_rows($sqlb);
$num = rand(0,$tt-1);
$sqlb = mysql_query("SELECT * FROM banner_pequeno ORDER BY BANNER_ID ASC LIMIT ".$num.",1");
$resultado = mysql_fetch_array($sqlb);
echo "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0' width='192' height='110'><param name='movie' value='$resultado[2]'><param name='quality' value='high'><embed src='$resultado[2]' quality='high' pluginspage='http://www.macromedia.com/go/getflashplayer&#039; type='application/x-shockwave-flash' width='192' height='110'></embed></object>";
?>
26/08/2003 10:30am (~13 anos atrás)

Celso,

ultima coisa

em vez de colocar
$q = "select * from banners";

faça:

$q = "select count(nome_algum_campo) AS total from banners";
$dado = mysql_query($q);
$dado = mysql_fetch_array($dado);
$total = $dado["total"];

pois é muito mais eficiente, imagine se tiver 500 registro de banners!!!!
Você estaria retornando todos!
Você estaria
18/08/2003 9:04am (~13 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)