LIMIT: omezení počtu záznamů
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:
- počet článků na stránku
- počet článků na stránku
- čí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:
- Čtenář si zobrazí výpis článků – dostane první stránku s deseti nejnovějšími.
- Redaktor vydá nový článek.
- 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
PAtrik10: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
Robo7:07 odpoledne on Říjen 8th, 2009
inak ROWID ma svoje unikatne kazdy zaznam v tabulke, nie vysledok dotazu
V Oracle sa to riesi pomocou
Robo7:07 odpoledne on Říjen 8th, 2009
V Oracle sa to riesi pomocou ROWNUM nie ROWID ;)
SELECT * FROM taulka WHERE ROWNUM < 20