W3docs

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 OFFSET

Il 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, quindi OFFSET 0 inizia dalla prima riga.
  • LIMIT 10, 5 restituisce 5 righe a partire dalla decima (cioè le righe 11–15). La stessa query si legge più chiaramente come LIMIT 5 OFFSET 10.

LIMIT senza ORDER BY è non deterministico. Senza un esplicito ORDER BY, MySQL è libero di restituire le righe in qualsiasi ordine, quindi "le prime 2 righe" possono variare da un'esecuzione all'altra. Usa sempre LIMIT insieme a ORDER BY quando 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: 85

Si 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: 88

Costruire 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 pages

Insidie comuni

  • Nessun ORDER BY, nessuna garanzia. Come indicato sopra, LIMIT restituisce una porzione stabile solo se abbinato a un ORDER BY su una colonna univoca (o con gestione dei pareggi).
  • Gli offset elevati sono lenti. LIMIT 20 OFFSET 100000 fa comunque scorrere e scartare 100.000 righe a MySQL. Per la paginazione profonda, preferisci la paginazione keysetWHERE id > :lastSeenId ORDER BY id LIMIT 20 — che salta direttamente al punto giusto usando l'indice. Vedi WHERE.
  • OFFSET non può essere usato da solo. MySQL richiede un LIMIT ogni volta che si usa OFFSET. 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) * $perPage nell'esempio sopra.

Esercitazione

Pratica
Qual è lo scopo della parola chiave 'LIMIT' in MySQL come discusso nel link fornito?
Qual è lo scopo della parola chiave 'LIMIT' in MySQL come discusso nel link fornito?
Was this page helpful?