¿Quieres registrarte?

Paginación con PHP y MySQL

Por: Maikel
24 de Septiembre del 2009
4663 de clabLevel
Otros artículos de Maikel
5,827 visitas

Un caso típico en el desarrollo web con PHP y MySQL es la funcionalidad de paginar resultados, si bien no es algo dificil muchos desperdician la potencialidad y malgastan los recursos de MySQL.

Para paginar resultados necesitamos saber 3 valores:

  1. Número de Página: este valor lo pide el usuario, asi que es fácil obtenerlo.
  2. Total de registros por página: normalmente lo define el programador con una constante
  3. Total de registros: necesitamos saber el total de registros para poder crear la cantidad de enlaces necesarios por página.

Donde se desperdicia el potencial y malgasta recurso de MySQL normalmente es en el 3 valor. Pues muchos ejecutan una consulta con LIMIT y otra consulta con un COUNT sin el LIMIT. Y es allí donde es el error, ya que MySQL ofrece las funciones: SQL_CALC_FOUND_ROWS y FOUND_ROWS.

Aunque con estas funciones debemos igual ejecutar dos consultas, el calculo y tiempo de respuesta es mucho más rápido que haciéndolo con COUNT.

Ejemplo:

Código :

<?php

$link = @mysql_connect("localhost", "usuario", "password");
mysql_select_db("base_de_datos", $link);

// maximo por pagina
$limit = 5;

// pagina pedida
$pag = (int) $_GET["pag"];
if ($pag < 1)
{
   $pag = 1;
}
$offset = ($pag-1) * $limit;


$sql = "SELECT SQL_CALC_FOUND_ROWS id, nombre FROM tabla LIMIT $offset, $limit";
$sqlTotal = "SELECT FOUND_ROWS() as total";

$rs = mysql_query($sql);
$rsTotal = mysql_query($sqlTotal);

$rowTotal = mysql_fetch_assoc($rsTotal);
// Total de registros sin limit
$total = $rowTotal["total"];

?>

<table border="1" bordercolor="#000">
   <thead>
      <tr>
         <td>Id</td>
         <td>Nombre</td>
      </tr>
   </thead>
   <tbody>
      <?php
         while ($row = mysql_fetch_assoc($rs))
         {
            $id = $row["id"];
            $name = htmlentities($row["nombre"]);
         ?>
         <tr>
            <td><?php echo $id; ?></td>
            <td><?php echo $name; ?></td>
         </tr>
         <?php
         }
      ?>
   </tbody>
   <tfoot>
      <tr>
         <td colspan="2">
      <?php
         $totalPag = ceil($total/$limit);
         $links = array();
         for( $i=1; $i<=$totalPag ; $i++)
         {
            $links[] = "<a href=\"?pag=$i\">$i</a>"; 
         }
         echo implode(" - ", $links);
      ?>
         </td>
      </tr>
   </tfoot>
</table>


Advertencia:

Enviar a twitter Enviar a facebook


También te interesa


Etiquetas php bases_de_datos mysql

Comentarios | Enviar un comentario
$offset = ($pag-1) * $maxPag; no sería?
$offset = ($pag-1) * $limit;
Por: Anonimo-blog
Muy buen tip Maik. :)

Sobretodo por lo de SQL_CALC_FOUND_ROWS, muchos lo desconocen.


saludos
Por: Dano-blog
Gracias Maikel!, no tenia idea de que existían esas funciones. A aplicar de ahora en adelante.
Por: -george-
Excelente Aporte... Deberíamos ir conociendo mas las consultas de MySql a solo las funciones de PHP.. Excelente Hermano xD
Por: JoseAlejandro_Realza
Si Anonimo-blog, tiene razon, asi es.
Por: toffman-blog
aunque no me andan los links a mi. porque sera?? osea los muestra pero en la url queda "?pag=" sin valores
Por: Toffman-blog
ha si lo que pasa es que no le pasa la variable $i; asi quedaria bien -> $links[] = "<a href="?pag=$i">$i</a>";
Por: toffman-blog
Cambiar

Código :

$offset = ($pag-1) * $maxPag; 

por

Código :

$offset = ($pag-1) * $limit;



Cambiar:

Código :

$links[] = "<a href=\"?pag=\">$i</a>"; 

por

Código :

$links[] = "<a href=\"?pag=$i\">$i</a>"; 



Es lo que pasa por escribir un tipo a las 3am...

saludos
Por: Maikel
maki podes explicar para que es SQL_CALC_FOUND_ROWS, Grcias
Por: jpcw-blog
SQL_CALC_FOUND_ROWS: calcula el número de resultados de una consulta sin LIMIT.
FOUND_ROWS: obtiene el resultado del último SQL_CALC_FOUND_ROWS ejecutados;

saludos
Por: Maikel
Gracias por compartir es tip Maikel
Por: psycho-vnz
muy buen aporte =)
cuando tenga tiempo lo pruebo ^^
Por: Jinik
Interesante aporte Maikel, justamente estaba necesitando algo similar... ^^
Por: Blackdragon
Ya está el tip actualizado con los cambios sugeridos por Maikel.
Por: Freddie
yo "paginó" con DataTables (table plug-in for jQuery)

lo recomiendo, te coloca casilla de busqueda, numero de registros por página (5,10, 25, 100)
Por: Calviche82-blog
Tiempo trabajando con MySQL y no conocía el SQL_CALC_FOUND_ROWS, uno siempre aprende algo nuevo. Excelente tip como siempre :)
Por: Aoyama
Maikel

Hola man!!, Bueno yo que soy un poco compulsivo con los errores esta línea te puede producir un warning

Maikel :


Código :

$pag = (int) $_GET["pag"];

Si no se a enviado la variable pag, ya sea por inicialización de la página o por que el usuario la borro. Yo aconsejaría verificarla algo como esto>

Código :

if (isset($_GET['pag'])){
   $pag = (int) $_GET["pag"]; 
   if ($pag < 1){ 
            $pag = 1; 
   } 
}else{
    $pag = 1;
}
También cabría notar, aunque no se si no lo vi, que pasaría si el usuario manda un valor de página superior a las páginas que se pueden generar, es decir, si el usuario manda pag=100, y solo se pueden generar 5 páginas, se generaria un error en la consulta, pero este se puede arreglar fácilmente comparando que la página enviada no sea mayor a las páginas que se generan de la consulta. Buena esas son mis sugerencias

Muy buen Tip Maikel :)
Por: esutoraiki

esutoraiki :


Maikel :


Código :

$pag = (int) $_GET["pag"];

Si no se a enviado la variable pag, ya sea por inicialización de la página o por que el usuario la borro. Yo aconsejaría verificarla algo como esto>

Código :

if (isset($_GET['pag'])){
   $pag = (int) $_GET["pag"]; 
   if ($pag < 1){ 
            $pag = 1; 
   } 
}else{
    $pag = 1;
}


Que tal así?

Código :

$pag = (int) @$_GET["pag"];

y me dejas lo demás como está

esutoraiki :

También cabría notar, aunque no se si no lo vi, que pasaría si el usuario manda un valor de página superior a las páginas que se pueden generar, es decir, si el usuario manda pag=100, y solo se pueden generar 5 páginas, se generaria un error en la consulta, pero este se puede arreglar fácilmente comparando que la página enviada no sea mayor a las páginas que se generan de la consulta. Buena esas son mis sugerencias


La consulta no generará error, simplemente no retornará registros.
Por: Maikel
Para ser más quisquillosos aún! :P

Código :

$pag = ( isset($_GET["pag"]) and ($_GET["pag"] > 0) and ($_GET["pag"] <= $totalPag) ? $pag = $_GET["pag"] : $pag = 1;


Se podría "comprobar" más el dato, is_int, etc...
Obviamente sería mucho más entendible con un If común en vez del ternario.

El error o no de si está seteada $_GET["pag"] dependería de la configuración de PHP. Por lo general los hostings compartidos y algunos que no le ponen enfasís en el detalle no tirarían error, un dedicado masomenos bueno seguramente.

Pero bueno hablando del tip, muy bueno! nunca lo había escuchado/leído el CALC ROWS, grax por el aporte Maikel!.
Por: raxiro
Maikel! sos un genio!! Mil millones de aplausos!! Gracias!!!!
Por: Andrés-blog
me gustó el tip ^^ Felicidades :)
Por: eldervaz
Hola man esta bueno esto `, pero me genera un problema es que el paginado sale impreso completo osea un link para cada pagina :S
que hago
Por: malditosan-blog
joda men me le quito el sombrero, este codigo si me sirvio, lo felicito
Por: jhonny-blog
Hola . Quiero decir que inclui el código y sin embargo, a pesar de mostrar la tabla con los links a las distintas paginas, siempre, en cada pagina, me tira todos los registros (puse 50 para probar).
ASi que quería saber porque no puedo limitarlos a 5.
Luego si, la tabla marca 10 paginas (correspondientes a 5*10=50 registros) o sea que esa parte esta ok,
¿Se puede solucionar?
Gracias!
Por: Oscar-blog
me sale este error Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in /var/www/html/php/paginacion/paginacion.php on line 28


el problema es id_categoria=$_GET[id_categoria]
pero lo quiero ordenar por categoria


// maximo por pagina
$limit = 4;

// pagina pedida
$pag = (int) $_GET["pag"];
if ($pag < 1)
{
$pag = 1;
}
$offset = ($pag-1) * $limit;


$sql = "SELECT SQL_CALC_FOUND_ROWS nombre_categoria,nombre_producto,ruta_foto_grande,ruta_foto_pequena,descripcion_producto FROM producto,foto,categoria where id_foto=producto_id_foto and id_categoria=producto_id_categoria and id_categoria=$_GET[id_categoria] group by nombre_producto LIMIT $offset, $limit";
$sqlTotal = "SELECT FOUND_ROWS() as total";

$rs = mysql_query($sql);
$rsTotal = mysql_query($sqlTotal);

$rowTotal = mysql_fetch_assoc($rsTotal);
// Total de registros sin limit
$total = $rowTotal["total"];

$nombre_categoria=0;
while ($row = mysql_fetch_assoc($rs))
{
echo "<div class='producto'>";
if($nombre_categoria==0){
echo " <div class='categoria'><h1>".ucfirst("{$row['nombre_categoria']}")."</h1></div>";
}
echo "<div class='nombre'><h2>{$row['nombre_producte']}</h2></div>
<div class='imagen'>
<a href=' {$row['ruta_foto_grande']}' /><img src=' {$row['ruta_foto_pequena']} '/></a>
<div class='ampliar'>
<p><a href=' {$row['ruta_foto_grande']}' />ampliar</a></p></div>
</div>
<div class='descripcion'><p>{$row['descripcion_producto']}</p></div></br></br></br><hr></div></br> ";
$nombre_categoria++;
}



$totalPag = ceil($total/$limit);
$links = array();
for( $i=1; $i<=$totalPag ; $i++)
{
$links[] = "<a href=\"?id_categoria=$_GET[id_categoria]?pag=$i\">$i</a>";
}
echo implode(" - ", $links);
?>
Por: albertrc
Holas amigo Bueno soyy novato en esto asi que sin animos de querer molestarlos, me podrian decir cuales serian los campos que deberia cambiar de acuerdo a mi bd X)
Por: Carlos-blog
Deja un comentario
IMPORTANTE

Recuerda ser respetuoso, no insultes a otras personas, ni uses palabrotas, hay una persona al otro lado de la pantalla.

Habla bien, NO ESCRIBAS EN MAYUSCULA TODO, no escribas como en un SMS, evita cosas como "ke", "x q" y demás abreviaciones.

Aquí funcionan las etiquetas de los foros, puedes usar [b] para negrita, [img] para las imágenes, [url] para los enlaces, etc.

Si tienes preguntas técnicas, envíalas mejor al foro.