Fazendo paginação de resultados aleatórios com MySQL
1. Extraindo uma listagem aleatória do MySQL
Bem, primeiro vamos ver como se extrai uma listagem aleatória no MySQL. De acordo com o manual do MySQL, existe uma função RAND(), que gera um número aleatório. A partir da versão 3.23, você pode fazer:
Pronto; até aí já podemos ter uma listagem aleatória. Se eu quiser apenas três registros, por exemplo, escolhidos aleatoriamente, da tabela, basta fazer:
2. Um script de paginação
Agora, vou mostrar aqui um algoritmo de paginação que, se não me engano, peguei no phpbrasil.com quando comecei a programar em PHP. Aí eu mexi, aperfeiçoei, converti pra outras liguagens, converti pra DB_dataobject do PEAR e por aí vai; e utilizo até hoje. A tabela que vou utilizar utiliza a estrutura abaixo:
E abaixo, o código de paginação em PHP:
Ok, até aí já temos os resultados sendo exibidos em páginas:
Produto: Bola
Produto: Chuveiro
Produto: CD
Produto: prod3
Produto: pp2pp2p
1 2 3 próxima
(Peraí, mas os resultados não estão aleatórios!...) Calma, vamos chegar lá...
3. A paginação aleatória
Agora, vamos fazer uma pequena alteração no script para exibirmos os resultados de forma aleatória. Para isso, basta fazer um alteração na query:
Note que agora os resultados são exibidos totalmente desordenados. Cada vez que você entra na primeira página, ela aparece em uma seqüência diferente, e quando vai para a segunda, da mesma maneira, aparecendo inclusive registros que você já viu:
Ex. de página 1:
Produto: pp2pp2p
Produto: prod3
Produto: ggg
Produto: CD
Produto: M&Ms
1 2 3 próxima
Ex. de página 2 :
Produto: dasdA
Produto: pp2pp2p ->repetido
Produto: DQWDQ
Produto: ggg -> repetido
Produto: M&Ms -> repetido
anterior 1 2 3 próxima
Para resolver isso, vamos utilizar um princípio de programação de números aleatórios: o uso da "semente" (seed, em Inglês). Resumindo, uma semente é um valor que serve para gerar uma seqüência de números aleatórios que será sempre a mesma, uma vez que a semente for a mesma. Por exemplo, suponhamos que, ao utilizar o número 7 como semente eu obtivesse, em uma seqüência de números aleatórios, o seguinte resultado:[ 8, 5, 6, 3]. Significa que sempre que eu chamar a mesma função de geração de números com a semente 7, vou obter o mesmo resultado: [8, 5, 6, 3]. Se eu usar semente 6, por exemplo, ou qualquer outra, talvez obtenha [6, 1, 2, 4] ou qualquer outra seqüência. Entendido?
Bom, a função RAND() do MySQL aceita um parâmetro como semente (ah... estou começando a entender aonde você quer chegar...). Para isso, basta utilizar RAND(N), onde "N" é um número inteiro. Na query que utilizamos, significa dizer que, sempre que eu chamar "SELECT * FROM produtos WHERE EXIBIR=1 ORDER BY RAND(7)", vou obter:
Na página 1:
Produto: CD
Produto: DQWDQ
Produto: M&Ms
Produto: Chuveiro
Produto: qDWQ
1 2 3 próxima
Na página 2:
Produto: ggg
Produto: dasdA
Produto: efrfefwe
Produto: Mais produto
Produto: prod3
Na página 3:
Produto: pp2pp2p
Produto: Bola
anterior 1 2 3
Obs.: A seqüência escolhida pelo computador depende da função implementada pela versão da linguagem, do sistema operacional e das condições que a função analisa pra gerar os números.
E não vai haver mais aquela confusão. (Sim, mas aí perde a graça, porque você vai ter sempre a mesma ordem!) Sim, é verdade, então podemos dar uma melhorada: vamos fazer de forma que, a primeira vez que a pessoa entrar na página, o programa gere aleatoriamente, via PHP, uma semente e passe a utilizar ela para as outras páginas. Farei isso passando a semente via url. O script vai ficar assim:
Pode testar; o resultado é bem interessante. Além disso, se você quiser fazer com PostgreSQL também dá. A diferença é que a função de número aleatório no PostgreSQL é random(); e para inserir uma semente é necessário enviar antes o comando "SET SEED=N", aonde "N" é um valor float entre 0 e 1. Em algumas versões, pode ser utilizado ao invés disso, "SELECT setseed(N)". O LIMIT no PosgreSQL também é diferente. Utiliza-se LIMIT A OFFSET B, aonde A e B são números inteiros (http://www.postgresql.org/docs/8.0/static/queries-limit.html). Aí vai ser necessário fazer umas alteraçõezinhas no código, mas agora que você já aprendeu, fica fácil...
Estou aberto a comentários, sugestões e dúvidas.
Bem, primeiro vamos ver como se extrai uma listagem aleatória no MySQL. De acordo com o manual do MySQL, existe uma função RAND(), que gera um número aleatório. A partir da versão 3.23, você pode fazer:
SELECT * FROM nome_tabela ORDER BY RAND()
Pronto; até aí já podemos ter uma listagem aleatória. Se eu quiser apenas três registros, por exemplo, escolhidos aleatoriamente, da tabela, basta fazer:
SELECT * FROM nome_tabela ORDER BY RAND() LIMIT 3
2. Um script de paginação
Agora, vou mostrar aqui um algoritmo de paginação que, se não me engano, peguei no phpbrasil.com quando comecei a programar em PHP. Aí eu mexi, aperfeiçoei, converti pra outras liguagens, converti pra DB_dataobject do PEAR e por aí vai; e utilizo até hoje. A tabela que vou utilizar utiliza a estrutura abaixo:
-- -- Estrutura da tabela `produtos` -- CREATE TABLE `produtos` ( `ID` int(11) NOT NULL auto_increment, `PRODUTO` varchar(255) default '', `DESCRICAO` varchar(255) default '', `EXIBIR` char(1) NOT NULL default '1', PRIMARY KEY (`ID`) ) ; -- -- Extraindo dados da tabela `produtos` -- INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (1, 'Bola', 'Objeto esférico que visa entreterimento', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (2, 'Chuveiro', 'Objeto que serve para limpeza', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (3, 'CD', 'Objeto que serve para ouvir música', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (4, 'prod3', 'Produto 3', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (5, 'pp2pp2p', 'ipip', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (6, 'ggg', '', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (7, 'dasdA', 'ggsdf', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (8, 'DQWDQ', 'dqqdw', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (9, 'qDWQ', 'agfagraw', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (10, 'efrfefwe', '', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (11, 'Mais produto', '+++', '1'); INSERT INTO `produtos` (`ID`, `PRODUTO`, `DESCRICAO`, `EXIBIR`) VALUES (12, 'M&Ms', '', '1');
E abaixo, o código de paginação em PHP:
<? //Conexão com o banco: mysql_connect("localhost","desenvolvimento","12345"); mysql_select_db("test"); // Informações da query. No caso, "SELECT * FROM produtos WHERE EXIBIR=1" $campos_query = "*"; $final_query = "FROM produtos WHERE EXIBIR=1"; // Declaração da pagina inicial $pagina = $_GET["pagina"]; if($pagina == "") { $pagina = "1"; } // Maximo de registros por pagina $maximo = 5; // Calculando o registro inicial $inicio = $pagina - 1; $inicio = $maximo * $inicio; // Conta os resultados no total da minha query $strCount = "SELECT COUNT(*) AS 'num_registros' $final_query"; $query = mysql_query($strCount); $row = mysql_fetch_array($query); $total = $row["num_registros"]; if($total<=0) { echo "<center>Nenhum registro encontrado.</center>"; } else { $strQuery = "SELECT $campos_query $final_query LIMIT $inicio,$maximo"; $query = mysql_query($strQuery); while($row = mysql_fetch_array($query)) { echo "Produto: ".$row["PRODUTO"]."<BR>"; } // Calculando pagina anterior $menos = $pagina - 1; // Calculando pagina posterior $mais = $pagina + 1; $pgs = ceil($total / $maximo); if($pgs > 1 ) { // Mostragem de pagina if($menos > 0) { echo "<a href=\"?pagina=$menos&\" class='texto_paginacao'>anterior</a> "; } // Listando as paginas for($i=1;$i <= $pgs;$i++) { if($i != $pagina) { echo " <a href=\"?pagina=".($i)."\" class='texto_paginacao'>$i</a>"; } else { echo " <strong lass='texto_paginacao_pgatual'>".$i.""; } } if($mais <= $pgs) { echo " <a href=\"?pagina=$mais\" class='texto_paginacao'>próxima</a>"; } } } ?>
Ok, até aí já temos os resultados sendo exibidos em páginas:
Produto: Bola
Produto: Chuveiro
Produto: CD
Produto: prod3
Produto: pp2pp2p
1 2 3 próxima
(Peraí, mas os resultados não estão aleatórios!...) Calma, vamos chegar lá...
3. A paginação aleatória
Agora, vamos fazer uma pequena alteração no script para exibirmos os resultados de forma aleatória. Para isso, basta fazer um alteração na query:
<? //Conexão com o banco: mysql_connect("localhost","desenvolvimento","12345"); mysql_select_db("test"); // Informações da query. No caso, "SELECT * FROM produtos WHERE EXIBIR=1" $campos_query = "*"; $final_query = "FROM produtos WHERE EXIBIR=1"; // Declaração da pagina inicial $pagina = $_GET["pagina"]; if($pagina == "") { $pagina = "1"; } // Maximo de registros por pagina $maximo=5; // Calculando o registro inicial $inicio = $pagina - 1; $inicio = $maximo * $inicio; // Conta os resultados no total da minha query $strCount = "SELECT COUNT(*) AS 'num_registros' $final_query "; $query = mysql_query($strCount); $row = mysql_fetch_array($query); $total = $row["num_registros"]; if($total <= 0) { echo "<center>Nenhum registro encontrado.</center>"; } else { $strQuery = "SELECT $campos_query $final_query ORDER BY RAND() LIMIT $inicio,$maximo"; // ALTEREI AQUI $query = mysql_query($strQuery); while($row = mysql_fetch_array($query)) { echo "Produto: ".$row["PRODUTO"]."<BR>"; } // Calculando pagina anterior $menos = $pagina - 1; // Calculando pagina posterior $mais = $pagina + 1; $pgs = ceil($total / $maximo); if($pgs > 1 ) { // Mostragem de pagina if($menos>0) { echo "<a href=\"?pagina=$menos\" class='texto_paginacao'>anterior</a> "; } // Listando as paginas for($i=1;$i <= $pgs;$i++) { if($i != $pagina) { echo " <a href=\"?pagina=".($i)."\" class='texto_paginacao'>$i</a>"; } else { echo " <strong class='texto_paginacao_pgatual'>".$i.""; } } if($mais <= $pgs) { echo " <a href=\"?pagina=$mais\" class='texto_paginacao'>próxima</a>"; } } } ?>
Note que agora os resultados são exibidos totalmente desordenados. Cada vez que você entra na primeira página, ela aparece em uma seqüência diferente, e quando vai para a segunda, da mesma maneira, aparecendo inclusive registros que você já viu:
Ex. de página 1:
Produto: pp2pp2p
Produto: prod3
Produto: ggg
Produto: CD
Produto: M&Ms
1 2 3 próxima
Ex. de página 2 :
Produto: dasdA
Produto: pp2pp2p ->repetido
Produto: DQWDQ
Produto: ggg -> repetido
Produto: M&Ms -> repetido
anterior 1 2 3 próxima
Para resolver isso, vamos utilizar um princípio de programação de números aleatórios: o uso da "semente" (seed, em Inglês). Resumindo, uma semente é um valor que serve para gerar uma seqüência de números aleatórios que será sempre a mesma, uma vez que a semente for a mesma. Por exemplo, suponhamos que, ao utilizar o número 7 como semente eu obtivesse, em uma seqüência de números aleatórios, o seguinte resultado:[ 8, 5, 6, 3]. Significa que sempre que eu chamar a mesma função de geração de números com a semente 7, vou obter o mesmo resultado: [8, 5, 6, 3]. Se eu usar semente 6, por exemplo, ou qualquer outra, talvez obtenha [6, 1, 2, 4] ou qualquer outra seqüência. Entendido?
Bom, a função RAND() do MySQL aceita um parâmetro como semente (ah... estou começando a entender aonde você quer chegar...). Para isso, basta utilizar RAND(N), onde "N" é um número inteiro. Na query que utilizamos, significa dizer que, sempre que eu chamar "SELECT * FROM produtos WHERE EXIBIR=1 ORDER BY RAND(7)", vou obter:
Na página 1:
Produto: CD
Produto: DQWDQ
Produto: M&Ms
Produto: Chuveiro
Produto: qDWQ
1 2 3 próxima
Na página 2:
Produto: ggg
Produto: dasdA
Produto: efrfefwe
Produto: Mais produto
Produto: prod3
Na página 3:
Produto: pp2pp2p
Produto: Bola
anterior 1 2 3
Obs.: A seqüência escolhida pelo computador depende da função implementada pela versão da linguagem, do sistema operacional e das condições que a função analisa pra gerar os números.
E não vai haver mais aquela confusão. (Sim, mas aí perde a graça, porque você vai ter sempre a mesma ordem!) Sim, é verdade, então podemos dar uma melhorada: vamos fazer de forma que, a primeira vez que a pessoa entrar na página, o programa gere aleatoriamente, via PHP, uma semente e passe a utilizar ela para as outras páginas. Farei isso passando a semente via url. O script vai ficar assim:
<? //Conexão com o banco: mysql_connect("localhost","desenvolvimento","12345"); mysql_select_db("test"); // Informações da query. No caso, "SELECT * FROM produtos WHERE EXIBIR=1 ORDER BY RAND()" $campos_query = "*"; $final_query = "FROM produtos WHERE EXIBIR=1"; // Declaração da pagina inicial $pagina = $_GET["pagina"]; if($pagina == "") { $pagina = "1"; } // Maximo de registros por pagina $maximo = 5; // Calculando o registro inicial $inicio = $pagina - 1; $inicio = $maximo * $inicio; // Conta os resultados no total da minha query $strCount = "SELECT COUNT(*) AS 'num_registros' $final_query"; $query = mysql_query($strCount); $row = mysql_fetch_array($query); $total = $row["num_registros"]; if($total <= 0) { echo "<center>Nenhum registro encontrado.</center>"; } else { if(!isset($_GET["seed"])) { $seed = rand(); // Caso ainda não exista uma semente, cria a semente via PHP. } else { $seed = addslashes($_GET["seed"]); // Caso já exista uma semente, utiliza a que foi passada na url. (o addslashes é por questão de segurança) } $strQuery = "SELECT $campos_query $final_query ORDER BY RAND($seed) LIMIT $inicio,$maximo"; $query = mysql_query($strQuery); while($row = mysql_fetch_array($query)) { echo "Produto: ".$row["PRODUTO"]."<BR>"; } // Calculando pagina anterior $menos = $pagina - 1; // Calculando pagina posterior $mais = $pagina + 1; $pgs = ceil($total / $maximo); if($pgs > 1 ) { // Mostragem de pagina if($menos>0) { echo "<a href=\"?pagina=$menos&seed=$seed\" class='texto_paginacao'>anterior</a> "; } // Listando as paginas for($i=1;$i <= $pgs;$i++) { if($i != $pagina) { echo " <a href=\"?pagina=".($i)."&seed=$seed\" class='texto_paginacao'>$i</a>"; } else { echo " <strong class='texto_paginacao_pgatual'>".$i.""; } } if($mais <= $pgs) { echo " <a href=\"?pagina=$mais&seed=$seed\" class='texto_paginacao'>próxima</a>"; } } } ?>
Pode testar; o resultado é bem interessante. Além disso, se você quiser fazer com PostgreSQL também dá. A diferença é que a função de número aleatório no PostgreSQL é random(); e para inserir uma semente é necessário enviar antes o comando "SET SEED=N", aonde "N" é um valor float entre 0 e 1. Em algumas versões, pode ser utilizado ao invés disso, "SELECT setseed(N)". O LIMIT no PosgreSQL também é diferente. Utiliza-se LIMIT A OFFSET B, aonde A e B são números inteiros (http://www.postgresql.org/docs/8.0/static/queries-limit.html). Aí vai ser necessário fazer umas alteraçõezinhas no código, mas agora que você já aprendeu, fica fácil...
Estou aberto a comentários, sugestões e dúvidas.
Muito bom o script!!! Desconhecia a função RAND do mysql.
Entretanto notei que são sempre mostrados resultados aleatórios quando apertamos o botão de voltar e avançar no browser... No meu caso em vez de recuperar o seed pela URL, preferi colocar na sessão.
Entretanto notei que são sempre mostrados resultados aleatórios quando apertamos o botão de voltar e avançar no browser... No meu caso em vez de recuperar o seed pela URL, preferi colocar na sessão.
12/07/2007 11:56am
(~17 anos atrás)
Cara, parabéns pelo artigo!
Eu fiz algumas modificações na maneira de lincar pq estava usando templates mais o script foi perfeito, mais uma vez PARABENS.
Valeu
Eu fiz algumas modificações na maneira de lincar pq estava usando templates mais o script foi perfeito, mais uma vez PARABENS.
Valeu
01/05/2007 2:03pm
(~17 anos atrás)
Desculpe a demora em responder... muito obrigado pelo elogio!
Abraços,
Adriano
Abraços,
Adriano
03/08/2006 4:44am
(~18 anos atrás)
de fato.. esse eh um dos melhores artigos q eu li por aqui.. mta gente desconhece o uso do seed (eu mesmo nao sabia que existia isso).. parabens ao autor do artigo.. sera de grande ajuda..
19/07/2006 6:18am
(~18 anos atrás)
Esse é o link do Manual do PHP em chm: http://www.php.net/get/php_manual_pt_BR.chm/from/a/mirror
Espero ter sido útil.
Abraços,
Adriano
Espero ter sido útil.
Abraços,
Adriano
16/04/2006 6:58pm
(~18 anos atrás)
Bom, pra fazer tudo você vai ter que pesquisar um pouquinho e ir aprendendo. Recomendo baixar o Manual do PHP em chm (Windows Help), que eu diria que é uma das melhores maneiras de aprender e se aperfeiçoar em PHP (www.php.net).
Mas, considerando que vc já tenha uma galeria pronta e as imagens estejam nessa tabela, o esquema de paginação seguiria da mesma forma, e a query e a listagem você faria com os campos que você tiver criado.
Para a busca pode criar um formulário pra submeter a pesquisa com um campo do tipo text que vamos dar o nome de "busca" (name="busca"). A partir daí, precisamos nos preocupar apenas em acrescentar esse conteúdo da busca na query e passá-la nos links da paginação. O final da query poderia ficar assim:
E aquele finalzinho...:
Mas, considerando que vc já tenha uma galeria pronta e as imagens estejam nessa tabela, o esquema de paginação seguiria da mesma forma, e a query e a listagem você faria com os campos que você tiver criado.
Para a busca pode criar um formulário pra submeter a pesquisa com um campo do tipo text que vamos dar o nome de "busca" (name="busca"). A partir daí, precisamos nos preocupar apenas em acrescentar esse conteúdo da busca na query e passá-la nos links da paginação. O final da query poderia ficar assim:
$final_query = "FROM fotos WHERE EXIBIR=1 AND descricao LIKE '%".addslashes($_GET["busca"])."%'";
E aquele finalzinho...:
if($pgs > 1 ) { // Mostragem de pagina if($menos>0) { echo "<a href=\"?pagina=$menos&seed=$seed&busca=".$_GET["busca"]."\" class='texto_paginacao'>anterior</a> "; } // Listando as paginas for($i=1;$i <= $pgs;$i++) { if($i != $pagina) { echo " <a href=\"?pagina=".($i)."&seed=$seed&busca=".$_GET["busca"]."\" class='texto_paginacao'>$i</a>"; } else { echo " <strong class='texto_paginacao_pgatual'>".$i.""; } } if($mais <= $pgs) { echo " <a href=\"?pagina=$mais&seed=$seed&busca=".$_GET["busca"]."\" class='texto_paginacao'>próxima</a>"; } }
16/04/2006 6:55pm
(~18 anos atrás)
sou iniciante, queria saber como eu posso fazer uma pagina de busca em minha galeria de fotos, como onde colocar esses comandos ai, kennedipaulo@hotmail.com esse e meu email entra em contato ai mim ajuda ai cara falow...
16/04/2006 3:28pm
(~18 anos atrás)
eu arrumeei certinho aki e deu tudo certo
valeu