<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python – AXML</maintitle>
  <author>Jiří Znamenáček</author>
  <title>SQLite – pokračování</title>
  <date>2010-11-11</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>


<slide title="CREATE &amp; INSERT &amp; DROP">

  <p>
    Samotnou databázi v SQLite získáme připojením k souboru databáze (což mimo jiné znamená, že co soubor, to jedna databáze). Otevření do té doby neexistujícího souboru tento soubor v daném umístění vytvoří, avšak vzniklá databáze bude samozřejmě prázdná.
  </p>

  <p class="enumerate">
    Tabulku v otevřené databázi vytvoříme příkazem <code>CREATE TABLE</code>, který má následující tvar:
  </p>
  <example lang="sql">
CREATE TABLE NázevTabulky (
  NázevSloupce-1 TypSloupce-1 [_omezení-na-sloupec-1_],
  NázevSloupce-2 TypSloupce-2 [_omezení-na-sloupec-2_],
  ...
);
  </example>
  <note>
    Mezi „omezení“ kladená na sloupec se počítají např. <code>NOT NULL</code>, <code>PRIMARY KEY</code> apod.
  </note>
  
  <p class="enumerate">
    Data do připravené tabulky zaneseme pomocí příkazu <code>INSERT</code>:
  </p>
  <example lang="sql">
INSERT INTO NázevTabulky (NázevSloupce-1, NázevSloupce-2, ...) 
     VALUES (HodnotaProSloupec-1, HodnotaProSloupec-2, ...);
  </example>
  <note>
    Vkládáme-li přesně stejný počet hodnot, jako má tabulka sloupců, můžeme identifikaci sloupců vynechat:
    <example lang="sql">
INSERT INTO NázevTabulky
     VALUES (HodnotaProSloupec-1, HodnotaProSloupec-2, ...);
    </example>
  </note>
  
  <p class="enumerate">
    Existující tabulku pak můžeme smazat pomocí příkazu <code>DROP TABLE</code>:
  </p>
  <example lang="sql">
DROP TABLE NázevTabulky;
  </example>
  <note>
    Tabulka je odstraněna včetně všech svých případných indexů a omezení (<em>constraints</em>).
  </note>

</slide>
<slide title="UPDATE &amp; DELETE &amp; ALTER">

  <p>
    Data v existující tabulce můžeme nejenom prozkoumávat pomocí známého příkazu <code>SELECT</code>, ale samozřejmě též i měnit nebo mazat. Také je možné existující tabulky či sloupce přejmenovávat a dokonce měnit počty sloupců v tabulkách (i když to druhé v lepším případě znamená, že se změnily požadavky na databázi, a v horším, že databáze byla špatně navržena).
  </p>
  
  <p class="enumerate">
    Syntaxe pro úpravu dat v tabulce je následující:
  </p>
  <example lang="sql">
-- bez podmínky
UPDATE NázevTabulky
   SET NázevSloupce = _nová-hodnota_;

-- s podmínkou
UPDATE NázevTabulky
   SET NázevSloupce = _nová-hodnota_
   WHERE _podmínka_;
  </example>
  
  <p class="enumerate">
    Syntaxe pro smazání údajů z tabulky je velmi podobná:
  </p>
  <example lang="sql">
-- smazání všech řádek tabulky (samotná tabulka však zůstane zachována)
DELETE FROM NázevTabulky;

-- smazání pouze těch řádek tabulky, které vyhovují zadané podmínce
DELETE FROM NázevTabulky
  WHERE _podmínka_;
  </example>
  
  <p class="enumerate">
    Z mocné množiny příkazů <code>ALTER</code> na změnu samotné definice tabulek podporuje SQLite pouze dvě nejzákladnější operace – přejmenování tabulky a přidání sloupečku do tabulky:
  </p>
  <example lang="sql">
-- přejmenování tabulky
ALTER TABLE StarýNázevTabulky
  RENAME TO NovýNázevTabulky;

-- přidání sloupce do tabulky
ALTER TABLE NázevTabulky
  ADD COLUMN _definice-sloupce_;
  </example>
  <p>
    Definice sloupce je přitom stejná jako u příkazu <code>CREATE TABLE</code>, tj. <code>NázevSloupce _typ-sloupce_ [_omezení-na-sloupec_]</code>
  </p>

</slide>
<slide title="Příklad (1)">

  <p>
    Abychom měli s čím pracovat, vytvořme si následující jednoduchou databázi o dvou tabulkách:
  </p>
  <example src="_files/join/podnik.sql" lang="sql" />
  <notes>
    <note>
      Volně podle Milan Šimůnek: <i>SQL – kompletní kapesní průvodce</i>, Grada 1999
    </note>
    <note>
      PS: Databáze je úmyslně navržena nevhodně, viz rozbor v dalším textu.
    </note>
  </notes>

</slide>
<slide title="Příklad (2)">

  <p>
    Uvedené dvě tabulky jsou maximálně (a úmyslně) zjednodušené pro potřeby demonstrace, není tudíž překvapivé, že obsahují několik prohřešků proti správnému databázovému návrhu:
  </p>
  <ul>
    <li>
      Sloupec <code>oddeleni</code> na tabulce <code>zamestnanci</code> (tedy <code>zamestnanci.oddeleni</code>) funguje zjevně jako cizí klíč (<em>foreign key</em>, <em>FK</em>) do tabulky <code>oddeleni</code>, protože číslo oddělení by asi mělo být jedinečné ^_^ A proto by mohlo být <code>oddeleni.cislo</code> klíčem primárním (<em>primary key</em>, <em>PK</em>), což nám ovšem kazí úmyslná hodnota <code>NULL</code> pro <em>sklad</em>.
    </li>
    <li>
      Na druhou stranu tabulka <code>zamestnanci</code> žádný primární klíč nemá. Navíc by tady primárním klíčem obecně nemohla být ani kombinace obou sloupců, protože pravděpodobnost dvou zaměstnanců stejného oddělení, kteří se budou i stejně jmenovat, rozhodně není nulová. V podobných případech se pro primární klíč zakládá další sloupeček (typicky <code>id INTEGER PRIMATY KEY AUTOINCREMENT</code>).
    </li>
    <li>
      Kromě dvou hodnot <code>NULL</code> si povšimněme ještě přiřazení neexistujícího oddělení číslo <em>4</em> zaměstnanci <em>Jiříček</em>. Ve schématu tabulky se první dá zakázat pomocí direktivy <code>NOT NULL</code> a druhé přidáním omezení (<em>constraint</em>) na příslušný cizí klíč.
    </li>
  </ul>

</slide>
<slide title="Příklad (3)">

  <p>
    Vhodnější databázové schéma pro uvedenou soustavu tabulek by tak mohlo vypadat např. takto:
  </p>
  <example lang="sql">
CREATE TABLE oddeleni (
  cislo INTEGER PRIMARY KEY,
  nazev TEXT
);

CREATE TABLE zamestnanci (
  id       INTEGER PRIMARY KEY AUTOINCREMENT,
  prijmeni TEXT,
  oddeleni INTEGER NOT NULL,
  FOREIGN KEY(oddeleni) REFERENCES oddeleni(cislo)
);
  </example>
  <note>
    Primární klíč nemůže být <code>NULL</code> z definice a podmínka cizího klíče na <code>zamestnanci.oddeleni</code> si zase vyžádá, aby každý zaměstnanec byl do nějakého (existujícího) oddělení přiřazen.
  </note>
  <note>
    Z důvodů historické kompatibility nemusí být ve vaší verzi SQLite podpora omezení na cizí klíče zapnuta. V tom případě si ji můžete vynutit pro každé spojení do databáze následujícím příkazem: <code>PRAGMA foreign_keys = ON;</code>
  </note>
  <note>
    S omezeními na cizí klíče není možné promazávat/zahazovat tabulky zcela libovolně – pokud by došlo k porušení omezení FK, příkaz <code>DROP</code> zahlásí chybu a nebude vykonán. Tabulky, na kterých závisí jiné tabulky, se musí upravovat/mazat až jako poslední.
  </note>

</slide>
<slide title="SELECT revisited I">

  <p>
    Příkaz <code>SELECT</code> zůstává nejdůležitější zbraní v arzenálu SQL i pro dotazy přes více tabulek. Co se však v tomto případě děje, je poněkud složitější. Výsledkem nejjednoduššího možného dotazu je totiž následující:
  </p>
  <example layout="vertical">
    <program src="_files/join/join.1.sql" lang="sql" />
    <out src="_files/join/join.1.out" lang="text" />
  </example>
  <p>
    Vidíme, že databáze vyplnila náš příkaz na získání všeho ze dvou tabulek tak, že zkombinovala všechny řádky první tabulky postupně s každým řádkem tabulky druhé – jinými slovy provedla <strong>kartézský součin</strong> obou tabulek.
  </p>

</slide>
<slide title="SELECT revisited II">

  <p class="enumerate">
    Takto zkombinované tabulky nám ale moc platné nejsou – zjevně nás zajímá pouze ta podmnožina plného kartézského součinu, která poskytne relevantní odpovědi. V tomto případě tedy taková podmnožina, která ke každému zaměstnanci z tabulky <code>zamestnanci</code> přidá z tabulky <code>oddeleni</code> pouze řádek s odpovídajícím číslem oddělení pracovníka:
  </p>
  <example layout="vertical">
    <program src="_files/join/join.2a.sql" lang="sql" />
    <out src="_files/join/join.2a.out" lang="text" />
  </example>
  
  <p class="enumerate">
    Pokud už číslo oddělení nebudeme dále k ničemu potřebovat (zde nám posloužilo ke spojení informací ze dvou tabulek), můžeme ho z výstupu standardním způsobem vynechat:
  </p>
  <example layout="vertical">
    <program src="_files/join/join.2b.sql" lang="sql" />
    <out src="_files/join/join.2b.out" lang="text" />
  </example>
  <note>
    Poznamenejme ještě, že vzhledem k tomu, že sloupečky obou tabulek mají navzájem různé názvy, mohli jsme použít i jednodušší (byť na první pohled ne už tak jasnou) variantu dotazu:
    <example lang="sql">
  SELECT prijmeni, nazev FROM zamestnanci, oddeleni
    WHERE zamestnanci.oddeleni = oddeleni.cislo;
    </example>
  </note>
  
  <!--example src="_files/join/podnik.dotazy.sql" lang="sql" /-->

</slide>
<slide title="JOIN – vnitřní spojení">

  <p>
    <code>SELECT</code>y s podmínkou na předchozím slajdu jsou příkladem tzv. <strong>vnitřního spojení</strong> tabulek – do výsledku jsou zahrnuty <strong>pouze ty řádky z první tabulky</strong> (a návazně i druhé), <strong>pro které byla nalezena odpovídající hodnota v tabulce druhé</strong>.
  </p>
  <p>
    V souladu s dalšími typy spojení se dají zapsat také speciálním způsobem – následující tři příkazy jsou navzájem ekvivalentní:
  </p>
  <example lang="sql">
SELECT zamestnanci.prijmeni, oddeleni.nazev FROM zamestnanci, oddeleni
  WHERE zamestnanci.oddeleni = oddeleni.cislo;

SELECT zamestnanci.prijmeni, oddeleni.nazev FROM zamestnanci 
  JOIN oddeleni 
    ON zamestnanci.oddeleni = oddeleni.cislo;

SELECT zamestnanci.prijmeni, oddeleni.nazev FROM zamestnanci 
  INNER JOIN oddeleni 
          ON zamestnanci.oddeleni = oddeleni.cislo;
  </example>

</slide>
<slide title="JOIN – vnější spojení">

  <p class="enumerate">
    Kromě vnitřního spojení existují i spojení další, např. <strong>spojení vnější</strong> <em>levé</em>. Podívejme se na následující příklad:
  </p>
  <example layout="vertical">
    <program src="_files/join/join.3.sql" lang="sql" />
    <out src="_files/join/join.3.out" lang="text" />
  </example>
  <p>
    Vidíme, že <strong>levé</strong> vnější spojení zahrnulo do výstupu <strong>všechny řádky z první tabulky</strong>, tedy i ty, pro které ve druhé tabulce neexistuje odpovídající hodnota. Ať už kvůli odkazu nezanesenému (<code>NULL</code>) či neexistujícímu (odkaz na oddělení číslo 4).
  </p>
  <note>
    Příkaz se dá opět zapsat kratším způsobem, a to vynecháním slovíčka <code>OUTER</code>:
    <example lang="sql">
SELECT zamestnanci.prijmeni, oddeleni.nazev FROM zamestnanci 
  LEFT JOIN oddeleni 
  ON zamestnanci.oddeleni = oddeleni.cislo;
    </example>
  </note>
  
  <p class="enumerate">
    <em>Pravé vnější spojení</em> by podobným způsobem udělalo totéž, jen pro druhou tabulku.
  </p>
  <note>
    V SQLite je pravé vnější spojení jedna z mála věcí, které <a href="http://sqlite.org/omitted.html" class="external">nejsou implementovány</a>.
  </note>

</slide>
<slide title="Poznámka k použití uvozovek u názvů">

    <p>
        Jazyk SQL obsahuje <a href="https://sqlite.org/lang_keywords.html" class="external">poměrně velké množství tzv. „klíčových“ slov</a>, která nepřekvapivě nemůžete použít v názvech objektů, se kterými pracujete (jména tabulek či sloupců a podobně). Řeší se to tak, že řetězce uvedené v uvozovkách (nebo apostrofech) se berou opravdu jako jména a nikoli jako klíčová slova. Tudíž zatímco příkaz..
    </p>
    <example lang="sql">
      CREATE TABLE prvky (
          id INTEGER PRIMARY KEY,
          symbol TEXT,
          name TEXT,
          group TEXT,
          period TEXT,
          block TEXT
      );
    </example>
    <p>
        ..selže, protože <em>group</em> patří mezi klíčová slova, jeho zuvozovkovaná varianta už projde v pořádku:
    </p>
    <example lang="sql">
      CREATE TABLE prvky (
          id INTEGER PRIMARY KEY,
          symbol TEXT,
          name TEXT,
          "group" TEXT,
          period TEXT,
          block TEXT
      );
    </example>
    <p>
        PS: Některá doporučení jdou dokonce tak daleko, že byste podle nich měli psát v uvozovkách vždy všechna jména…
    </p>

</slide>
<slide title="Funkce">

  <p>
    V databázových systémech bývají k dispozici dva druhy funkcí:
  </p>
  <ul>
    <li>
      <em>skalární</em> funkce operují nad jednou hodnotou, tj. nad jedním políčkem řádku; typicky jde o funkce pro práci s řetězci nebo třeba datem; 
    </li>
    <li>
      <em>agregační</em> funkce naopak pracují s množinou hodnot, běžně návratovou množinou řádků; typickým příkladem jsou funkce pro sumaci nebo průměr.
    </li>
  </ul>

</slide>
<slide title="Funkce – agregační">

  <p>
    V základním nastavení podporuje SQLite následující <strong>agregační funkce</strong>:
  </p>
  <ul>
    <li>
      <strong>count(*|X)</strong> – vrací počet řádků (pro parametr <code>*</code>) nebo non-NULL výskytů hodnoty <code>X</code>
    </li>
    <li>
      <strong>min(X)</strong> &amp; <strong>max(X)</strong> – vrací minimální, resp. maximální hodnotu z celé skupiny, případně NULL; je to tedy první, resp. poslední non-NULL hodnota z množiny seřazené pomocí <code>ORDER BY</code>
    </li>
    <li>
      <strong>sum(X)</strong> &amp; <strong>total(X)</strong> – vrací součet zadaného prvku přes celou skupinu; zatímco <code>sum()</code> odpovídá SQL-standardu a vrací NULL, když nemá co sečíst, nestandardní <code>total()</code> v uvedeném případě vrátí <code>0.0</code>
    </li>
    <li>
      <strong>avg(X)</strong> – vrací průměr ze všech non-NULL hodnot <code>X</code> ve skupině, případně NULL, není-li z čeho průměr počítat; řetězce a <code>BLOB</code>y, které není možné převést na číslo, jsou vyhodnoceny jako 0
    </li>
    <li>
      <strong>group_concat(X[,Y])</strong> – vrací řetězec vytvořený slepením non-NULL hodnot sloupce <code>X</code> (pořadí není zaručeno); oddělovačem je čárka nebo parametr <code>Y</code>, je-li zadán
    </li>
  </ul>
  <p>
    Každou agregační funkci je možné doplnit klíčovým slovem <code>distinct</code>. Kupříkladu <code>count(distinct X)</code> pak vrátí celkový počet navzájem různých non-NULL hodnot <code>X</code>.
  </p>

</slide>
<slide title="Funkce – práce s datem">

  <p>
    SQLite poskytuje pro <strong>práci s datem</strong> kromě univerzální (a tudíž také pěkně komplexní) funkce <code>strftime(format, timestring, modifier, modifier, ...)</code> následující pomocné funkce:
  </p>
  <ul>
    <li>
      <strong>date()</strong> – vrací datum jako <code>YYYY-MM-DD</code>;
    </li>
    <li>
      <strong>time()</strong> – vrací čas jako <code>HH:MM:SS</code>;
    </li>
    <li>
      <strong>datetime()</strong> – vrací <code>YYYY-MM-DD HH:MM:SS</code>;
    </li>
    <li>
      <strong>julianday()</strong> – vrací počet dní od poledne v Greenwichi 24. listopadu 4714 před Kristem.
    </li>
  </ul>
  <note>
    Všechny funkce přebírají parametry ve formě <code>(timestring, modifier, modifier, ...)</code>. Podrobnosti viz <a href="http://sqlite.org/lang_datefunc.html" class="external">originální dokumentace</a>.
  </note>
  <p>
    Příklady:
  </p>
  <example lang="sql">
-- ukaž aktuální čas
SELECT time('now');

-- ukazž aktuální datum
SELECT date('now');

-- poslední den současného měsíce
SELECT date('now','start of month','+1 month','-1 day');

-- datum prvního úterý v říjnu letošního roku
SELECT date('now','start of year','+9 months','weekday 2');
  </example>

</slide>
<slide title="Funkce – základní">

  <p>
    SQLite obsahuje veliké množství základních funkcí. Namátkou:
  </p>
  <ul>
    <li>
      <strong>abs(X)</strong> – 
    </li>
    <li>
      <strong>length(X)</strong> – počet znaků pro řetězce, počet bajtů pro <code>BLOB</code>y
    </li>
    <li>
      <strong>lower(X)</strong> – 
    </li>
    <li>
      <strong>quote(X)</strong> – vrací svůj argument v podobě vhodné pro vložení do dalších SQL-příkazů
    </li>
    <li>
      <strong>random()</strong> – 
    </li>
    <li>
      <strong>replace(X, Y, Z)</strong> – zamění výskyty <code>Y</code> v <code>X</code> za <code>Z</code>
    </li>
    <li>
      <strong>substr(X, Y, Z)</strong> – podřetězec z <code>X</code> od <code>Y</code> po <code>Z</code>
    </li>
    <li>
      <strong>trim(X[,Y])</strong> – odstraní mezery (nebo cokoliv z <code>Y</code>) na obou koncích řetězce <code>X</code>
    </li>
    <li>
      <strong>upper(X)</strong> – 
    </li>
  </ul>
  <note>
    Kompletní přehled viz <a href="http://sqlite.org/lang_corefunc.html" class="external">originální dokumentace</a>.
  </note>

</slide>
<slide title="GROUP BY &amp; HAVING">

  <p>
    Příkaz <code>SELECT</code> je možné doplnit o podpříkazy <code>GROUP BY</code> a <code>HAVING</code>:
  </p>
  <ul>
    <li>
      <code>GROUP BY</code> – určuje podmínky, podle kterých budou vstupní řádky (již „profiltrované“ příkazem <code>SELECT-FROM-WHERE</code>) seskupeny na výstupu do jednoho řádku souhrnného;
    </li>
    <li>
      <code>HAVING</code> – návratové řádky již vybrané podmínkou <code>GROUP BY</code> ještě dále omezí.
    </li>
  </ul>
  <p>
    Často je tato kombinace používána spolu s agregačními funkcemi.
  </p>

</slide>
<slide title="Příklad – GROUP BY (1)">

  <p>
    Pro příklad prozkoumejme města z databáze <a href="_files/world/world.db">world</a>.  Vypišme si nejdříve města s více jak pěti milióny obyvatel tak, jak jsou zanesena v databázi:
  </p>
  <example layout="vertical">
    <program src="_files/groupby/groupby.1.sql" lang="sql" />
    <out src="_files/groupby/groupby.1.out" lang="text" />
  </example>

</slide>
<slide title="Příklad – GROUP BY (2)">

  <p>
    Nyní je uspořádejme do skupin podle kódu země a jména:
  </p>
  <example layout="vertical">
    <program src="_files/groupby/groupby.2.sql" lang="sql" />
    <out src="_files/groupby/groupby.2.out" lang="text" />
  </example>
  <note>
    Seskupovací podmínka se skládá ze dvou prvků, které jsou dohromady poměrně jedinečné (pokud nebudou v jednom státě dvě tak veliká města se stejným názvem). Je poučné se podívat, co se stane, omezíme-li podmínku pouze na <code>GROUP BY CountryCode</code>:
    <example lang="text">
BRA|Rio de Janeiro|5598953
CHN|Tianjin|5286800
COD|Kinshasa|5064000
COL|Santafé de Bogotá|6260862
EGY|Cairo|6789479
GBR|London|7285000
IDN|Jakarta|9604900
IND|Delhi|7206704
IRN|Teheran|6758845
JPN|Tokyo|7980230
KOR|Seoul|9981619
MEX|Ciudad de México|8591309
PAK|Lahore|5063499
PER|Lima|6464693
RUS|Moscow|8389200
THA|Bangkok|6320174
TUR|Istanbul|8787958
USA|New York|8008278
    </example>
    Výsledkem je výstup, který je něco platný snad pouze jako náhodný výběr z množiny prvků.
  </note>

</slide>
<slide title="Příklad – GROUP BY (3)">

  <p>
    Teď se podíváme, kolik takových měst v každé konkrétní zemi je a kolik mají dohromady obyvatel:
  </p>
  <example layout="vertical">
    <program src="_files/groupby/groupby.3.sql" lang="sql" />
    <out src="_files/groupby/groupby.3.out" lang="text" />
  </example>
  <note>
    Ačkoli tu zdánlivě máme opět nejednoznačnost seskupovacího klíče jako v předchozím případě, ve skutečnosti jsme díky použití agregačních funkcí z každé skupiny zemí „vytáhli“ souhrné informace za všechny seskupené řádky (počet měst neboli vlastně seskupených řádků &amp; součet počtu obyvatel ve všech těchto městech dohromady).
  </note>

</slide>
<slide title="Příklad – GROUP BY s HAVING">

  <p>
    Nakonec poslední dotaz ještě omezíme pouze na ty státy, kde v tak lidnatých městech žije dohromady alespoň deset miliónů lidí:
  </p>
  <example layout="vertical">
    <program src="_files/groupby/groupby.4.sql" lang="sql" />
    <out src="_files/groupby/groupby.4.out" lang="text" />
  </example>
  <note>
    Podmínka <code>HAVING</code> zde – jak se snaží naznačit už odsazení – výstupní řádky předchozího příkazu <code>GROUP BY</code> ještě dále omezuje.
  </note>

</slide>
<slide title="Transakce">

  <p>
    Stala-li by se data v databázi nekonzistentní (např. jedna z tabulek by se snažila odkazovat na údaje v jiné, které by tam třebas kvůli výpadku proudu a následně nedokončeným operacím ale nebyly), nebyla by nám databáze nadále už moc platná. Hlavním způsobem, jak se databáze chrání proti těmto chybám, je <strong>transakční zpracování</strong>.
  </p>
  <p>
    Transakce je celek operací, které buď budou provedeny všechny, nebo nebude provedena žádná. Prakticky to znamená, že databáze po dobu vykonávání transakce udržuje dva různé stavy – je-li transakce úspěšně dokončena, je původní stav databáze nahrazen novým, dojde-li naopak k chybě při zpracování transakce, nový stav databáze se neprojeví.
  </p>
  <p>
    Programově se provedení transakcí liší případ od případu a databázi od databáze. V SQLite se bloky příkazů, které mají být provedeny společně v rámci jedné transakce, uzavírají do následujícího bloku:
  </p>
  <example lang="sql">
BEGIN TRANSACTION;
...
COMMIT;
  </example>

</slide>
<slide title="„Triggery“ – obsluha událostí">

  <p>
    Často může být užitečné, aby databáze dokázala zareagovat na jisté události jistým způsobem. Tento mechanizmus se nazývá <strong>trigger</strong> a dokáže reagovat na <code>DELETE</code>, <code>INSERT</code> a <code>UPDATE</code>.
  </p>
  <p>
    Jako příklad uveďme přiřazení NULL do odkazu na oddělení u všech zaměstnanců, kteří pracovali v oddělení, které jsme právě zrušili:
  </p>
  <example lang="sql">
CREATE TRIGGER uprav_cislo_oddeleni DELETE ON oddeleni
  BEGIN
    UPDATE zamestnanci SET oddeleni = NULL
      WHERE zamestnanci.oddeleni = oddeleni.cislo;
  END;
  </example>
  <note>
    Je vidět, že <em>trigger</em> je zcela samostatná skupina příkazů, každý ukončený vlastním středníkem. (V příkladu je příkaz pouze jeden.)
  </note>
  <note>
    V rámci <em>trigger</em>'u je možné vyvolat např. i <code>ROLLBACK</code>, tedy vrácení databáze do původního stavu. Více viz <a href="http://sqlite.org/lang_createtrigger.html" class="external">dokumentace</a>.
  </note>

</slide>
<slide title="Pár drobností na závěr">

  <p>
    Databáze jsou samy o sobě předmět na nejméně jeden celý semestr, proto jsme se trošku podrobněji seznámili pouze s vybranou podmnožinou příkazů a technik. Zde ještě pár dalších:
  </p>

  <p class="enumerate">
    Příkaz <code>SELECT</code> je možno doplnit o klíčové slovo <code>DISTINCT</code>, které zaručí, že ve výsledku budou pouze jedinečné prvky podle zadaného kritéria, např. následující dotaz do databáze <a href="_files/world/world.db">world</a>..
  </p>
  <example lang="sql">
    SELECT CountryCode FROM City WHERE Population > 5000000;
  </example>
  <p>
    ..vrátí množinu výsledků <code>BRA BRA GBR EGY IDN IND IND IRN JPN CHN CHN CHN CHN COL COD KOR MEX PAK PAK PER THA TUR RUS USA</code>, zatímco..
  </p>
  <example lang="sql">
    SELECT DISTINCT CountryCode FROM City WHERE Population > 5000000;
  </example>
  <p>
    ..vrátí menší množinu <code>BRA CHN COD COL EGY GBR IDN IND IRN JPN KOR MEX PAK PER RUS THA TUR USA</code>.
  </p>
  <note>
    Je si však třeba uvědomit, že podmínka <code>DISTINCT</code> dokáže být pěkně náročná – každý řádek vyhovující dotazu <code>SELECT</code> se musí porovnat s každým již vybraným.
  </note>
  
  <p class="enumerate">
    Prakticky všechny velké moderní databázové systémy obsahují kromě (varianty) dotazovacího jazyka SQL též <em>jazyk procedurální</em>, který můžeme na mnoha místech použít místo SQL. Příkladem budiž <em>PL/SQL</em> databáze Oracle nebo <em>PL/pgSQL</em> databáze PostgreSQL. SQLite toho času žádný takový jazyk nemá.
  </p>

</slide>
<slide title="Kompletní přehled SQL pro SQLite">

  <p>
    Jelikož jazyk SQL a zvláště jeho rozumné použití v praxi je studium na několik semestrů a jelikož každá databázová implementace si o něm myslí něco trošku jiného, pro další podrobnosti vás odkazuji na oficiální dokumetaci:
  </p>
  <blockquote class="center">
    <a href="http://sqlite.org/lang.html" class="external">SQL As Understood By SQLite</a>
  </blockquote>
  <p>
    Tam jsou všechny možné dostupné příkazy k dispozici v podobě myslím velmi přehledných <em>syntaktických diagramů</em>. Samozřejmě se předpokládá, že už o SQL něco víte (a to i více, než je k dispozici v mých úvodních přednáškách).
  </p>

</slide>


</lecture>