viernes, 30 de septiembre de 2011

Paginación con PHP y MySQL



Un caso típico en el desarrollo web con PHP y MySQL es la funcionalidad de paginar resultados, si bien no es algo difícil 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, así 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 yFOUND_ROWS.

·         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.

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

Ejemplo

Código:

<?php

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

// máximo por página
$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:

·         En la configuración de PHP (php.ini) el valor mysql.trace_mode debe estar en Off. Si está en On por cada consulta que se haga a MySQL se ejecuta un explain y esto hace sql_calc_found_rows inutil, por ende, found_rows siempre retornara 0. Este valor normalmente está en off, pero cabe la advertencia.