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:
- Número de Página: este valor lo pide el usuario, asi que es fácil obtenerlo.
- Total de registros por página: normalmente lo define el programador con una constante
- 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.
- 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 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:
- 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.

Por Anonimo el 24 de Septiembre de 2009
$offset = ($pag-1) * $limit;
Por Dano el 24 de Septiembre de 2009
Sobretodo por lo de SQL_CALC_FOUND_ROWS, muchos lo desconocen.
saludos
Por -george- el 24 de Septiembre de 2009
Por toffman el 24 de Septiembre de 2009
Por Toffman el 24 de Septiembre de 2009
Por toffman el 24 de Septiembre de 2009
Código :
por
Código :
Cambiar:
Código :
por
Código :
Es lo que pasa por escribir un tipo a las 3am...
saludos
Por jpcw el 24 de Septiembre de 2009
FOUND_ROWS: obtiene el resultado del último SQL_CALC_FOUND_ROWS ejecutados;
saludos
cuando tenga tiempo lo pruebo
Por Calviche82 el 24 de Septiembre de 2009
lo recomiendo, te coloca casilla de busqueda, numero de registros por página (5,10, 25, 100)
Hola man!!, Bueno yo que soy un poco compulsivo con los errores esta línea te puede producir un warning
Maikel :
Código :
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 sugerenciasMuy buen Tip Maikel
esutoraiki :
Maikel :
Código :
Código :
if (isset($_GET['pag'])){ $pag = (int) $_GET["pag"]; if ($pag < 1){ $pag = 1; } }else{ $pag = 1; }Que tal así?
Código :
y me dejas lo demás como está
esutoraiki :
La consulta no generará error, simplemente no retornará registros.
Por raxiro el 26 de Septiembre de 2009
Código :
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 Andrés el 26 de Septiembre de 2009
Por malditosan el 15 de Octubre de 2009
que hago
Por jhonny el 14 de Noviembre de 2009
Por Oscar el 21 de Noviembre de 2009
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 albertrc el 22 de Enero de 2010
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 Carlos el 15 de Febrero de 2010
Por alejandro el 30 de Marzo de 2010
Gracias
Por Radamanthys el 22 de Abril de 2010
Por lool el 11 de Mayo de 2010
Por podrian hacer un lis el 15 de Mayo de 2010
Por Tomás el 02 de Junio de 2010
Por alexx855 el 14 de Junio de 2010
una pregunta simple!
como seria para q los resultados aparescan al reves???? osea enves de comensar por 1,2,3,4 q sea 4,3,2,1
invertido
en mi caso em sirve para q los ids q voy agregando aparescan en primer lugar
Por jorge uribe el 18 de Junio de 2010
2sql_calc_found_rows" en mis querys y cuando ejecuto el "found_rows()" me devuelve un valor 1 o 2, no se que es lo que me falla, apreciaria cualquier tipo de ayuda.
gracias de antemano
Por Elmer el 30 de Junio de 2010
Como podría hacer para que en vez de imprimir sobre la pagina la numeración con formato 1-2-3-4-5-... . Lo imprima en una lista en la que pueda yo seleccionar la pagina que quiero visualizar.
Lo que pasa es que en caso de que tenga 1000 registros, entonces se visualizara de la siguiente forma 1-2-3-4-...(hasta el 200)-198-199-200.
O que es lo que podria hacer.
gracias.
Por Programador el 08 de Julio de 2010
Estoy intentando hacer lo mismo, pero FOUND_ROWS() siempre me devuelve 1, da igual el número de registros del resultado, siempre sale 1 como número total de registros.
No se si para que esto funcione depende de la version de php o mysql, o hay que modificar algo en php.ini.
¿Le ocurre esto mismo a alguien más?
Por Ariel el 28 de Julio de 2010
Muy bueno el codigo, pero tengo una duda.
Como se podria hacer para en vez de poner numeros del 1 en adelante, se pueda poner solo 4 botones (Primero, Anterior, Siguiente, Ultimo)
Si alguien lo sabe seria de mucha ayuda poder orientarme a traves de estos comentarios.
Por Ana Lucia el 16 de Noviembre de 2010
Por Marlon Giron el 16 de Noviembre de 2010
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Documento sin título</title>
</head>
<body>
<form action="paginacion_2.php" method="post">
<center>
<table>
<tr>
<td>IDDistribuidora:</td><td><input type="text" name="distribuidora" id="distribuidora" /></td>
</tr>
<tr>
<td>mes:</td><td><input type="text" name="mes" id="mes" size="4" /></td>
</tr>
<tr>
<td>Año:</td><td><input type="text" name="anio" id="anio" size="4" /></td>
</tr>
</table>
<input type="submit" value="Aceptar" />
</center>
</form>
<?php
$localhost = "localhost";
$user = "root";
$pass = "manager2010";
$conexion = mysql_connect("localhost", "root", "manager2010") or die ("Lo siento no se pudo conectar a la BD ".mysql_error());
mysql_select_db("db_calidad",$conexion);
$distribuidora = $_POST['distribuidora'];
$mes = $_POST['mes'];
$anio = $_POST['anio'];
if(isset($_POST['distribuidora'])){
$limit = 25;
$pag = (int) @$_GET['pag'];
if($pag < 1){
$pag = 1;
}
$offset = ($pag - 1) * $limit;
$sql = "Select SQL_CALC_FOUND_ROWS idusuario, nroreclamo from reclamos_quejas where mes = '$mes' and anio = '$anio' and iddistribuidora = '$distribuidora' LIMIT $offset, $limit";
$sqltotal = "Select FOUND_ROWS() as total";
$rs = mysql_query($sql);
$rstotal = mysql_query($sqltotal);
$rowtotal = mysql_fetch_assoc($rstotal);
$total = $rowtotal["total"];
?>
<table border="1" bordercolor="#000">
<thead>
<tr>
<td>IDusuario</td>
<td>Número Reclamo</td>
</tr>
</thead>
<tbody>
<?php
while($row = mysql_fetch_assoc($rs)){
$idusuario = $row["idusuario"];
$nroreclamo = htmlentities ($row["nroreclamo"]);
?>
<tr>
<td>
<?php echo $idusuario;?>
</td>
<td> <?php echo $nroreclamo;?>
</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>
</body>
</html>
Por Jefer el 12 de Mayo de 2011
Por Bethox el 27 de Mayo de 2011
Por Nor el 07 de Julio de 2011
Gracias por compartir este código. Me ha servido de mucho.
Mi duda es si se puede quedar el link, del número de las páginas no activas, con "underline" y la activa sin ella (y sin que aparezca la "mano" de link activo). Es que a mi me salen todos los links activos, estés o no en la página.
Saludos.
Por Ricardo el 21 de Julio de 2011
Por Luciano el 29 de Agosto de 2011
como se haria con esta paginación un Anterior Siguiente?
Saludos y gracias!
Por Luciano el 29 de Agosto de 2011
<?php
if ($pag != 1){
?>
<a href="index.php?pag=<?php echo $pag-1; ?>">Anterior</a>
<?php
}
$PagUlt=$total/$limit;
$Res=$total%$limit;
if($Res>0) $PagUlt=floor($PagUlt)+1;
if ($pag<$PagUlt){
?>
<a href="index.php?pag=<?php echo $pag+1; ?>">Siguiente</a>
<?
}
?>
Por karlos el 17 de Noviembre de 2011
Por airwolf97 el 23 de Noviembre de 2011
Por Omsystems el 10 de Enero de 2012
Por jesus ivan el 12 de Enero de 2012