PHP MySQL Select com Limit: Guia Completo
Aprenda a usar a cláusula LIMIT do MySQL no PHP para limitar linhas, pular registros com OFFSET e criar paginação segura com prepared statements.
Em um banco de dados, muitas vezes você quer apenas uma parte de uma tabela — os dez posts mais recentes, a primeira página de resultados de busca, um único "melhor pontuador" — em vez de todas as linhas. A cláusula LIMIT em uma instrução SELECT do MySQL define quantas linhas a consulta pode retornar. Combinada com um OFFSET, ela é a base da paginação: servir grandes conjuntos de resultados uma página por vez, em vez de carregar milhares de linhas na memória.
Este capítulo aborda a sintaxe de LIMIT, o OFFSET que pula linhas, como construir uma paginação segura com prepared statements e as armadilhas que costumam pegar as pessoas de surpresa. Ele pressupõe que você já saiba conectar-se ao MySQL e executar um SELECT básico.
Sintaxe de SELECT com LIMIT
A forma básica define um número máximo de linhas a retornar:
SELECT column1, column2, ... FROM table_name LIMIT row_count;Para também pular linhas do início do conjunto de resultados, adicione um deslocamento (offset). O MySQL aceita duas grafias equivalentes:
SELECT ... FROM table_name LIMIT offset, row_count; -- offset first
SELECT ... FROM table_name LIMIT row_count OFFSET offset; -- explicit OFFSETO que cada parte significa:
row_count— o número máximo de linhas a retornar.offset— quantas linhas pular antes de começar a retornar resultados. O deslocamento é baseado em zero, portantoOFFSET 0começa na primeira linha.LIMIT 10, 5retorna 5 linhas a partir da posição seguinte às 10 primeiras (ou seja, as linhas 11 a 15). A mesma consulta fica mais natural escrita comoLIMIT 5 OFFSET 10.
LIMITsemORDER BYé não determinístico. Sem umORDER BYexplícito, o MySQL pode retornar as linhas em qualquer ordem, de modo que "as 2 primeiras linhas" podem variar de uma execução para outra. Sempre combineLIMITcomORDER BYquando a ordem das linhas importar.
Exemplo: recuperando as primeiras linhas
Considere uma tabela chamada 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 buscar os dois primeiros alunos (ordenados por id para que o resultado seja estável):
<?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);
?>Saída:
ID: 1 Name: John Class: 10 Marks: 90
ID: 2 Name: Michael Class: 9 Marks: 85Observe o uso de mysqli_fetch_assoc() (apenas array associativo) em vez de mysqli_fetch_array(), que retorna chaves numéricas e de texto ao mesmo tempo e, por isso, aloca o dobro de memória para os mesmos dados.
Pulando linhas com OFFSET
Para obter a segunda página de dois alunos — as linhas 3 e 4 — pule as duas primeiras:
<?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);
?>Saída:
ID: 3 Name: Jessica Marks: 80
ID: 4 Name: Sarah Marks: 88Construindo uma paginação segura
Na paginação real, o número da página vem da entrada do usuário (uma URL como ?page=3), então o offset nunca deve ser concatenado diretamente no SQL. Use um prepared statement com parâmetros inteiros vinculados:
<?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);
?>Converter a página com (int) e vinculá-la como inteiro ("ii") previne SQL injection, já que LIMIT/OFFSET aceitam apenas números de qualquer maneira.
Para exibir "Página 3 de N", você também precisa do número total de linhas. Execute uma consulta COUNT(*) separada e divida pelo tamanho da 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 pagesArmadilhas comuns
- Sem
ORDER BY, sem garantia. Como mencionado acima,LIMITsó retorna uma fatia estável quando combinado com umORDER BYem uma coluna única (ou com critério de desempate). - Offsets grandes são lentos.
LIMIT 20 OFFSET 100000ainda obriga o MySQL a percorrer e descartar 100.000 linhas. Para paginação profunda, prefira a paginação keyset —WHERE id > :lastSeenId ORDER BY id LIMIT 20— que vai direto ao ponto certo usando o índice. VejaWHERE. OFFSETnão pode ser usado sozinho. O MySQL exige umLIMITsempre que você usaOFFSET. Para pular linhas e retornar "todo o resto", use um limite enorme:LIMIT 18446744073709551615 OFFSET 10.- As páginas são indexadas a partir de zero internamente. Um erro clássico de off-by-one é esquecer que a página 1 corresponde a
OFFSET 0, daí o($page - 1) * $perPageacima.