PHP MySQL SELECT con LIMIT: Guía completa
Aprende a usar la cláusula LIMIT en MySQL con PHP para recuperar un número específico de registros y construir paginación segura con sentencias preparadas.
En una base de datos a menudo solo se necesita una porción de una tabla — las diez publicaciones más recientes, la primera página de resultados de búsqueda, un único "mejor puntuador" — en lugar de todas las filas. La cláusula LIMIT en una sentencia SELECT de MySQL limita la cantidad de filas que devuelve la consulta. Combinada con un OFFSET, es la base de la paginación: servir grandes conjuntos de resultados de a una página a la vez, en lugar de cargar miles de filas en memoria.
Este capítulo cubre la sintaxis de LIMIT, el OFFSET para omitir filas, cómo construir paginación de forma segura con sentencias preparadas y los errores comunes que suelen cometerse. Se asume que ya sabes conectarte a MySQL y ejecutar un SELECT básico.
Sintaxis de SELECT con LIMIT
La forma básica establece un número máximo de filas a retornar:
SELECT column1, column2, ... FROM table_name LIMIT row_count;Para también omitir filas desde el inicio del conjunto de resultados, agrega un offset. MySQL admite dos formas equivalentes:
SELECT ... FROM table_name LIMIT offset, row_count; -- offset first
SELECT ... FROM table_name LIMIT row_count OFFSET offset; -- explicit OFFSETQué significa cada parte:
row_count— el número máximo de filas a retornar.offset— cuántas filas omitir antes de comenzar a retornar filas. El offset es basado en cero, por lo queOFFSET 0comienza en la primera fila.LIMIT 10, 5retorna 5 filas comenzando después de las primeras 10 (es decir, las filas 11–15). La misma consulta se lee de forma más natural comoLIMIT 5 OFFSET 10.
LIMITsinORDER BYno es determinista. Sin unORDER BYexplícito, MySQL puede devolver las filas en cualquier orden, por lo que "las primeras 2 filas" pueden diferir entre ejecuciones. Siempre combinaLIMITconORDER BYcuando el orden de las filas importa.
Ejemplo: recuperar las primeras filas
Considera una tabla llamada students:
+----+---------+--------+-------+
| id | name | class | marks |
+----+---------+--------+-------+
| 1 | John | 10 | 90 |
| 2 | Michael | 9 | 85 |
| 3 | Jessica | 8 | 80 |
| 4 | Sarah | 10 | 88 |
| 5 | David | 9 | 72 |
+----+---------+--------+-------+Para obtener los primeros dos estudiantes (ordenados por id para que el resultado sea estable):
<?php
$conn = mysqli_connect("localhost", "username", "password", "database");
$query = "SELECT id, name, class, marks FROM students ORDER BY id LIMIT 2";
$result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($result)) {
echo "ID: {$row['id']} Name: {$row['name']} Class: {$row['class']} Marks: {$row['marks']}<br>";
}
mysqli_close($conn);
?>Salida:
ID: 1 Name: John Class: 10 Marks: 90
ID: 2 Name: Michael Class: 9 Marks: 85Nótese el uso de mysqli_fetch_assoc() (solo array asociativo) en lugar de mysqli_fetch_array(), que devuelve tanto claves numéricas como de cadena y por tanto asigna el doble de memoria para los mismos datos.
Omitir filas con OFFSET
Para obtener la segunda página de dos estudiantes — filas 3 y 4 — omite las primeras dos:
<?php
$conn = mysqli_connect("localhost", "username", "password", "database");
$query = "SELECT id, name, marks FROM students ORDER BY id LIMIT 2 OFFSET 2";
$result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($result)) {
echo "ID: {$row['id']} Name: {$row['name']} Marks: {$row['marks']}<br>";
}
mysqli_close($conn);
?>Salida:
ID: 3 Name: Jessica Marks: 80
ID: 4 Name: Sarah Marks: 88Construir paginación de forma segura
Para una paginación real, el número de página proviene de la entrada del usuario (una URL como ?page=3), por lo que el offset nunca debe concatenarse directamente en SQL. Usa una sentencia preparada con parámetros enteros enlazados:
<?php
$conn = mysqli_connect("localhost", "username", "password", "database");
$perPage = 2;
$page = max(1, (int) ($_GET['page'] ?? 1)); // force a positive integer
$offset = ($page - 1) * $perPage;
$stmt = mysqli_prepare(
$conn,
"SELECT id, name, marks FROM students ORDER BY id LIMIT ? OFFSET ?"
);
mysqli_stmt_bind_param($stmt, "ii", $perPage, $offset); // "ii" = two integers
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
echo "ID: {$row['id']} Name: {$row['name']} Marks: {$row['marks']}<br>";
}
mysqli_close($conn);
?>Convertir la página a (int) y enlazarla como entero ("ii") previene la inyección SQL, ya que LIMIT/OFFSET solo aceptan números de todas formas.
Para mostrar "Página 3 de N", también necesitas el conteo total de filas. Ejecuta una consulta COUNT(*) separada y divide por el tamaño de página:
$total = (int) mysqli_fetch_row(
mysqli_query($conn, "SELECT COUNT(*) FROM students")
)[0];
$pageCount = (int) ceil($total / $perPage); // 5 rows / 2 per page = 3 pagesErrores comunes
- Sin
ORDER BY, sin garantía. Como se indicó,LIMITsolo devuelve un fragmento estable cuando se combina con unORDER BYen una columna única (o con desempate). - Los offsets grandes son lentos.
LIMIT 20 OFFSET 100000hace que MySQL escanee y descarte 100.000 filas. Para paginación profunda, prefiere la paginación por keyset —WHERE id > :lastSeenId ORDER BY id LIMIT 20— que salta directamente al lugar correcto usando el índice. VerWHERE. OFFSETno puede usarse solo. MySQL requiere unLIMITsiempre que usesOFFSET. Para omitir filas y retornar "el resto", usa un límite enorme:LIMIT 18446744073709551615 OFFSET 10.- Las páginas se indexan internamente desde cero. Un error de desfase frecuente es olvidar que la página 1 corresponde a
OFFSET 0, de ahí el($page - 1) * $perPagedel ejemplo anterior.