Načrtovanje in razvoj spletnih aplikacij

Omejitve izpisov podatkovne zbirke - LIMIT

Kadar ne želimo prikazati vseh zapisov naenkrat, lahko izpis omejimo. V MySQL oziroma MariaDB za to uporabimo ukaza LIMIT in OFFSET.

Omejen izpis je uporaben pri daljših seznamih, pri preglednejšem prikazu podatkov in pri pripravi paginacije. Namesto da uporabniku prikažemo vse zapise hkrati, mu lahko prikažemo samo določen del rezultatov.

Pomni: LIMIT določa, koliko zapisov naj se prikaže, OFFSET pa določa, koliko začetnih zapisov naj se preskoči.

Pri omejenem izpisu rezultate skoraj vedno tudi uredimo z ukazom ORDER BY. Tako je vrstni red prikaza predvidljiv.

Vsebina strani

Osnovna pravila

Pri omejenem izpisu določimo, koliko zapisov želimo prikazati in od katerega mesta v rezultatu naj se prikaz začne.

  • Za določanje števila prikazanih zapisov uporabimo LIMIT.
  • Za določanje začetnega odmika uporabimo OFFSET.
  • Vrednost pri LIMIT mora biti pozitivno celo število.
  • Vrednost pri OFFSET mora biti celo število, ki je večje ali enako nič.
  • Rezultate pred omejitvijo uredimo z ukazom ORDER BY.
  • Če uporabnik vnese neveljavne vrednosti, uporabimo privzeti izpis ali prikažemo opozorilo.
  • Pri izpisu podatkov uporabimo htmlspecialchars().

Pozor: Vrednosti za LIMIT in OFFSET pogosto pridejo iz obrazca. Zato jih je treba pred uporabo preveriti kot cela števila.

Omejen izpis zapisov

Omejen izpis pomeni, da iz tabele prikažemo le določen del zapisov. To je uporabno pri daljših seznamih, pri prikazu po straneh in pri pripravi preglednejšega izpisa.

Osnovna sintaksa za omejen izpis je:

SELECT *
FROM imeTabele
LIMIT steviloZapisov;

Če želimo določiti še začetni odmik, uporabimo:

SELECT *
FROM imeTabele
LIMIT steviloZapisov OFFSET zacetek;

Če želimo iz tabele knjige prikazati 5 zapisov od tretjega zapisa naprej, uporabimo:

SELECT *
FROM knjige
ORDER BY ID_knjige
LIMIT 5 OFFSET 2;

Vrednost OFFSET 2 pomeni, da se prva dva zapisa preskočita, nato pa se prikaže največ 5 zapisov.

Pomni: Vrednost pri OFFSET se začne z nič. OFFSET 0 pomeni, da se ne preskoči noben zapis.

Osnovni primer z mysqli

Spodnji zgled prikaže omejeno število zapisov iz tabele knjige. Če uporabnik vnese napačne podatke, se izpišejo vsi zapisi.

<?php
define('DB_SERVER', 'localhost');
define('DB_USER', 'uporabnik');
define('DB_PASS', 'skritoGeslo');
define('DB_NAME', 'knjiznica');

// Povezava do podatkovne zbirke
$connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);

// Preverjanje povezave
if (!$connection) {
    die(
        'Povezava s podatkovno zbirko ni vzpostavljena: ' .
        mysqli_connect_error() .
        ' (' . mysqli_connect_errno() . ')'
    );
}

$stevilo = trim($_POST['stevilo'] ?? '');
$start = trim($_POST['start'] ?? '');

$steviloValid = filter_var($stevilo, FILTER_VALIDATE_INT);
$startValid = filter_var($start, FILTER_VALIDATE_INT);

if ($steviloValid !== false && $startValid !== false && (int)$stevilo > 0 && (int)$start >= 0) {
    $stmt = mysqli_prepare(
        $connection,
        "SELECT ID_knjige, Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto
         FROM knjige
         ORDER BY ID_knjige
         LIMIT ? OFFSET ?"
    );
    mysqli_stmt_bind_param($stmt, 'ii', $steviloValid, $startValid);
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);
} else {
    $result = mysqli_query(
        $connection,
        "SELECT ID_knjige, Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto
         FROM knjige
         ORDER BY ID_knjige"
    );
}

while ($row = mysqli_fetch_assoc($result)) {
    echo htmlspecialchars($row['Naslov']) . '<br>';
}

if (isset($stmt)) {
    mysqli_stmt_close($stmt);
}
mysqli_close($connection);
?>

Pri mysqli vrednosti za LIMIT in OFFSET preverimo kot cela števila, nato ju vežemo kot parametra s funkcijo mysqli_stmt_bind_param().

Osnovni primer s PDO

Tudi z vmesnikom PDO lahko omejen izpis izvedemo s pripravljenim stavkom in parametroma za LIMIT ter OFFSET.

<?php
$streznik = 'localhost';
$baza = 'knjiznica';
$uporabnik = 'uporabnik';
$geslo = 'skritoGeslo';

try {
    $pdo = new PDO("mysql:host=$streznik;dbname=$baza;charset=utf8mb4", $uporabnik, $geslo);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $stevilo = trim($_POST['stevilo'] ?? '');
    $start = trim($_POST['start'] ?? '');

    $steviloValid = filter_var($stevilo, FILTER_VALIDATE_INT);
    $startValid = filter_var($start, FILTER_VALIDATE_INT);

    if ($steviloValid !== false && $startValid !== false && (int)$stevilo > 0 && (int)$start >= 0) {
        $stmt = $pdo->prepare(
            "SELECT ID_knjige, Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto
             FROM knjige
             ORDER BY ID_knjige
             LIMIT :stevilo OFFSET :start"
        );
        $stmt->bindValue(':stevilo', (int)$stevilo, PDO::PARAM_INT);
        $stmt->bindValue(':start', (int)$start, PDO::PARAM_INT);
        $stmt->execute();
    } else {
        $stmt = $pdo->prepare(
            "SELECT ID_knjige, Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto
             FROM knjige
             ORDER BY ID_knjige"
        );
        $stmt->execute();
    }

    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    foreach ($rows as $row) {
        echo htmlspecialchars($row['Naslov']) . '<br>';
    }
}
catch (PDOException $e) {
    echo 'Napaka pri omejenem izpisu: ' . $e->getMessage();
}
?>

Pri PDO vrednosti za LIMIT in OFFSET vežemo kot cela števila z PDO::PARAM_INT. Tako preprečimo, da bi se v poizvedbo vključile neustrezne vrednosti.

Preverjanje vrednosti za omejen izpis

Pred uporabo ukazov LIMIT in OFFSET moramo preveriti, ali je uporabnik vnesel veljavni celi števili.

  • število zapisov mora biti pozitivno celo število,
  • začetni odmik mora biti nič ali več,
  • če podatka nista pravilna, uporabniku pokažemo opozorilo ali izpišemo vse zapise,
  • omejen izpis je najbolj smiseln skupaj z urejenim prikazom zapisov.
$steviloValid = filter_var($stevilo, FILTER_VALIDATE_INT);
$startValid = filter_var($start, FILTER_VALIDATE_INT);

if ($steviloValid === false || $startValid === false) {
    echo 'Vneseni vrednosti nista veljavni celi števili.';
}

Pozor: Vrednost LIMIT 0 ne pomeni prikaza vseh zapisov, ampak pomeni, da se ne prikaže noben zapis. Zato mora biti število prikazanih zapisov večje od nič.

Povezava z paginacijo rezultatov

Ukaza LIMIT in OFFSET se pogosto uporabljata pri paginaciji rezultatov. Pri tem se velik seznam razdeli na več strani.

Če želimo na vsaki strani prikazati 10 zapisov, lahko odmik izračunamo iz številke strani:

$naStran = 10;
$stran = 3;
$offset = ($stran - 1) * $naStran;

Za tretjo stran je odmik 20, saj se prvih 20 zapisov preskoči, nato pa se prikaže naslednjih 10 zapisov.

SELECT *
FROM knjige
ORDER BY ID_knjige
LIMIT 10 OFFSET 20;

Pomni: Pri paginaciji mora biti vrstni red rezultatov določen. Zato je ORDER BY pri uporabi LIMIT in OFFSET zelo pomemben.

Primerjava pristopov mysqli in PDO

Oba pristopa omogočata omejen izpis s preverjenimi številskimi parametri. Razlikujeta se predvsem v načinu vezave parametrov in branju rezultatov.

Korak mysqli PDO
Preverjanje števil filter_var() filter_var()
Priprava stavka mysqli_prepare() $pdo->prepare()
Parametra ?, ? :stevilo, :start
Vezava vrednosti mysqli_stmt_bind_param() $stmt->bindValue()
Branje rezultatov mysqli_fetch_assoc() $stmt->fetchAll()

Aplikacija Knjige

📘Aplikacija Knjige

V priloženi aplikaciji Knjige je omejen izpis izveden v datoteki 11_omejen-izpis.php. Uporabnik v obrazec vnese število zapisov in začetni odmik, nato pa aplikacija glede na vneseni vrednosti pripravi ustrezno poizvedbo.

Stran najprej pridobi največji ID_knjige in ga uporabi pri dodatni kontroli, ali je začetni odmik smiseln. Če podatka manjkata, nista veljavna ali je začetni odmik previsok, aplikacija prikaže vse zapise. Če sta oba podatka pravilna, uporabi LIMIT :stevilo OFFSET :start.

Pod obrazcem aplikacija izpiše tudi opis trenutnega izpisa, število prikazanih zapisov in preglednico rezultatov. Vrednosti so pri izpisu zaščitene s funkcijo htmlspecialchars().

Primer: aplikacija Knjige – 11_omejen-izpis.php
  1. Najprej pripravimo obrazec za vnos števila zapisov in začetnega odmika.
  2. Ob oddaji obrazca preberemo obe vrednosti.
  3. Preverimo, ali sta vneseni vrednosti veljavni celi števili.
  4. Preverimo, da je število zapisov večje od 0.
  5. Preverimo, da je začetni odmik večji ali enak 0.
  6. Če sta podatka pravilna, pripravimo poizvedbo z ukazoma LIMIT in OFFSET.
  7. Če podatka nista pravilna, uporabniku izpišemo vse zapise ali opozorilo.
  8. Rezultate uredimo, izvedemo poizvedbo in jih izpišemo v tabeli.
  9. Na koncu prikažemo še opis trenutnega izpisa in število prikazanih zapisov.

Pozor: Pri omejenem izpisu je pomembno, da uporabnik ne more poslati negativnega odmika ali neustrezne vrednosti za število zapisov. Takšne vrednosti je treba zavrniti ali nadomestiti s privzetim izpisom.

Priporočila

  • Za omejen izpis uporabi LIMIT.
  • Za začetni odmik uporabi OFFSET.
  • Vrednosti za LIMIT in OFFSET vedno preveri kot cela števila.
  • Število zapisov naj bo večje od 0.
  • Začetni odmik naj bo večji ali enak 0.
  • Rezultate vedno uredi z ORDER BY.
  • Pri večjih seznamih razmisli o paginaciji rezultatov.
  • Uporabniku prikaži opis izpisa in število prikazanih zapisov.

Pogoste napake

  • Vrednosti za LIMIT in OFFSET niso preverjene.
  • Za število zapisov je dovoljena vrednost 0, zato se ne prikaže noben zapis.
  • Začetni odmik je negativen.
  • Ukaz LIMIT je uporabljen brez ORDER BY, zato je izpis manj predvidljiv.
  • Uporabniku ni jasno prikazano, kateri del rezultatov gleda.
  • Ni predvidenega odziva za neveljavne vrednosti obrazca.
  • Pri izpisu podatkov manjka htmlspecialchars().
  • Pri paginaciji je napačno izračunan začetni odmik.

Omejen izpis je pogosto povezan z uporabniškim vnosom. Zato je treba vrednosti za število zapisov in začetni odmik obravnavati kot nezaupan podatek, dokler ju strežniška koda ne preveri.