+5

Fazendo paginação de resultados aleatórios com MySQL

criado por Adriano de Oliveira Gonçalves em 17/02/2006 7:42am
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:

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.

Comentários:

Mostrando 1 - 10 de 17 comentários
Lucas Marques disse:
muito bom mesmo
eu arrumeei certinho aki e deu tudo certo
valeu
06/05/2008 12:24pm (~16 anos atrás)

Valeu Sam, gostei da idéia da sessão...
12/07/2007 4:17pm (~17 anos atrás)

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.
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
01/05/2007 2:03pm (~17 anos atrás)

It...Zhak disse:
Me ajudou muito.
12/09/2006 12:06pm (~17 anos atrás)

Desculpe a demora em responder... muito obrigado pelo elogio!

Abraços,

Adriano
03/08/2006 4:44am (~17 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
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:

$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)

kennedy paulo disse:
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)

Novo Comentário:

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