Základy SQL, část 1.
V této sérii článků budu popisovat poněkud opomíjenou věc - SQL a PL/SQL syntax pro současné databáze, a to konkrétně pro opensource produkt MySQL 5.x (tedy včetně VIEWS, TRIGGERS, atd.). Řada lidí se nějak naučí programovat v PHP, ale kvalitní použití SQL pro ně zůstává záhadou. A to je velice špatně.
Osobně veškeré rozsáhlé aplikace píšu zásadně nad databází (typicky Oracle 8 až 10g). Proč? Řada činností se dá rozdělit na události, a tyto události, pokud je napíšete jako triggery a uložené procedury, není nutné explicitně volat. Ať už přistupujete do databáze z webové aplikace, z Delphi či z .NET aplikace, máte záruku, že daná akce se vždy provede. Navíc je zpracování dat nad databází mnohem efektivnější a rychlejší než tahat všechna data na klienta, tam to aplikačně zpracovat, a poté data posílat zpět k uložení na server.
Ubohá znalost SQL je dána i zcela nechutným rozšířením jedné extrémně špatné databáze, tedy MySQL 3.x a 4.x. Mírně lepší je až MySQL 5.x, nicméně, její nasazení na hostingu není zatím moc reálné, řada českých webhostingů se sotva dopracovala k MySQL 4.1.
Tolik k úvodu, a následuje první, pochopitelně nejsnazší, část tohoto seriálu.
SELECT a LEFT/RIGHT/INNER JOIN
Představte si, že máte nějaké fórum, ve kterém ja tabulka příspěvků (TOPICS) a tabulka práv uživatelů (PERMS). Pokud administrátor nic nenastaví, je tabulka PERMS prázdná a platí defaultní práva systému, pokud chce defaultní práva změnit, zapíše přes administrační rozhraní práva pro jednotlivé skupiny uživatelů do tabulky PERMS.
Pokud chcete tedy vypsat posledních 5 příspěvků ve fóru, nemůžete udělat prostý SELECT na tabulku TOPICS, vypsalo by se i to, co právě přihlášenému uživateli běžně není přístupné. Řešením je použít LEFT JOIN:
SELECT t.id, t.subject, t.last_post FROM topics AS t LEFT JOIN perms AS p ON (p.forum_id=t.forum_id AND p.group_id=4) WHERE p.read_forum IS NULL OR p.read_forum=1 ORDER BY t.last_post DESC LIMIT 0 , 5
Uvedený jednoduchý SELECT a LEFT JOIN respektuje to, že práva nemusí být v tabulce PERMS nastavena (IS NULL), ale pokud jsou, zkontroluje, zda uživatel zařazený ve skupině [4] má právo pro čtení tohoto fóra (read_forum = 1).
Existuje ještě RIGHT JOIN, který obrátí pozici tabulek, a samozřejmě INNER JOIN, který požaduje, aby shodná data byla v obou tabulkách, nepovoluje tedy NULL hodnoty. Klasický INNER JOIN by v této situaci samozřejmě nebylo možné použít, protože tabulka PERMS je naplněna jen pokud jsou hodnoty práv rozdílné od defaultních hodnot.
Více k dané problematice: MySQL.com manuál na JOIN.
SELECT DISTINCT a GROUP BY
Příkaz SELECT DISTINCT se hodí všude tam, kde chcete z mnoha stejných hodnot vybrat jen ty unikátní, například máte tabulku komentářů a chcete vybrat posledních 5 komentářů od různých autorů.
SELECT DISTINCT author FROM comment ORDER BY time DESC LIMIT 0 , 5
Uvedený příklad je nicméně poněkud umělý, ve skutečnosti se budete zajímat i o číslo posledního komentáře daného uživatele (třeba pro zobrazení odkazu na takový komentář) a datum komentáře, což můžete nejsnáze udělat pomocí klauzule GROUP BY, který provede seskupení dotazu kolem pole autor, třeba takto:
SELECT author, MAX(time), MAX(comment_id) FROM comment GROUP BY author ORDER BY time DESC LIMIT 0,5
V obou případech je vhodné mít definován INDEX na pole autor i time. Příkaz EXPLAIN Vám zobrazí, jaké existující či pomocné indexy jsou pro SQL dotaz použity.
Hodilo by se na začátek popsat taky logiku názvů tabulek resp. názvů jeich sloupců. Někdy i na "profesionálních dílech" lze najít roztodivné pojmenování.
Nechápu, proč je v posledním příkladu použita fce. MAX?
[1] Protože chceš zjistit číslo posledního komentáře - což bude asi to nejvyšší číslo..
Zajisté chvályhodný záměr. Jsem zvědavý, co se z něj vyvine.
Dovolím si vznést pár dotazů:
[3] syntaxe všech příkladů je a bude přizpůsobena MySQL 5.x, aby si ji kdokoliv jednoduše otestovat. Nemá smysl, bohužel, se zabývat pro webdesign Oraclem.. Jinak článek opravdu začíná od základů, i se tak jmenuje, viz [1], který nechápe ani toto triviální použití a dokazuje nutnost opakování základů ;-)
[4] Po pravdě řečeno, konstrukce MAX(autor) mi také přišla nadbytečná, když se podle autora agreguje. Nebo se pletu?
A MAX(time), MAX(comment_id) by v rámci tohoto blogu asi fungoval podle očekávání (číslo posledního příspěvku a datum jeho vložení), je to ale tím, že příspěvek s nejvyšším číslem je vždy ten poslední. Asi by se navíc mělo agregovat i podle ID článku, jinak to přestane fungovat i tady.
Radku situace se už lepší, já se snažím vážné věci nahostovat tam, kde je PostgreSQL o které si myslím, že občas zbytečně opomíjena a přitom může směle konkurovat i velikým komerčním databázím. Zkus někdy porovnat db mezi sebou (nemyslím výkonově :-))
[4] Ale on má pravdu,
MAX(author)
je skutečně nadbytečné, lepší jeauthor
.INDEX na pole autor i time působí dojmem, že mají být vytvořeny indexy
(author)
a(time)
, pro tento dotaz je ale vhodnější složený index(author, time)
.[7] máš pravdu, první MAX je tam nadbytečný
Zdravím, musím souhlasit s prvním příspěvkem, skoro by podle mě bylo vhodnější zahájit základy databází obecně.
Pokud použiji analogii, k čemu mi bude dobrá znalost používání příboru, když s ním budu jíst z koryta svinstvo spolu s ostatními prasátky?
Alespoň základní věci, jako postup návrhu db, jak vytvořit kvalitní schema, čemu se vyhnout, jak pojmenovat tabulky a sloupce, jak a proč dekomponovat složité vztahy atd...
Nemuselo by se snad zabíhat do věcí jako normální formy apod., protože to jde vysvětlit jednodušeji pomocí "přikázání":-). Pro "teenagery" používající MySQL by to dle mého názoru bylo pro jejich další praxi přínosnější.
Možná by bylo vhodné u příkladů ukázat i datový model.
Ne všichni jsou zběhlí v SQL a nemusí jim být hned jasná struktura tabulek z uvedených SQL dotazů
A co CROSS JOIN nebo FULL JOIN? ;)
Mne vyhovuje, že seriál nezačína úplnymi základmi, o tom sa dajú nájsť iné seriály. Teším sa na časť o TRIGGERS.
[4] Ahoj Radku, díky za inspiraci. Jsem zvědav kolik lidí se bude chtít skutečně dozvědět něco hlubšího o PL/SQL.
Jenom malá poznámka k tomu, že by všechno měla dělat databáze.
Obecně to samozřejmě pravda je, ale vždy je třeba se zamyslet nad konkrétním systémem, protože nezřídka nastává situace, kdy DB stroj "jede jak fretka" a na nějaké složitější operace prostě nemá čas.
Proto je čas od času lepší získat surová data a zpracovat je až na aplikačním serveru (pokud DB i aplikační server běží na jednom stroji, předchozí s klidem ignorujte:-)
Prostě při programování používejte hlavu.
[14] tato situace nenastane v podstatě nikdy. Výkon databáze se dá podstatně lépe škálovat, včetně clusterů..
[14] Velmi bludný názor. Dovedeme-li ho do důsledku, není databázový server vůbec potřeba - všechno se uloží do souboru a budem se v tom hrabat vlastními metodami. A přitom jsou (nebo aspoň mají být) DB servery navrženy tak, aby můj dotaz vykonaly co nejefektivněji. A vůbec už nemluvím o referenční integritě, transakcích, triggerech a podobných vychytávkách, které by jen zoufalec řešil na úrovni aplikace. A ty baga zbytečných dat, lítajících po síti, si taky s klidem odpustím.
Také si myslím, že seriál začíná moc daleko. Na začátek bych dal Relační algebru, aby si lidi uvědomili, jak to vůbec vlasně celé funguje. Oni pak vidí operaci typu JOIN a vůbec nemají ponětí, jaké rozsáhlé výpočty jsou za tím. Pak se stává že nerozumně joinujou několik tisíci řádkových tabulek a diví se, že je to pomalé. Zkrátka a dobře místo toho aby nejprve provedli restrikci a pak teprve joinovali, tak nejprve si udělají join a pak až restrikci.
Zajímavý článek, ale opravdu tam chybí popsat tabulky. Kdy bude další díl? :)
děkuji za tento seriál článků....začínám s SQL a tohle mi právě přijde vhod...
ještě jednou díky moc a jen tak dál