PHP MySQL SELECT con LIMIT: Guida Completa
Scopri come usare la clausola LIMIT in MySQL con PHP per recuperare un numero specifico di righe e implementare la paginazione in modo sicuro.
In un database spesso si vuole ottenere solo una porzione di una tabella — i dieci post più recenti, la prima pagina dei risultati di ricerca, un unico "miglior punteggio" — anziché tutte le righe. La clausola LIMIT in un'istruzione MySQL SELECT limita il numero di righe restituite dalla query. Combinata con un OFFSET, costituisce il fondamento della paginazione: servire grandi insiemi di risultati una pagina alla volta invece di caricare migliaia di righe in memoria.
Questo capitolo tratta la sintassi di LIMIT, il salto di righe con OFFSET, come costruire la paginazione in modo sicuro con istruzioni preparate e le insidie più comuni. Si assume che tu sappia già connetterti a MySQL ed eseguire un SELECT di base.
Sintassi di SELECT con LIMIT
La forma base imposta un numero massimo di righe da restituire:
SELECT column1, column2, ... FROM table_name LIMIT row_count;Per saltare anche alcune righe dall'inizio del set di risultati, aggiungi un offset. MySQL supporta due sintassi equivalenti:
SELECT ... FROM table_name LIMIT offset, row_count; -- offset first
SELECT ... FROM table_name LIMIT row_count OFFSET offset; -- explicit OFFSETIl significato di ciascuna parte:
row_count— il numero massimo di righe da restituire.offset— quante righe saltare prima di iniziare a restituire i risultati. L'offset è a base zero, quindiOFFSET 0inizia dalla prima riga.LIMIT 10, 5restituisce 5 righe a partire dalla decima (cioè le righe 11–15). La stessa query si legge più chiaramente comeLIMIT 5 OFFSET 10.
LIMITsenzaORDER BYè non deterministico. Senza un esplicitoORDER BY, MySQL è libero di restituire le righe in qualsiasi ordine, quindi "le prime 2 righe" possono variare da un'esecuzione all'altra. Usa sempreLIMITinsieme aORDER BYquando l'ordine delle righe è importante.
Esempio: recupero delle prime righe
Considera una tabella chiamata 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 |
+----+---------+--------+-------+Per recuperare i primi due studenti (ordinati per id in modo che il risultato sia stabile):
<?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);
?>Output:
ID: 1 Name: John Class: 10 Marks: 90
ID: 2 Name: Michael Class: 9 Marks: 85Si noti l'uso di mysqli_fetch_assoc() (solo array associativo) anziché mysqli_fetch_array(), che restituisce sia chiavi numeriche sia stringhe e quindi alloca il doppio della memoria per gli stessi dati.
Saltare righe con OFFSET
Per ottenere la seconda pagina di due studenti — righe 3 e 4 — salta le prime due:
<?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);
?>Output:
ID: 3 Name: Jessica Marks: 80
ID: 4 Name: Sarah Marks: 88Costruire la paginazione in modo sicuro
Per una paginazione reale il numero di pagina proviene dall'input dell'utente (un URL come ?page=3), quindi l'offset non deve mai essere concatenato direttamente nell'SQL. Usa una istruzione preparata con parametri interi vincolati:
<?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);
?>Il casting della pagina a (int) e il suo binding come intero ("ii") prevengono l'SQL injection, poiché LIMIT/OFFSET accettano comunque solo numeri.
Per mostrare "Pagina 3 di N", è necessario anche il conteggio totale delle righe. Esegui una query COUNT(*) separata e dividi per la dimensione della pagina:
$total = (int) mysqli_fetch_row(
mysqli_query($conn, "SELECT COUNT(*) FROM students")
)[0];
$pageCount = (int) ceil($total / $perPage); // 5 rows / 2 per page = 3 pagesInsidie comuni
- Nessun
ORDER BY, nessuna garanzia. Come indicato sopra,LIMITrestituisce una porzione stabile solo se abbinato a unORDER BYsu una colonna univoca (o con gestione dei pareggi). - Gli offset elevati sono lenti.
LIMIT 20 OFFSET 100000fa comunque scorrere e scartare 100.000 righe a MySQL. Per la paginazione profonda, preferisci la paginazione keyset —WHERE id > :lastSeenId ORDER BY id LIMIT 20— che salta direttamente al punto giusto usando l'indice. VediWHERE. OFFSETnon può essere usato da solo. MySQL richiede unLIMITogni volta che si usaOFFSET. Per saltare righe e restituire "tutto il resto", usa un limite molto grande:LIMIT 18446744073709551615 OFFSET 10.- Le pagine sono indicizzate a zero internamente. Un errore off-by-one comune è dimenticare che la pagina 1 corrisponde a
OFFSET 0, da cui($page - 1) * $perPagenell'esempio sopra.