LIMIT: omezení počtu záznamů

5
Srp/09
3

Klausule LIMIT slouží k omezení počtu záznamů.

SELECT *
FROM tabulka
LIMIT 1;

Tímto dotazem jsme získali jeden záznam (pokud tabulka alespoň jeden obsahuje) z tabulky.

Limit má smysl využívat hlavně současně s řazením (ORDER BY) – jinak totiž není zaručeno, které záznam dostaneme.

Vypíšeme si např. medailisty (první tři) z nějaké soutěže:

SELECT *
FROM soutezici
ORDER BY poradi
LIMIT 3;

Stránkování

Zejména u webových aplikací se můžete setkat se stránkováním – aplikace nevypisuje všechny záznamy najednou, ale např. jen prvních deset článků. Další se uživateli zobrazí až po kliknutí na další stránku.

K tomu se právě využívá LIMIT na úrovni databázového dotazu:

SELECT *
FROM clanek
ORDER BY datum_vydani DESC
LIMIT ? OFFSET ? * ?;

Za parametry (otazníky) dosadíme tyto hodnoty:

  1. počet článků na stránku
  2. počet článků na stránku
  3. číslo stránky – číslujeme od nuly (u první zobrazené stránky je OFFSET roven nule).

OFFSET zde znamená posun – vybíráme zvolený počet záznamů, ale ne od začátku, ale od záznamu daného OFFSETem.

Kdy nepoužívat LIMIT

Představme si následující situaci:

  1. Čtenář si zobrazí výpis článků – dostane první stránku s deseti nejnovějšími.
  2. Redaktor vydá nový článek.
  3. Uživatel si zobrazí druhou stránku z výpisu – očekává dalších deset článků – dostane jich deset, ale jeden z nich se mu zobrazil už v minulém výpisu, protože mezitím došlo k posunu vydáním nového článku.

V podstatě se jedná o chybu, ale její závažnost je tak malá, že si nebudeme dávat práci s jejím řešením. Jsou ale případy, kdy si takovou chybu nemůžeme dovolit.

Dejme tomu, že tiskneme faktury a posíláme je do tiskárny po 100 kusech. Načteme si z databáze 100 faktur, odešleme je do tiskárny a načteme si (pomocí LIMIT … OFFSET …) dalších sto faktur. Jenže mezi tím účetní zadala do systému několik nových záznamů a došlo k posunu. Důsledkem bude, že několik faktur vytiskneme a odešleme zákazníkům dvakrát. A to je vážný problém.

Teoreticky bychom mohli využít transakce a uzamknout si záznamy tak, abychom měli po celou dobu tisku zaručený výsledek – v rámci naší transakce by nedošlo k posunu (nebyla by ovlivněna transakcemi účetní). Toto řešení je dost nehospodárné, protože transakce by musela trvat celou dobu než dojde k tisku všech položek.

Také bychom mohli načíst všechny faktury do aplikace a dávky po sto kusech vytvářet až v ní. Čím více záznamů, tím větší bude paměťová náročnost takové aplikace. Což je dost omezující, jelikož rostoucí počet zpracovaných záznamů neznamená jen to, že zpracování bude trvat úměrně déle, ale i to, že pokud dojde k překročení určitého množství, aplikace vyčerpá všechnu svou paměť a nebude schopná pokračovat v práci.

Řešení, které pravděpodobně použijeme je to, že jedním z atributů faktury bude její stav a u každého záznamu budeme sledovat, zda už byl vytištěný nebo ne.

Vypsání náhodného záznamu

Oblíbenou součástí webových stránek nebo aplikací je vypisování náhodných citátů nebo např. tipů k používání aplikace. Také se můžeme setkat se zobrazováním náhodné fotografie z alba atd.

Pokud v dotazu nepožadujeme řazení (ORDER BY) pořadí záznamů sice není zaručeno, ale s velkou pravděpodobností bude vždy stejné (obvykle odpovídá pořadí, v jakém byly záznamy do databáze vloženy).

Chceme-li tedy vybrat jeden náhodný záznam, použijeme následující dotaz:

SELECT *
FROM sbirka.polozka
ORDER BY random()
LIMIT 1;

Název této funkce se liší podle použitého SŘBD – v PostgreSQL se jmenuje random(), zatímco např. v MySQL se jmenuje rand().

Posledních n záznamů ale řazeno obráceně

Co když chceme vypsat deset nejstarších deset článků?

SELECT *
FROM clanek
ORDER BY datum_vydani
LIMIT 10;

Tímto dotazem jsme získali nejstarších deset záznamů, ale máme je seřazené od nejstaršího – a my je chceme řadit od nejnovějšího.

Někoho možná napadne zjistit počet záznamů a použít např. LIMIT 10 OFFSET 90 při sto článcích. To ale není dobré řešení. My použijeme vnořený dotaz:

SELECT * FROM (
    SELECT *
    FROM clanek
    ORDER BY datum_vydani
    LIMIT 10
) AS clanky
ORDER BY clanky.datum_vydani DESC;

Ine databaze

PAtrik
10:10 dop. on Září 12th, 2009

Pekny clanok. Ja by som este dodal, ze v clanku uvedene neplati na vsetky databazove systemy. Plati to minimalne na MySQL a Postgres. V Oracle sa to riesi pomocou rowid (kazdy riadok vysledku dotazu ma svoje id) v MS SQL a Sybase sa to zas riesi bud formou SELECT TOP 10 * FROM table alebo nastavenim limitu este pred dotazom pomocou SET ROWCOUNT 10.

inak ROWID ma svoje unikatne kazdy zaznam v tabulke, nie vysledok dotazu

V Oracle sa to riesi pomocou ROWNUM nie ROWID ;)
SELECT * FROM taulka WHERE ROWNUM < 20