1
Úvod
Milý čtenáři,
V dnešní době prakticky ve všech oblastech lidské činosti roste význam
informací. Se zájmem o informace roste i objem produkovaných dat. Objemná
data je však nutné nějakým způsobem ukládat, zpracovávat a vytěžovat. Pro tyto
procesy jsou určeny databázová systémy, zkráceně databáze. Následující učební
text Vás provede základy dnešních databázových systémů. V první kapitole jsou
popsány typy databází, základní terminologie a způsob schematického znázornění
tzv. relačního datového modelu. V následujících kapitolách je vysvětlován jazyk
SQL, pomocí jehož příkazů pracujeme s databázovým systémem. V kapitole 2 a 3
jsou přehledně sepsány základní operace a nabízené funkce SQL, které vám po
zvládnutí umožní pracovat s libovolnou relační databází v rozsahu vkládání,
úprav, mazání dat a samozřejmě získávání dat. Budete schopen vyhledat
požadované záznamy z jedné i z několika tabulek a připravit jednoduché
sumarizační reporty. Kapitoly 4 a 5 již popisují pokročilejší techniky, které
využijete při dolování dat, při tvorbě analytických a statistických reportů. Šestá
kapitola je zaměřena na vyhledávání v textových datech a použití regulárních
výrazů. Databázové systémy taktéž nabízí procedurální zpracování dat, které je
rozebráno v sedmé kapitole. Osmá kapitola popisuje způsob importu data do
databáze a možnosti exportu dat do jiných systémů. Poslední kapitola vás
seznámí se základy XML jazyka, což je univerzální formát pro migraci dat mezi
počítačovými systémy. Kromě základů samotného XML jsou popsány i
databázové funkce pro práci s ním.
Celý text je primárně zaměřen na 2 konkrétní databázové systémy: komerční
databázový systém ORACLE a open source produkt POSTGRESQL. Základní
vlastnosti SQL je však možné uplatnit i v jiných relačních databázových
systémech. Text je doplněn o praktické příklady, které je možné vyzkoušet
v databázi POSTGRESQL. Tato databáze je zdarma ke stažení, popis její
instalace je popsán v úvodní kapitole. Příklady a cvičení jsou laděny do
biomedicínské oblasti, nicméně nabyté znalosti lze uplatnit v jakékoliv oblasti.
Proč studovat problematiku databázových systémů?
Na databázové systémy dnes narazíme prakticky všude a specialistů, kteří umí
s nimi pracovat je stále nedostatek. Zvládnutí SQL znamená být schopen získávat
informace z primárních systémů a mít dobré uplatnění na trhu práce.
Pro koho je text určen?
Pro všechny, kteří se chtějí naučit pracovat s relační databází. Příklady jsou
laděny do oblasti medicíny, nicméně vysvětlovaná principy a získané znalosti lze
uplatnit kdekoliv. Pro studium nejsou nutné žádné speciální předpoklady, pouze
pokročilá znalost práce s počítačem, logické myšlení a trpělivost.
Co budu umět po přečtení?
Nic. ;-) Samotným přečtením získáte přehled o problematice, praktické zvládnutí
SQL však vyžaduje praktická cvičení a sestavování vlastních SQL dotazů. Ideální
je po přečtení a zvládnutí praktických cvičení vytvořit si vlastní databázi a začít
zkoušet „dolovat“ informace.
2
1 Úvod do práce s databází
Výstupy
Po přečtení této kapitoly student:
Zná základní typy databázových systémů
Orientuje se v dostupných databázových produktech
Umí instalovat databázový server (Postgresql)
Umí se prostřednictvím databázového klienta připojit k databázi
Rozumí základní terminologii relačních databází a uložení dat v databázových
tabulkách
Zná základní datové typy atributů, které jsou používány v relačních databázích
Umí číst schematické znázornění datového modelu, rozumí problematice
datového modelování
Pod pojmem databáze rozumíme softwarovou aplikaci, jejímž primárním úkolem je
zajistit jednak ukládání strukturovaných dat, jednak zajistit co možná nejrychlejší a
nejefektivnější přístup k těmto datům. Od moderní databáze se taktéž očekává, že zajistí
bezpečnost dat, jak z pohledu omezení přístupu k datům pouze pro oprávněné osoby, tak z
pohledu zachování integrity dat při víceuživatelském využívání vložených dat.
1.1 Rozdělení databází podle typu
Podle způsobu ukládání dat rozlišujeme několik typů databází:
Hierarchická databáze – tato databáze je založená na hierarchickém modelu.
Logické uspořádání dat má stromovou strukturu.
Síťová databáze – tato databáze je založená na síťovém modelu, ve kterém jsou
data logicky i fyzicky uspořádána jako uzly rovinného grafu. Každý záznam může
být spojený s libovolným počtem dalších záznamů.
Objektová databáze – tato databáze je založena na objektech, jejich zapouzdření a
dědičnosti. Místo tabulek jsou zde uloženy přímo objekty, včetně svých vlastností,
a místo řádků se ukládají samotné instance objektů.
Relační databáze – tato databáze založená na relačním modelu, v němž jsou data
logicky uspořádána do relací, tj. výsledků kartézského součinu nad doménami
neboli množinami údajů. Relační databáze je založena na tabulkách, které
obvykle chápeme tak, že uchovávají informace o relacích mezi jednotlivými
záznamy.
Hierarchické a síťové databáze se poprvé objevují v 60. letech minulého století na
sálových počítačích. Jedním z prvních průkopníků databází byl Charles Bachman. Jedním z
prvních databázových systémů byl IMS, který byl vyvinut firmou IBM pro program letu na
Měsíc Program Apollo.
V roce 1970 začínají zveřejněním článku E. F. Codda první relační databáze, které
pohlížejí na data jako na tabulky. Kolem roku 1974 se vyvíjí první verze dotazovacího jazyka
SQL. Vývoj této technologie po 10 letech přinesl výkonově použitelné systémy, srovnatelné
se síťovými a hierarchickými databázemi.
3
V 90. letech 20. století se začínaly objevovat první objektově orientované databáze,
jejichž filozofie byla přebírána z objektově orientovaných jazyků. Tyto databáze měly podle
předpokladů vytlačit relační systémy. Původní předpoklady se však nenaplnily a vznikla
kompromisní objektově-relační technologie.
1.2 Databázové produkty
V dnešní době se můžeme setkat s celou řadou databázových systémů od mnoha výrobců.
Škála je velmi široká od open source produktů až po velmi drahé komerční produkty. Pokud
stojíme pře problémem, který produkt zvolit, měli bychom kromě dostupných finančních
prostředků definovat naše požadavky:
Operační systém, na kterém budeme databázový systém provozovat
Počet uživatelů, kteří budou k datům přistupovat
Objem zpracovávaných dat
Požadavky na výkon
Požadavky na dostupné funkce
Požadavky na dostupnost a bezpečnost dat
Požadavky na technickou podporu dodavatele
Nemusí vždy platit, že komerční produkty jsou kvalitnější než produkty zdarma, pro
menší projekty jsou open-source produkty zcela dostačující, komerční produkty najdeme v
oblasti kriticky důležitých systémů, kde jsou požadavky na vysoký výkon a vysokou
dostupnost a spolehlivost.
Následující abecedně setříděný přehled produktů není zdaleka kompletní, nicméně
obsahuje nejrozšířenější databázové produkty.
Cache - Nejrozšířenější objektová databáze, která umožňuje k datům přistupovat také
pomocí SQL dotazů. Nabízí transakční zpracování, masivní škálovatelnost, real-time analýzy
a webový přístup k databázi. Připravená pro Javu a .NET a certifikovaná na Red Hat a SUSE
Enterprise Linux.
DB2 - komerční relační databáze firmy IBM pro aplikace s vysokými nároky na
dostupnost a zabezpečení dat.
Firebird - původně známá jako databáze Interbase, která byla vyvíjená firmou Borland,
následně uvolněn její zdrojový kód dále vyvíjena open source komunitou jako relační
databáze Firebird. Volně dostupná kompaktní databáze instalovatelná na širokém spektru
operačních systémů, s podporou transakčního zpracování, uložených procedur i triggerů. Ve
srovnání s POSTGRESQL menší nabídka funkcí, výhodou je snadné zálohování a přenos
databázového souboru (veškerá data jsou ukládána v jednom souboru).
ObjectDB - Volně dostupná (pro osobní a nekomerční použití) objektová databáze pro
Javu. Může pracovat jak v klient-server, tak v embedded režimu.
Objectivity/DB - Jedná se o distribuovanou (data mohou být transparentně replikována
na různých serverech), objektovou databázi s nejširším výběrem API – pro C++, Javu,
Python, Smalltalk a obecný ODBC. K dipozici je i 64bitová verze. Stáhnout lze 60denní trial,
nebo plnou placenou.
ObjectStore - Objektová databáze, která může být použita v C++, nebo Javě. Nabízí
robustní systém cachování, transakční zpracování, online zálohy a replikace, škálování nebo
4
podporu clusterů. K dispozici je testovací embedded verze omezená na jeden proces, případně
placená plná verze.
MS ACCESS - relační databázová aplikace firmy Microsoft určená pro jednouživatelské
aplikace pro prostředí operačního systému Widows, součást kancelářského balíku MS Office.
MS SQL - komerční relační databáze firmy Microsoft určená pro platformu MS
Windows a související Microsft technologie (.NET).
MySQL - open source relační databáze, kterou nedávno převzala firma ORACLE. Je
velmi rozšířená jako databáze pro webové aplikace. Dokáže zpracovávat velké objemy dat,
méně vhodná je pro aplikace vyžadující efektivní transakční zpracování.
ORACLE - komerční relační databáze s podporou objektově relační technologie. Patří ke
špičce v oblasti databázových systémů se širou škálou specificky zaměřených modulů
(analýza textu, datové sklady, geografická data a další). Databáze je certifikovaná pro systémy
MS Windows i vybrané distribuce UNIX a LINUX. Zdarma je dostupná okleštěná a
limitovaná verze ORACLE Express.
POSTGRESQL - open source alternativa k databázi ORACLE, volně dostupná pro
širokou šálu operačních systémů. Nabízí řadu SQL funkcí, vlastní procedurální jazyk PgSQL
pro vytváření uživatelských procedur a triggerů.
1.3 Přístup k databázi
Databázové systémy lze rozdělit na jednouživatelské a víceuživatelské. Ty první jsou
obvykle používány pro lokální zpracování dat nebo jako úložný systém pro lokální aplikace.
Nepočítá se s tím, že by data využívalo více uživatelů současně. Příkladem může být MS
ACCESS. Tato aplikace v sobě obsahuje jak datové úložiště a SQL engine, tak doplňující
nástroje pro tvorbu reportů či elektronických formulářů. Vše je dostupné v jedné aplikaci.
Oproti tomu víceuživatelské databáze jsou navrženy tak, aby dokázaly zpracovat současnou
manipulaci dat několika uživatelů. Jsou rozděleny na tzv. serverovou část a klienta. Server je
aplikace, která běží nepřetržitě na vybraném počítači, zajišťuje ukládání dat a vyřizuje SQL
příkazy klientů. Klient je samostatná aplikace, pomocí které jsme schopni připojit se k
databázovému serveru a ovládat ho pomocí příkazů. Klient nám také zobrazuje z databáze
získaná data. Klientských aplikací k jednomu databázovému produktu je často více. Liší se
obvykle uživatelským komfortem. Řádkový klient má jen jednoduché uživatelské prostředí v
podobě příkazového řádku, kde píšeme SQL dotazy, které klient odesílá serveru. Získaná data
klient jednoduše vypíše na obrazovku nebo zapíše do souboru. Grafický klient pak umožňuje
prohlížení databáze pomocí menu, SQL příkazy je možné skládat pomocí průvodců, data lze
exportovat do různých formátů.
Z výše uvedeného vyplývá, že před tím než začneme s databází pracovat, musíme buď
instalovat databázový server, nebo získat potřebné informace o tom, kde je server
provozován. Druhým krokem je instalace databázového klienta na náš počítač. Pokud
instalujeme databázový server, je obvykle součástí instalačního balíčku i instalace klienta. V
případě POSTGRESQL stačí na operačním systému windows spustit instalační soubor pro
vybranou verzi (např. postgresql-9.2.4-1-windows-x64.exe), který lze stáhnout na stránkách
projektu http://www.postgresql.org. Kromě serveru se nainstaluje řádkový klient psql a
grafický klient pgAdmin.
5
1.3.1 Připojení k POSTGRESQL
K databázi POSTGRESQL se v prostředí MS Windows připojíme spuštěním skriptu
runpsql.bat, který součástí instalace klienta. Skript se nás postupně dotáže na:
Jméno nebo IP adresu serveru, kde běží databázový server. Výchozí hodnota je
náš počítač (localhost).
Jméno databáze na daném serveru. Na databázovém serveru můžeme mít uložena
data více nezávislých projektů. Při připojování specifikujeme, se kterou datovou
strukturou chceme pracovat. Výchozí hodnota je postgres, což je výchozí datová
struktura (databáze) na databázovém serveru POSTGRESQL.
Port, na kterém je databázová aplikace na daném serveru dostupná. Výchozí
hodnota je 5432.
Přihlašovací login. Výchozí účet je postgres, což je hlavní administrátorský účet
databázového serveru.
Heslo. Výchozí heslo k účtu postgres volíme během instalace serveru.
Pokud se připojujeme k jinému než lokálnímu serveru, musí nám administrátor databáze
výše uvedené údaje poskytnout.
Pokud se k databázovému serveru připojujeme přes klienta pgAdmin, zadáváme stejné
údaje, ale v grafickém režimu. V okně Strom objektů vybereme nejprve server, zadáme heslo
a v následujícím kroku zvolíme databázi, se kterou chceme pracovat. Pokud se chceme
připojit poprvé ke vzdálenému serveru, zvolíme v menu Soubor => Přidat server.
Obrázek 1 - Aplikace pgAdmin
6
Obrázek 2 - pgAdmin - registrace nového serveru
Vyplníme potřebné údaje, položku Služba necháváme obvykle prázdnou.
Jakmile jsme připojeni k serveru, zvolíme kliknutím databázi, se kterou chceme pracovat.
Seznam databází obsahuje minimálně výchozí databázi postgres. Novou databázi vytvoříme
kliknutím pravým tlačítkem na Databáze a výběrem položky Nová databáze. V dialogovém
okně stačí vyplnit jméno nové databáze, ostatní položky je možné nechat ve výchozím stavu.
Alternativní cestou je vytvoření databáze pomocí SQL příkazu. V řádkovém klientu psql
můžeme příkazy SQL zadávat hned po připojení, v pgAdmin nejprve klikneme na vybranou
databázi (postgres) a v hlavní liště klikneme na SQL ikonu. Příkaz pro vytvoření databáze v
POSTGRESQL vypadá následovně:
CREATE DATABASE moje_db
WITH ENCODING='UTF8'
CONNECTION LIMIT=-1;
Název nové databáze je "moje_db", encoding označuje znakovou sadu databáze (UTF8 je
univerzální sada podporující veškeré světové jazyky), connection limit uvádí maximální počet
současně připojených uživatelů (-1 značí neomezený počet).
1.3.2 Připojení k ORACLE
Pro připojení k databázi ORACLE opět potřebujeme nejprve instalovat klienta databáze.
Klienta je možné po registraci zdarma stáhnout na http://www.oracle.com. Instalační
průvodce nabízí několik variant instalace. Pro připojení k databázi potřebujeme instalovat
buď řádkový klient sqlplus nebo grafický klient sqldeveloper.
7
Klient sqlplus se hodí hlavně pro automatické spouštění hotových skriptů a sestav, pro
dolování dat a testování SQL příkazů je mnohem vhodnější sqldeveloper, proto se dále
zaměříme na něj. Způsobů připojení k ORACLE databázi skrze sqldeveloper je několik typů a
se správným výběrem nám obvykle musí poradit administrátor databáze. Potřebujeme znát
kromě loginu a hesla buď tzv. network alias, nebo IP adresu serveru, port a tzv. SID. Pokud
chceme používat network alias, musíme mít na svém počítači nakonfigurován soubor
tnsnames.ora, který najdeme ve složce klienta v podadresáři /network/admin. Tento soubor
obsahuje seznam ORACLE databází, ke kterým se můžeme připojit. Soubor nám musí
poskytnout administrátor databáze.
Pokud máme možnost připojovat se pomocí Network alias, klikneme v SQLDeveloper v
záložce Connections na zelené plus a vytvoříme nové připojení. Definujeme zde název
připojení, uživatelské jméno, heslo a v Connection type vybereme možnost TNS. Pokud
máme správně konfigurovaný soubor tnsnames.ora, můžeme v roletce Network alias vybrat
cílovou databázi. Tlačítkem Save definici připojení uložíme, tlačítkem Connect se připojíme
k databázi.
Obrázek 3 - ORACLE Sqldeveloper - konfigurace připojení k databázi přes TNS záznam
Pokud nemůžeme využít tuto variantu připojení, zvolíme variantu Connection type Basic,
vyplníme Hostname a položku SID. Obrázek uvádí variantu, pokud se připojujeme k lokální
instalaci ORACLE serveru ve verzi Express.
8
Obrázek 4 - ORACLE Sqldeveloper - konfigurace přímého připojení k databázi
Poznámka: Položku Role ponecháváme na možnosti default kromě případu, že se
připojujeme pod administrátorským účtem SYS, pro který je nutné zvolit roli SYSDBA.
Oproti PostgreSQL v databázi ORACLE jako uživatel nevytváříme vlastní databázi. Po
přihlášení máme k dispozici vlastní uživatelské schéma (odpovídá "databázi" v PostgreSQL),
kde vytváříme vlastní databázové objekty. Pokud jsou na serveru uchovávána data více
nezávislých aplikací, bude mít každá tato aplikace vlastní uživatelský účet a s ním spojené
schéma. Přistupovat ze svého účtu do jiného schématu můžeme v případě, že nám vlastník
schématu udělí patřičná oprávnění.
1.4 Datová struktura relační databáze
Relační databáze ukládají data do tabulek, na které se můžeme dívat jako na
dvojrozměrné pole nebo jako na matici. Pojem "relační" pochází z anglického "relation", což
je termín z relační algebry, kde označuje výsledek kartézského součinu nad doménami.
Přestože relační databáze vycházejí z relační algebry, její znalost pro užívání databáze není
nikterak nutná. V této publikaci se termínu relace a doména budeme vyhýbat, místo toho se
podíváme na relační databáze z praktického pohledu. Tabulku relační databáze tvoří 1 až n
sloupců, kde každý sloupec představuje jeden atribut ukládaného objektu. Záznamy
jednotlivých objektů pak tvoří řádky tabulky. Objektem může být cokoliv z reálného světa, co
chceme popsat a uložit v tabulce, například student, učitel, pacient, vyšetření, atd. Tabulku
vytvoříme tak, že definujeme příkaz SQL jazyka, ve kterém specifikujeme název tabulky a
jednotlivé sloupce (atributy). U každého sloupce uvádíme sadu parametrů, přičemž povinné
jsou dva: jméno sloupce a datový typ.
1.4.1 Jména databázových objektů
Při práci s databází pojmenováváme nejen tabulky a jejich sloupce, ale veškeré vytvářené
objekty. Limity pro pojmenovávání se liší mezi databázovými systémy, hlavně pokud jde o
maximální délku jména. Pokud se chceme vyhnout problémům se jmény, dodržujeme
následující pravidla:
9
používáme pouze písmena anglické abecedy a číslice
jméno začíná vždy písmenem
místo mezer používáme znak podtržítka "_"
používáme srozumitelná jména, ale snažíme se omezit jejich délku (max. 30
znaků)
Databáze standardně nerozlišují velikost písmen v názvech a klíčových slovech,
doporučuje se proto používat pro jména buď pouze malá písmena, nebo jen velká.
1.4.2 Datové typy
Datový typ nám definuje, jaké hodnoty budeme schopni do daného sloupce ukládat.
Základní datová typy jsou text, číslo, datum a LOB. LOB je specifický datový typ, který
použijeme v případě, že chceme do tabulky ukládat objemná data jako je obrázek, hudba,
video nebo velmi dlouhý text. Od těchto základních typů odvozuje každý databázový systém
své specifické typy a podtypy. Nejpoužívanější datové typy v systému ORACLE a
PostgreSQL popisuje tabulka.
Tabulka 1 - Datové typy
Obecný typ ORACLE POSTGRESQL
Text VARCHAR2 (max. délka) VARCHAR (max. délka)
Číslo NUMBER (číslic, des. míst) NUMERIC (číslic, des.míst)
Datum Date, Timestamp Date, Timestamp
LOB BLOB, CLOB Bytea
U textového sloupce definujeme maximální délku textu (1 až limit databáze), který
budeme schopni do sloupce uložit. U číselného sloupce definujeme maximální počet číslic (1
až limit databáze) a počet desetinných míst (0 pro celá čísla). Pro LOB definuje ORACLE
podtypy BLOB pro ukládání binárních dat (obrázek, video, hudba) a CLOB pro ukládání
dlouhého textu (delší než 4096 znaků). Pokud se pokusíme do sloupce tabulky uložit data
neodpovídající specifikovanému datovému typu, ohlásí databáze chybu a data se nevloží.
Kontrola na správný datový typ je jedna ze základních kontrol, které databáze nabízejí.
Přestože bychom mohli všechna data ukládat jako datový typ text nebo dokonce LOB,
nebylo by to efektivní. Jednak jsou čísla efektivněji ukládána než text, jednak pro čísla a
datumy nabízí databáze řadu funkcí, které pro textové položky nemůžeme použít.
1.4.3 Datový model
Databázi obvykle netvoří jen jedna tabulka, ale obvykle několik, desítky či stovky
tabulek. Struktura tabulek databáze, tzv. datový model, odráží modelovanou skutečnost.
Tabulky jsou mezi sebou provázány pomocí klíčů. Klíčem označujeme 1 až n sloupců
tabulky. Rozlišujeme 2 typy klíčů: primární klíč a cizí klíč. Každá tabulka by měla obsahovat
právě jeden primární klíč a 0 až N cizích klíčů. Primární klíč musí být definován tak, aby
obsah sloupce nebo sloupců, které ho tvoří, byl v každém řádku tabulky unikátní. Jinými
slovy hodnota primárního klíče, která je uložena v jednom řádku, se nesmí objevit v žádném
dalším řádku. Ochranu před vložením duplicity do primárního klíče zajišťuje databáze.
Pomocí cizích klíčů se definují vazby mezi tabulkami. Vazby (relationship) se definují
mezi dvojicí tabulek a mohou být třech typů:
10
1:1
1:n
m:n
Poměry odpovídají počtu řádků, které si ve vázaných tabulkách odpovídají. Vazba 1:1
uvádí, že každému řádků z tabulky A se váže právě jeden řádek z tabulky B. Tato vazba je
nejméně častá, protože obvykle není důvod rozdělovat popisovaný objekt do dvou tabulek,
místo toho vytvoříme jednu velkou tabulku. Důvodem rozdělení může být limit databáze na
počet sloupců jedné tabulky. V tom případě mají obě tabulky stejný primární klíč.
Vazba 1:n je nejčastější vazba, pomocí ní definujeme podřízený vztah tabulky B k
tabulce A. Jednomu řádku tabulky A odpovídá 1 až N řádků tabulky B. O tabulce A mluvíme
jako o nadřízené nebo rodičovské tabulce, o tabulce B jako o závislé nebo dětské tabulce.
Jako klasický příklad vazby 1:n je matka a její děti. Matka může mít více dětí, ale každé dítě
má právě jednu matku. Tato vazba se v relační databázi modeluje tak, že primární klíč
rodičovské tabulky vložíme do tabulky dětské, kde o něm mluvíme jako o cizím klíči. Dětská
tabulka má tak jak vlastní primární klíč tak cizí klíč z rodičovské tabulky. Rodičovská tabulka
zůstává nezměněna. Kromě vložení cizího klíče do dětské tabulky definujeme omezení tzv.
constraint, čímž databází sdělíme vytvoření vazby. Databáze následně zajistí, že ke každému
řádku v dětské tabulce existuje právě jeden řádek v rodičovské tabulce. Při vkládání dat
musíme začít vložením řídícího řádku a teprve následně vložit řádek nebo řádky to tabulky
dětské. Naopak pokud data mažeme, musíme odstranit nejdřív záznam v dětské tabulce a
teprve následně v tabulce řídící.
Třetím typem vazby mezi tabulkami je m:n. Příkladem této situace je například vztah
mezi učiteli a studenty. Každý učitel učí více studentů, ale zároveň každý student navštěvuje
hodiny několika učitelů. Pokud chceme namodelovat tuto vazbu, musíme vytvořit třetí
vazební tabulku, ve které skombinujeme primární klíče tabulky učitelů a studentů. Vazební
tabulka tak obsahuje dva cizí klíče, které obvykle tvoří dohromady primární klíč této tabulky.
Tímto způsobem dekomponujeme vazbu m:n na dvě vazby 1:n. Nadefinováním tabulek a
vazeb mezi nimi vzniká datový model databáze. Jeho schematický nákres ukazuje obrázek.
Obdélníky odpovídají jednotlivým tabulkám s jejich atributy, čáry pak zobrazují vazby
mezi nimi. Podle zakončení čar poznáme řídící tabulku od tabulky dětské, u dětské tabulky je
zakončení rozvětvené. Někdy je u zakončení přímo připojen popisek, který uvádí, kolik
dětských záznamů je pro jeden řídící záznam povoleno.
S návrhem datové struktury databáze (datového modelu) souvisí pojem normalizace. Pod
pojmem normalizace rozumíme proces zjednodušování a optimalizace navržených struktur
databázových tabulek. Hlavním cílem je navrhnout databázové tabulky tak, aby obsahovaly
minimální počet redundantních dat. Správnost navržení struktur lze ohodnotit některou z
následujících normálních forem.
1. Nultá normální forma (0NF) - tabulka v nulté normální formě obsahuje alespoň jeden
sloupec (atribut), který může obsahovat více druhů hodnot.
11
2. První normální forma (1NF) - tabulka je v první normální formě, pokud všechny
sloupce (atributy) nelze dále dělit na části nesoucí nějakou informaci, neboli prvky
musí být atomické. Jeden sloupec neobsahuje složené hodnoty.
3. Druhá normální forma (2NF) - tabulka je v druhé normální formě, pokud obsahuje
pouze atributy (sloupce), které jsou závislé na celém klíči.
4. Třetí normální forma (3NF) - tabulka je ve třetí normální formě, pokud neexistují
žádné závislosti mezi neklíčovými atributy (sloupci).
5. Čtvrtá normální forma (4NF) - tabulka je ve čtvrté normální formě, pokud sloupce
(atributy) v ní obsažené popisují pouze jeden fakt nebo jednu souvislost.
6. Pátá normální forma (5NF) - tabulka je v páté normální formě, pokud by se přidáním
libovolného nového sloupce (atributu) rozpadla na více tabulek.
Takto zní oficiální definice normálních forem, v praxi se aplikují první tři, kdy se
snažíme v modelu pro každou tabulku definovat primární klíč a sloupce definovat atomicky,
tedy tak, aby obsahovaly dále smysluplně nedělitelnou informaci. Jako příklad rozdělení na
atomické prvky můžeme uvést například položku bydliště, kterou bychom měli správně
rozdělit na atributy (sloupce) ulice, číslo domu, město a PSČ.
Otázky:
1. Je možné mít v jedné databázi tabulku PACIENT a pacient?
2. Jaký je rozdíl mezi datovým typem BLOB a VARCHAR?
3. Jaký datový typ zvolíte pro telefonní číslo?
Cvičení:
1. Nainstalujte si na svůj počítač server POSTGRESQL pro váš operační systém
2. Vytvořte si vlastní databázi Student.
2 Základy SQL
Výstupy
Zná základní syntax jednoduchých SQL dotazů
Umí pomocí SQL prohlížet a zjišťovat strukturu a obsah databázových tabulek
Umí pomocí SQL vkládat a mazat záznamy v databázi
Je schopen tvořit sumarizační přehledy dat nad jednou tabulkou
Rozumí pojmu databázová transakce
Zná základní příkazy pro tvorbu, změnu a rušení databázových objektů
2.1 Skupiny příkazů
SQL jazyk se výrazně liší od klasických programovacích procedurálních jazyků. Při
řešení úlohy neříkáme databází, jak má požadovaný úkol splnit, ale pouze formulujeme příkaz
a specifikujeme naše požadavky. Příkazy, které je schopna databáze zpracovat jsou 4 skupiny.
Manipulaci s daty obstarávají příkazy ze skupiny DML (Data manipulation language). Pro
správu datových struktur a objektů jsou určeny DDL (Data definition language) příkazy. Třetí
a čtvrtou skupinou jsou příkazy pro řízení transakcí a oprávnění jednotlivých databázových
uživatelů.
12
2.2 DML příkazy
Nejprve se zaměříme na DML příkazy. Základní DML příkazy jsou 4 a umožňují
provádět následující manipulaci s daty.
SELECT - výběr a zobrazení dat
INSERT - Vkládání dat
UPDATE - Změna dat
DELETE - Smazání dat
2.2.1 SELECT
Jazyk SQL je velice snadný, co se týká slovníku neboli klíčových slov. Nejjednodušší
SQL příkaz obsahuje pouhá 2 klíčová slova.
SELECT * FROM jmeno_tabulky
Prvním slovem je jeden ze 4 uvedených příkazů (SELECT), následuje operátor hvězdička
(*), kterým říkáme, že chceme získat všechny sloupce tabulky. Druhé klíčové slovo (FROM)
uvozuje název tabulky, ze které chceme data získat. Místo operátoru * můžeme zapsat názvy
sloupců oddělené čárkou.
SELECT jmeno_sloupce1, jmeno_sloupce2 FROM jmeno_tabulky
Po spuštění toho dotazu nám databáze zobrazí všechny řádky zvolené tabulky. Pokud
chceme zobrazení omezit jen na vybrané řádky, použijeme další klíčové slovo WHERE a
specifikujeme omezující podmínku. Podmínku tvoří název sloupce, operátor a případně
konstanta. Dotaz pak má podobu
SELECT * FROM jmeno_tabulky WHERE jmeno_sloupce1 = 10
nebo
SELECT * FROM jmeno_tabulky WHERE jmeno_sloupce1 = jmeno_sloupce2
První dotaz zobrazí pouze řádky, které mají ve sloupci jmeno_sloupce1 uloženu 10,
druhý dotaz zobrazí pouze řádky, které obsahují stejnou hodnotu ve sloupci jmeno_sloupce1 a
jmeno_sloupce2. Podmínek je možné specifikovat více, oddělují se pomocí logických
operátorů AND a OR.
Zobrazený seznam je možné nechat setřídit dle vybraných sloupců. Jejich seznam
specifikujeme na konci dotazu za klíčové slovo ORDER BY. Výchozí je třídění vzestupně,
pokud chceme třídit podle některého sloupce sestupně, doplníme klíčové slovo DESC za
název sloupce. Názvy sloupců opět oddělujeme čárkou.
13
SELECT * FROM jmeno_tabulky WHERE jmeno_sloupce1 = jmeno_sloupce2
ORDER BY jmeno_sloupce DESC, jmeno_sloupce2
Kromě výpisu uložených dat umožňuje příkaz SELECT získat základní sumární údaje o
obsahu tabulek. K tomuto slouží agregační funkce, které použijeme místo nebo v kombinaci s
názvem sloupce. Pro zjištění počtu řádků použijeme funkci COUNT. Tuto funkci lze použít
ve třech podobách:
SELECT COUNT(*), COUNT(sloupec1), COUNT (DISTINCT sloupec1) FROM tabulka1
První varianta s hvězdičkou spočítá celkový počet řádků v tabulce1, druhá forma spočítá
řádky, které ve sloupci sloupec1 obsahují hodnotu (jsou neprázdné), třetí forma s klíčovým
slovem DISTINCT spočítá počet unikátních hodnot ve sloupci sloupec1. Mějme tabulku se
třemi řádky:
sloupec1 sloupec2
Ano 1
Ano 2
3
Výsledek dotazu bude 3, 2, 1, tedy 3 řádky celkem, 2 neprázdné řádky, 1 unikátní
hodnota ('Ano').
Mezi agregační funkce patří dále MAX, MIN, AVG, SUM, které pro daný sloupec vypočítají
maximum, minimum, aritmetický průměr a celkový součet. Minimum a maximum lze použít
pro všechny datové typy (u textových sloupců vrací funkce první a poslední záznam dle
abecedy), průměr a sumaci lze počítat pouze nad číselnými datovými typy.
2.2.2 INSERT
Dalším příkazem z rodiny DML je příkaz INSERT pro vkládání záznamů do tabulky.
Syntaxe tohoto příkazu je:
INSERT INTO tabulka1 (sloupec1, sloupec2) VALUES (hodnota1, hodnota2)
Tento příkaz vloží do tabulky tabulka1 jeden řádek, přičemž sloupec1 bude obsahovat
hodnotu hodnota1 a sloupec2 hodnotu2. Počet sloupců musí odpovídat počtu hodnot. Pokud
tabulka obsahuje ještě další sloupce, bude jejich hodnota buď NULL nebo bude rovna
výchozí hodnotě sloupce. Výchozí hodnoty sloupců je možné definovat při vytváření tabulky.
Připomeňme, že textové hodnoty je nutné uvádět uzavřené v jednoduchých apostrofech
('textová hodnota'). Příkaz INSERT je možné kombinovat s příkazem SELECT, pokud
14
chceme záznamy místo zobrazení uložit do tabulky. V tomto případě vypadá syntaxe
následovně:
INSERT INTO tabulka (sloupec1, sloupec2)
SELECT sloupec3, sloupec4 FROM tabulka2
V tomto případě je klíčové slovo VALUES nahrazeno příkazem SELECT a výsledkem
je, že se všechny řádky vybrané příkazem SELECT z tabulky2 uloží do tabulky1. Počet
sloupců a jejich datové typy v části SELECT a INSERT musí odpovídat.
2.2.3 UPDATE
Pokud chceme změnit hodnoty záznamů uložených v tabulce, použijeme příkaz
UPDATE. Jeho syntaxe je následující:
UPDATE tabulka SET sloupec = hodnota, sloupec2 = hodnota2
Tento příkaz změní hodnotu sloupce na definovanou hodnotu ve všech řádcích tabulky. Místo
konstantní hodnoty je možné použít název jiného sloupce či složitější výraz (výpočet).
Klíčové slovo SET je povinné, jednotlivá přiřazení jsou oddělena čárkou. Častěji než měnit
všechny řádky tabulky potřebujeme měnit hodnoty jen vybraných řádků. V tomto případě
musíme doplnit podmínku za klíčové slovo WHERE.
UPDATE tabulka SET sloupec = hodnota WHERE sloupec = hodnota2
Tato varianta změní hodnotu sloupce pouze u těch řádků, kde hodnota sloupce odpovídá
hodnotě2. Stejně jako v případě restrikce řádků v příkazu SELECT je možné další podmínky
připojovat přes operátory AND nebo OR. Pokud výsledné podmínce neodpovídá žádný řádek,
nedojde k chybě, pouze není změněn žádný záznam. SQL dovede v jednom příkaze zaměnit
hodnotu dvou sloupců, což standardní procedurální jazyky nedovedou. Je tedy možné napsat:
UPDATE tabulka SET sloupec1 = sloupec2, sloupec2=sloupec1
Tento dotaz korektně zamění hodnoty sloupců.
2.2.4 DELETE
Posledním ze základních DML příkazů je příkaz DELETE, který odstraní záznamy z
tabulky. Základní syntaxe je:
DELETE FROM tabulka,
který smaže všechny záznamy v tabulce a jde tedy o velmi destruktivní příkaz. Pokud
chceme smazat pouze vybrané záznamy, je nutné stejně jako v případě příkazů UPDATE
specifikovat podmínku za klíčové slovo WHERE.
Opomenutí uvedení podmínky za příkazy UPDATE a DELETE je častá začátečnická
chyba, která má velmi neblahé důsledky. Proto je nutné s těmito příkazy zacházet vždy velmi
opatrně a podmínku testovat nejprve v kombinaci s příkazem SELECT.
15
2.2.5 Databázové transakce
DML příkazy lze zapouzdřit do tzv. transakcí. V rámci transakce můžeme provést
několik DML příkazů, ale teprve po posledním z nich rozhodneme, zda všechny provedené
změny budou platné nebo ne. Databáze zajistí, že budou provedeny buď všechny změny, nebo
žádná. Pro řízení transakcí existují 2 základní příkazy. Příkaz COMMIT transakci potvrdí,
příkaz ROLLBACK transakci zruší. Transakce začíná spuštěním prvního DML příkazu.
Změny prováděné v rámci transakce nejsou před spuštěním COMMIT mimo transakci
viditelné, jinými slovy, ostatní uživatelé vidí stále stejná data jako před zahájením transakce.
Poznámka: transakční režim je nutné v některých databázích či v databázových klientech
explicitně aktivovat, například PostgreSQL běží defaultně v autocommit režimu, kdy po
každém DML příkazu se automaticky provede COMMIT. Toto chování lze změnit
pomocí SET AUTOCOMMIT = off
2.3 DDL příkazy
Pomocí příkazů DDL vytváříme, měníme a rušíme databázové objekty. Slouží k tomu
příkazy CREATE, ALTER a DROP. Syntaxe těchto příkazů se liší pro jednotlivé databázové
objekty a rozdíly jsou i mezi databázemi. Ukážeme se proto jen základní syntax pro tvorbu a
rušení databázových tabulek. Tabulku vytvoříme pomocí příkazů CREATE TABLE.
Následuje v závorkách výčet sloupců a jejich specifikace. Sloupce jsou odděleny čárkou, u
každého sloupce je nutné definovat minimálně jeho datový typ. Příkaz pro vytvoření
jednoduché tabulky v databázi ORACLE může vypadat takto:
CREATE TABLE tabulka1 (
prijmeni VARCHAR2(30),
datum_narozeni DATE,
hmotnost NUMBER(3)
)
VARCHAR2 je ORACLE datový typ určený pro ukládání textů s variabilní délkou (v závorce
je uvedena maximální délka), DATE slouží pro ukládání data a času s přesností na sekundy,
NUMBER je datový typ pro ukládání čísel, jejichž maximální velikost a počet desetinných
míst určuje parametr/y v závorce.
Názvy datových typů se u databází mírně liší, stejná tabulka pro databázi PostgreSQL by
vypadala následovně:
CREATE TABLE tabulka1 (
prijmeni VARCHAR(30),
datum_narozeni TIMESTAMP,
hmotnost NUMERIC(3)
)
Tabulku zrušíme příkazem DROP TABLE tabulka1 . Pozor, zrušením tabulky nenávratně
přijdeme o data, která byla v tabulce uložena. Zachránit nás pak může už jen záloha dat nebo
specifické funkce konkrétní databáze ( funkce flashback v případě ORACLE). DDL příkazy
nelze zařadit do transakce, provádějí se okamžitě a nevratně. Navíc databáze potvrdí všechny
16
naše dosud nepotvrzené DML příkazy.
Pomocí příkazů ALTER lze obvykle přidávat, přejmenovávat nebo rušit sloupce v tabulce a
do určité míry měnit datové typy sloupců. Lze například bez problémů rozšířit v naplněné
tabulce maximální délku textového sloupce, nelze už ovšem měnit datumový prvek na
číselný.
2.4 Příkazy pro řízení přístupu
Poslední skupinou SQL příkazů jsou příkazy pro řízení přístupových oprávnění. V
databázích platí politika, že které objekty vytvořím ty také vlastním a rozhoduji o tom, kdo
další k nim bude mít přístup. Oprávnění se udělují na celé objekty, tedy například na celý
obsah tabulky. Lze však specifikovat, jaké operace bude moci jiný uživatel nad objektem
používat. Typy oprávnění odpovídají DML příkazům. Můžeme tedy zpřístupnit tabulku pro
čtení, tím že povolíme příkaz SELECT, ale zakážeme ostatní operace INSERT, UPDATE,
DELETE. K udělování oprávnění se používá příkaz GRANT. Syntaxe je
GRANT opravneni ON objekt TO uzivatel
GRANT SELECT ON tabulka1 TO Novak
Po spuštění tohoto příkazu může uživatel Novak spouštět příkazy SELECT nad tabulkou
tabulka1. Odejmout přidělená práva můžeme příkazem REVOKE.
REVOKE opravneni ON objekt FROM uzivatel
REVOKE SELECT ON tabulka1 FROM Novak
Otázky:
1. Lze pomocí příkazu UPDATE smazat data v databázi?
2. Lze po příkazu DROP TABLE použít ROLLBACK pro obnovení tabulky?
3. Co je chybného v příkazu
SELECT sloupec FROM tabulka ORDER BY sloupec WHERE sloupec = 5
Cvičení:
1. Vytvořte tabulku (DDL příkaz) se sloupci pro text, desetinné číslo a datum
2. Vložte řádek se svým jménem, výškou v cm a datem narození
3. Změňte údaj o výšce z centimetrů na milimetry, případně i metry
4. Vložte další řádek opět s výškou v cm.
5. Smažte řádek s výškou větší než 200.
17
3 Funkce a operátory v SQL
Výstupy:
Umí použít základní operátory SQL
Ovládá množinové operátory SQL
Má přehled a umí využít v databázích dostupné matematické funkce, funkce pro
práci s textovými řetězci a funkce pro práci s časovými atributy
Zná základní agregační funkce SQL
Jazyk SQL nám umožňuje nejen z databáze získávat uložená data, ale zároveň tato data
na výstupu modifikovat nejrůznějším způsobem. Slouží k tomu bohatá výbava operátorů a
funkcí, kterou dnešní databázové systémy nabízejí. Bohužel standardizace v této oblasti není
vysoká, a proto stejné funkce se v jednotlivých systémech jmenují jinak a v některých
případech se mírně jinak i chovají.
3.1 Testování funkcí a operátorů
Funkce a operátory se v SQL konstrukcích aplikují na jednotlivé řádky z databáze
získaných dat. Pokud si chceme vyzkoušet některou funkci, je nejpřehlednější testovat s co
nejjednodušším SQL dotazem a ideálně na jednom záznamu. Nejjednodušší SQL dotaz je:
SELECT 1+1 FROM tabulka_s_jednim_radkem
Databáze POSTGRESQL umožňuje pro testování funkcí dokonce jednodušší podobu:
SELECT 1 + 1
Tento příkaz zobrazí jeden řádek s jedním sloupcem s překvapivou hodnotou 2. Tento
zápis nelze aplikovat v databázi ORACLE (klíčové slovo FROM je zde vždy povinné), nabízí
ale tzv. pseudotabulku DUAL, která obsahuje právě jeden řádek. Prostý součet v ORACLE
lze tedy realizovat takto:
SELECT 1+1 FROM DUAL
V databázi PGSQL jako generátor řádků slouží funkce generate_series(od, do). Tato
funkce vrací řádky v intervalu parametrů od do.
SELECT 1 + 1 FROM GENERATE_SERIES(1,1)
vrátí 1 řádek
SELECT cislo + 1 FROM GENERATE_SERIES(5,9) as cislo
vrátí 5 řádků s čísly 6 až 10
V dalším textu budou funkce a operátory prezentovány ve variantě pro ORACLE
databázi.
V reálné praxi však zpracováváme pomocí funkcí a operátorů data skutečných tabulek.
Například zobrazení ceny zboží včetně DPH by vypadalo následovně (předpokládá tabulku s
názvem zbozi s číselným sloupcem cena):
SELECT cena * 1.21 FROM zbozi
18
3.2 Operátory
3.2.1 Základní operátory
Mezi základní operátory patří operace sčítání (+) a odečítání (-), násobení (*) a dělení (/).
Sčítat a odečítat lze číselné konstanty a hodnoty číselných sloupců tabulek. Od datumových
sloupců a konstant lze odečítat a přičítat číselné hodnoty, číslo představuje počet přičítaných
či odečítaných dnů. Pokud datový sloupec obsahuje i časovou komponentu, lze odečítat a
přičítat i desetinné číslo, kdy desetinná část odpovídá části jednoho dne, např.:
SELECT datum_narozeni + 5.5 FROM patients
přičte k datu narození 5 dnů a 12 hodin (půl dne).
Odečítat lze také dvě data vzájemně, výsledkem je číslo, které odpovídá počtu dnů mezi
daty.
Násobit a dělit lze pouze číselné datové typy a číselné konstanty.
Pomocí operátoru lze také spojovat textové řetězce, v databázi ORACLE A PGSQL jde
o operátor dvou svislítek (||):
SELECT jmeno || ' ' || prijmeni FROM pacient
Tento dotaz pojí hodnotu sloupce jména a příjmení a oddělí je mezerou.
Operátory a funkce se používají primárně za klíčovým slovem SELECT nebo při
definování podmínek v části WHERE. Jejich vstupem, u funkcí mluvíme o parametrech, jsou
buď názvy sloupců tabulky nebo konstanty. Parametry se uvádějí v kulatých závorkách za
názvem funkce a oddělují se čárkou. Pokud použijeme funkci v kombinaci s názvem sloupce
tabulky, zpracovává funkce či operátor hodnotu každého řádku a výstupem je modifikovaná
hodnota pro každý řádek, který SQL dotaz vrátí. O výsledku funkcí mluvíme jako o
navrácené hodnotě.
3.2.2 Logické operátory
Mezi operátory patří také výrazy, které využíváme při sestavování složitějších podmínek
v části SQL dotazu za WHERE. Jde o tzv. logické operátory: AND , OR a NOT.
Operátory AND a OR spojují dvě podmínky. Pracují tak, že nejprve vyhodnotí pravdivost
těchto podmínek (TRUE/FALSE/NULL, pravda/nepravda/NULL) a následně vyhodnotí
výsledek dle tabulek.
Tabulka 2 - Logický operátor AND
AND TRUE FALSE NULL
TRUE TRUE
FALSE FALSE FALSE
NULL NULL FALSE NULL
Tabulka 3 - Logický operátor OR
OR TRUE FALSE NULL
19
TRUE TRUE
FALSE TRUE FALSE
NULL TRUE NULL NULL
Operátor NOT má jen jeden parametr, nad kterým provede negaci TRUE => FALSE,
FALSE => TRUE.
Podmínka vek > 30 AND vek < 50 je pravdivá(splněna) pro hodnoty sloupce vek mezi 30
a 50, podmínka vek < 30 OR vek > 50 je pravdivá pro věk pod 30 nebo nad 50. Podmínka
vek < 30 AND vek > 50 není pravdivá nikdy, podmínka vek > 30 OR vek < 50 je pravdivá
vždy.
3.2.3 Množinové operátory
Jiným typem operátorů jsou množinové operátory UNION, UNION ALL, INTERSECT a
MINUS. Těmito operátory lze spojovat celé SQL dotazy a získávat tak spojené množiny
výsledků. Pomocí UNION a UNION ALL můžeme sloučit výsledky dvou dotazů, kdy k
výsledným záznamům prvního dotazu se připojí výsledky druhého dotazu:
SELECT patient_id FROM ambulance
UNION
SELECT patient_id FROM nemocnice
Počet sloupců prvního a druhého dotazu musí být stejný a musí být stejného datového
typu. Rozdíl mezi UNION a UNION ALL je v tom, že UNION odstraňuje duplicitní
záznamy, zatímco UNION ALL provede prosté sloučení výsledků (množin).
Operátor INTERSECT provede průnik množin a výsledkem jsou pouze ty záznamy, které
jsou obsaženy ve výsledku obou dotazů. Operátorem MINUS získáme množinu řádků, které
vrátí první dotaz a které zároveň neobsahuje výsledek druhého dotazu.
3.3 Funkce
Funkce lze dělit podle datových typů jejich parametrů na funkce pro textové hodnoty,
číselné hodnoty a datumové hodnoty. Speciální kategorií jsou pak funkce pracující s
libovolným typem a s prázdnou hodnotou NULL.
3.3.1 Funkce nahrazující NULL
NULL hodnotu lze ve výsledku nahrazovat konstantou nebo hodnotou jiného sloupce
pomocí funkcí NVL, NVL2, COALESCE. Funkce NVL a NVL2 lze požít pouze v databázi
ORACLE, funkce COALESCE je dostupná v ORACLE i PGSQL.
Funkce NVL vyhodnotí první parametr a pokud je NULL, výsledkem je druhý parametr.
Pokud první parametr není NULL, je výsledkem funkce první parametr.
SELECT NVL(NULL, 0) FROM DUAL - výsledek je 0
SELECT NVL(5, 0) FROM DUAL - výsledek je 5
SELECT NVL(cena, 0) FROM zbozi - výsledkem je buď hodnota ve sloupci cena
nebo 0, pokud není na daném řádku cena uvedena.
20
Funkce NVL2 má o parametr víc, druhý parametr je výsledkem funkce v případě, kdy
první parametr není NULL, třetí parametr je výsledkem, pokud první parametr je NULL.
SELECT NVL2(999,0,1) FROM DUAL - výsledek je 0, protože 999 není NULL
Funkce COALESCE má neomezený počet parametrů a jejím výsledkem je první NOT
NULL parametr.
SELECT COALESCE (cena_akce, cena_prodej, cena_nakup, 0) FROM zbozi testuje
pro každý řádek tabulky zbozi postupně jednotlivé sloupce a vrátí tu hodnotu,
která není NULL.
3.3.2 Funkce GREATEST a LEAST
Funkce GREATEST a LEAST patří mezi funkce s neomezeným počtem parametrů a lze
je využít pro všechny datové typy, které lze nějakým způsobem třídit. Funkce porovnává
všechny zadané parametry a vrací ten největší resp. nejmenší.
SELECT GREATEST (3,6,9,0), LEAST (3,6,9,0) FROM DUAL
Výsledek je jeden řádek a dva sloupce s hodnotami 9 a 0.
3.3.3 Funkce DECODE a podmíněný výraz CASE
Při práci s daty často potřebujeme ve výstupních sestavách nahrazovat primární data
uložená v databázi za výstižnější popisy, lépe uchopitelné cílových čtenářem výstupu.
Klasický příklad je situace, kdy kategoriální data jsou v databázi kódována čísly, ale pro
výstup potřebujeme textový popis kategorií. V této situaci využijeme funkci decode. Jejím
prvním parametrem je název sloupce, který obsahuje kategoriální data, následují dvojice
parametrů, kde první je vstupní hodnota a druhým je hodnota, kterou chceme původní
hodnotu nahradit. Posledním parametrem decode funkce je volitelně defaultní hodnota, která
se použije v případě, kdy vstupní hodnota neodpovídá žádné předchozímu dekódovacímu
pravidlu. Mějme tabulku pacientů, kde pohlaví pacienta je označeno čísly 1 pro muže a 2 pro
ženy. Dekódovací dotaz bude vypadat následovně:
SELECT DECODE(pohlavi, 1, ‘muž’, 2, ‘žena’, ‘neznámo’) FROM patient
Všimněte si, že výstupní hodnoty mohou být odlišného datového typu než původní
vstupní hodnoty, všechny výstupní hodnoty však musí být stejného datového typu.
Pro dekódovací operaci lze využít i podmíněný výraz, který má však ještě širší uplatnění.
Pomocí podmíněného výrazu, který je uvozen klíčovým slovem CASE, můžeme
vyhodnocovat hodnoty všech sloupců každého řádku a výslednou hodnotu odvozovat pomocí
specifikované podmínky. Konstrukce podmíněného výrazu je následující:
CASE
WHEN podmínka THEN výsledek
WHEN podmínka2 THEN vysledek2
….
ELSE výsledek_n
21
END
Jednotlivé WHEN větve se vyhodnocují postupně a vyhodnocování končí na první
splněné podmínce. Pořadí podmínek je tedy důležité. Pokud není splněna žádná podmínka, je
výsledek výrazu hodnota ve větvi ELSE. Větev ELSE je nepovinná, pokud není definována a
vstupní hodnota nesplní žádnou z uvedených podmínke je výsledkem NULL. Výsledky ve
všech větvích musí být stejného datového typu. Kromě jednoduchého dekódování lze
podmíněný výraz použít pro kategorizaci vstupních hodnot. Například soubor pacientů
můžeme roztřídit podle pohlaví a věku:
SELECT pohlavi, vek,
CASE WHEN pohlavi = 1 AND vek < 30 THEN ̍m30̍
CASE WHEN pohlavi = 1 AND vek < 50 THEN ̍m50̍
CASE WHEN pohlavi = 1 THEN ̍m_nad50̍
CASE WHEN pohlavi = 2 AND vek < 30 THEN ̍z30̍
ELSE ̍z_nad30̍
END kategorie
FROM patient
Jak je naznačeno v příkladu v podmínce za WHEN můžeme použít libovolné logické
operátory stejně jako funkce, které jsou představeny v dalším textu.
̍
3.3.4 Datumové funkce
Nejvýznamnější funkce pro datový typ datum jsou:
Funkce vracející aktuální datum a čas
Funkce pro práci s časovým intervalem
Funkce pro formátování vstupního či výstupního data
Standardní funkcí, která vrátí systémové datum je CURRENT_DATE, v jednotlivých
databázových systémech se však může lišit její návratová hodnota. V ORACLE je
synonymem funkce SYSDATE, která vrátí aktuální datum i čas s přesností na sekundy. V
databáze PGSQL vrací tato funkce pouze datum. Standardní funkcí pro získání aktuálního
času je CURRENT_TIME, která však není dostupná v ORACLE. Funkce
CURRENT_TIMESTAMP je dostupná v ORACLE i PGSQL a vrací datum i čas.
Jak bylo zmíněno výše, pro práci s datumy lze využívat operátory + a - . Takto lze
pracovat, pokud pracujeme v jednotkách dnů či týdnů. Problém je, pokud potřebujeme
pracovat s přesností na měsíce nebo roky. Kalendářní měsíc má 28 - 31 dnů, rok má 365 nebo
366 dnů. Vyjádřit rozdíl mezi dvěma daty jako počet měsíců nebo let bez zaokrouhlování není
tedy zcela triviální. ORACLE nabízí pro tyto úlohy 2 velmi užitečné funkce: ADD_MONTHS
a MONTHS_BETWEEN. První umožňuje přičítat či odečítat měsíce k danému datu, druhá
vrací počet měsíců mezi dvěma daty. Protože rok má vždy 12 měsíců, je možné s pomocí
uvedených funkcí pracovat i s roky. Příklad ukazuje, jak zjistit současný věk osoby, pokud
máme v databázi uloženo datum narození. Současný věk odpovídá rozdílu aktuálního data a
data narození v měsících, pokud toto číslo podělíme 12 a zaokrouhlíme dolů na celé číslo,
dostáváme věk v letech.
22
SELECT TRUNC (MONTHS_BETWEEN (CURRENT_DATE, date_of_birth)/12)
FROM patients
Databáze PGSQL nabízí funkci AGE pro výpočet rozdílu mezi dvěma daty.
SELECT AGE (current_date, date_of_birth) FROM patients
Formátování datumu
Datum a čas je databází interně ukládán jako číslo, které udává počet dnů od databázově
specifického výchozího data. Pokud chceme datum zobrazit jako výsledek SQL dotazu,
databáze transformuje toto číslo do defaultního formátu. Tento formát nám ale často
nevyhovuje. Proto máme k dispozici (ORACLE i PGSQL) formátovací funkci TO_CHAR,
která nám umožňuje přesně specifikovat výstupní formát. Funkce má dva parametry, prvním
je formátované datum (sloupec tabulky s datumovým typem), druhým je specifikace formátu
ohraničená apostrofy. Pro specifikaci formátu se využívají zástupné znaky, seznam nejčastěji
používaných uvádí tabulka.
Tabulka 4 - Symboly pro formátování datumu
Symbol Popis
dd den měsíce
mm kalendářní měsíc (1-12)
yyyy kalendářní rok
hh24 hodiny (0-23)
mi minuty (0-59)
ss sekundy (0-59)
ww číslo týdne v roce
Pokud chceme zobrazit datum a čas dle českých zvyklostí, specifikujeme formát
následovně
SELECT TO_CHAR(CURRENT_TIMESTAMP, 'dd. mm. yyyy hh24:mi:ss') FROM
DUAL
Výsledek je 20. 7. 2013 10:13:23.
Pokud chceme formát datumu vhodný ke třídění, použijeme
SELECT TO_CHAR(CURRENT_TIMESTAMP, 'yyyy-mm-dd') FROM DUAL
S formátem datumu je problém i při vkládání dat do databáze. Aby databáze byla schopna
převést vkládané datum do svého interního formátu, musíme opět přesně specifikovat
vkládaný formát. Použijeme k tomu funkci TO_DATE, jejímž prvním parametrem je textový
řetězec představující vkládané datum, druhý parametr je opět specifikace formátu. Specifikace
formátu je shodná jako v případě funkce TO_CHAR. Příkaz INSERT pro vložení data do
tabulky patients, vypadá takto
INSERT INTO patients (date_of_birth) VALUES
(TO_DATE('13.3.1950','dd.mm.yyyy'))
23
Nezkušení uživatelé databáze často funkce TO_CHAR a T0_DATE zaměňují, což může
způsobit buď chybu při vykonávání SQL příkazu, v horším případě pak vrácení či vložení
chybných dat. Pravidlo je přitom jednoduché, pokud je prvním parametrem textový sloupec
nebo text v apostrofech, musí jít vždy o funkci TO_DATE, pokud je parametrem datumový
sloupec nebo zmíněné funkce vracející aktuální datum a čas, je namístě funkce TO_CHAR.
Funkce TO_CHAR a TO_DATE lze kombinovat, například pokud chceme přeformátovat
datumovou konstantu.
SELECT TO_CHAR(TO_DATE('22.3.2000','dd.mm.yyyy'), 'yyyy-mm') FROM DUAL;
Dotaz v příkladu nejprve specifikované datum převede do interního formátu a následně
ho funkce TO_CHAR zobrazí ve formátu rok-měsíc.
Extrahovat komponenty data lze také pomocí funkce EXTRACT. Pomocí této funkce
můžeme z data získat rok (YEAR), měsíc (MONTH), den (DAY)
SELECT EXTRACT(YEAR FROM TO_DATE('22.3.2000','dd.mm.yyyy')) FROM
DUAL;
3.3.5 Textové funkce
Databáze nabízí také řadu funkcí pro práci s textovými řetězci. Mezi nejpoužívanější a
nejrozšířenější patří funkce uvedené v tabulce.
Tabulka 5 - Funkce pro práci s textem
Název funkce Popis funkce
SUBSTR(text, od, počet) Vrací podřetězec textu dle pozice
INSTR(text, subtext) Hledání podřetězce v textu, vrací pozici
nalezeného podřetězce (pouze ORACLE)
STRPOS(text, subtext) obdoba INSTR (POSTGRESQL)
LOWER (text), UPPER(text) Převede text na malá, resp. velká
písmena
INITCAP (text) Převede první písmeno slov na velké
písmeno, ostatní na malá
LTRIM (text), RTRIM (text) Odstranění mezer (nežádoucích znaků) z
textu (zleva, zprava)
REPLACE(text, puvodni, nove) Nahrazení podřetězce za jiný
TRANSLATE(text, nahradit_co,
nahradit_cim)
Nahrazení po znacích
LENGTH(text) Vrací délku textu ve znacích
Pokud chceme z textu získat určitý podřetězec, použijeme funkci SUBSTR. Parametrem
je zpracovávaný řetězec, druhým parametrem je pořadí prvního znaku, který chceme
extrahovat, třetím nepovinným parametrem je počet znaků, které chceme extrahovat. Pokud
chceme zkrátit řetězec na prvních 5 znaků, použijeme funkci SUBSTR následovně:
SELECT SUBSTR('dlouhý text', 1, 5) FROM DUAL;
24
Funkce, která prohledává text na výskyt specifikovaného podřetězce, se jmenuje v
ORACLE INSTR, v PGSQL pak STRPOS. Parametrem je prohledávaný řetězec a hledaný
text, výsledkem je pořadí prvního znaku nalezeného řetězce nebo nula, pokud podřetězec není
nalezen.
Pokud chceme získat z textu určitou část, která je oddělena definovaným symbolem,
použijeme kombinaci funkcí SUBSTR a INSTR.
SELECT SUBSTR('Výsledek:67',INSTR('Výsledek:67',':')+1) FROM DUAL
Funkce INSTR nejprve nalezne symbol ':', vrátí pořadí tohoto znaku, který předá funkci
SUBSTR. Ta extrahuje text za tímto znakem. Jednička je připočtena proto, aby výsledek
neobsahoval úvodní dvojtečku.
Funkce, která vrátí počet znaků v řetězci, se jmenuje v ORACLE i PGSQL LENGTH.
Parametrem je analyzovaný řetězec.
Při práci s textem je často potřebné sjednocení velikosti písmen. Celý text můžeme
snadno převést na malá písmena (LOWER) nebo velká písmena (UPPER). Tyto funkce
využijeme, pokud budeme chtít pracovat s řetězci bez rozlišení velikosti písmen. Funkci
INITCAP využijeme, pokud chceme, aby každé slovo řetězce začínalo velkým písmenem,
například v případě vlastních jmen.
SELECT INITCAP('klIMeŠ daNIel') FROM DUAL -- výsledek je Klimeš Daniel
Dvojice funcí LTRIM a RTRIM umožňuje odstranit nežadoucí znaky z levé, resp. z pravé
strany řetězce. Defaultně se odstraňují mezery, pokud chceme odstranit specifikované znaky,
uvedeme jejich výčet jako druhý parametr.
SELECT RTRIM('text ++++ ', '+ ') FROM DUAL
Dotaz odstraní zprava znaky mezer a plus, výsledkem je 'text'.
K nahrazování znaků v řetězcích slouží funkce REPLACE a TRANSLATE. Pomocí
REPLACE nahradíme podřetězec definovaný v druhém parametru za text uvedený jako třetí
parametr. Pokud neuvedeme třetí parametr, bude nalezený podřetězec odstraněn. Funkce
REPLACE defaultně nahrazuje všechny nalezené podřetězce. Funkce TRANSLATE slouží
pro nahrazení jednotlivých znaků. Druhým jejím parametrem je seznam nahrazovaných
znaků, třetí parametr pak obsahuje seznam znaků nahrazujících. Nahrazuje se vždy první znak
druhého parametru za první znak třetího parametru, druhý za druhý, atd. Pokud chybí třetí
parametr, jsou znaky druhého parametru odstraněny. Funkce se využívá, např. pokud chceme
odstranit českou diakritiku z textového řetězce.
SELECT TRANSLATE('žluťoučký kůň','žťčýůň','ztcyun') FROM DUAL -- výsledek je
'zlutoucky kun'
3.3.6 Funkce s číselným parametrem
Široká škála funkcí existuje i pro číselné datové typy. Základní přehled uvádí tabulka.
Tabulka 6 - Funkce pro práci s čísly
Název funkce Popis funkce
SIN, COS, TAN Goniometrické funkce s jedním parametrem,
kterým je úhel v radiánech
ABS(číslo) Vrací absolutní hodnotu z čísla
25
POWER(číslo, exponent) Umocňuje číslo na exponent
SQRT (číslo) Vrací druhou mocninu z čísla
LN(číslo), LOG (číslo) Vrací přirozený, dekadický logaritmus čísla
ROUND(číslo, přesnost), CEIL(číslo),
TRUNC(číslo, přesnost), FLOOR(číslo)
Zaokrouhluje číslo
MOD Vrací zbytek po celočíselném dělení
K zaokrouhlování čísel slouží trojice funkcí ROUND, CEIL, TRUNC (resp. FLOOR).
Nabízí všechny základní možnosti zaokrouhlování. ROUND zaokrouhluje od 5 nahoru,
funkce CEIL zaokrouhluje vždy nahoru, funkce TRUNC nebo FLOOR zaokrouhluje vždy
dolů (odříznutí).
SELECT ROUND(5.5), TRUNC(5.5), FLOOR(5.5), CEIL (5.5) FROM DUAL
Výsledkem dotazu je 6, 5, 5, 6
U funkcí ROUND a TRUNC je možné specifikovat druhý parametr a určit jím, na kolik
desetinných míst se má zaokrouhlení provést.
Uvedený výčet funkcí je jen základ ze široké nabídky některých databázových systémů.
Celou nabídku a podrobnosti k jednotlivým funkcím je třeba vždy hledat v dokumentaci
daného databázového systému.
3.3.7 Agregační funkce
Speciální skupinou jsou funkce agregační. Zatímco dosud zmíněné funkce vrací jednu
hodnotu pro každý řádek zpracovávaného SQL dotazu, agregační funkce vrací jen jeden
řádek, tedy agregují všechny řádky do jedné hodnoty. Seznam standardních funkcí uvádí
tabulka
Tabulka 7 - Agregační funkce
Název funkce Popis funkce
COUNT Počet řádků, které jsou výsledkem SQL
dotazu
AVG(sloupec) Vypočítá aritmetické průměr sloupce
SUM(sloupec) Vypočítá sumární součet sloupce
MIN(sloupec) Vrací minimum sloupce
MAX(sloupec) Vrací maximum sloupce
STDDEV(sloupec) Počítá standardní odchylku
MEDIAN(sloupec) Počítá medián ze sloupce
Funkci COUNT lze použít ve třech variantách:
SELECT COUNT(*), COUNT(date_of_birth), COUNT(DISTINCT date_of_birth) FROM
patients
26
Varianta s hvězdičkou vrací prostý počet řádku SQL dotazu, druhá varianta s názvem sloupce
vrací počet řádků s vyplněnou hodnotou daného sloupce (NOT NULL), třetí varianta s klíčovým
slovem DISTINCT vrací počet unikátních hodnot v daném sloupci.
Pro výsledek musí platit vždy
COUNT(*) >= COUNT(sloupec) >= COUNT(DISTINCT sloupec)
Funkce AVG, STDDEV a SUM lze použít pouze pro číselné sloupce, sloupce MIN,
MAX pro všechny základní datové typy.
Agregační funkce nelze kombinovat s ostatními funkcemi. Nelze:
SELECT COUNT(*), LENGTH (patient_id) FROM patients.
Výjimkou jsou funkce, které vrací jednu hodnotu jako např. CURRENT_DATE. I v
tomto případě je však doporučeno aplikovat na tyto funkce funkci agregační:
SELECT COUNT(*), MAX(CURRENT_DATE) FROM patients.
Bez omezení je možné aplikovat standardní funkce na výsledek agregační funkce:
SELECT ROUND(COUNT(*)/10) FROM patients
3.4 Otázky a cvičení
1. Co je výsledkem operace NOT (NULL AND TRUE)?
2. Jak přičtete k datumovému sloupci 3 hodiny?
3. Jak extrahujete posledních 5 znaků z textového řetězce?
4. Jak zjistíte, kolikátý den v roce právě je?
5. Upravte SQL dotaz, aby byl funkční:
SELECT sloupec FROM tabulka1
UNION ALL
SELECT sloupec1, sloupec2 FROM tabulka2
4 Pokročilé SQL
Výstupy:
Připomene si problematiku vytváření vztahů mezi tabulkami
Umí získat data spojením více tabulek
Rozumí rozdílu mezi vnitřním a vnějším spojením tabulek
Zná možnosti zanoření SQL dotazů
27
Ve druhé kapitole jsme se seznámili se základní konstrukcí SQL dotazů. Víme, jak získat
požadované sloupce, jak omezit výpis na určité řádky a jak získat sumární data pomocí
agregačních funkcí. V této kapitole si ukážeme agregování dat pomocí klíčových slov
GROUP BY a HAVING. Protože databáze jsou jen zřídka tvořeny jednou tabulkou,
vysvětlíme si způsob získávání dat z více tabulek pomocí tzv. spojování (joining) tabulek. V
poslední části kapitoly budou vysvětleny možnosti vytváření složitějších zanořených SQL
dotazů.
4.1 Seskupování dat
Podívejme se nejprve na možnost pokročilé agregace. Mějme tabulku pacientů se sloupci
identifikátor pacienta, pohlaví a datumu narození
CREATE TABLE patients
(
patient_id VARCHAR(10),
sex VARCHAR(1) ,
date_of_birth TIMESTAMP
);
Vložíme několik řádků:
INSERT INTO patients (patient_id, sex, date_of_birth) VALUES
('pat1','F',TO_DATE('2.4.1940','dd.mm.yyyy'));
INSERT INTO patients (patient_id, sex, date_of_birth) VALUES
('pat2','M',TO_DATE('30.3.1950','dd.mm.yyyy'));
INSERT INTO patients (patient_id, sex, date_of_birth) VALUES
('pat3','F',TO_DATE('13.8.1947','dd.mm.yyyy'));
INSERT INTO patients (patient_id, sex, date_of_birth) VALUES
('pat4','M',TO_DATE('23.11.1987','dd.mm.yyyy'));
INSERT INTO patients (patient_id, sex, date_of_birth) VALUES
('pat5','F',TO_DATE('3.9.1975','dd.mm.yyyy'));
Nyní se budeme snažit získat sumární přehled o obsahu tabulky. Zajímá nás, kolik
záznamů tabulka obsahuje, kolik je v ní žen kolik mužů a v jakých věkových kategoriích.
Počet řádků již zjistit umíme:
SELECT COUNT(*) FROM patients;
Snadno také zjistíme, počet unikátních hodnot v jednotlivých sloupcích:
SELECT COUNT(DISTINCT patient_id), COUNT(DISTINCT sex),
COUNT(DISTINCT date_of_birth) FROM patients ;
Z výsledku je vidět, že máme tabulku s 5 řádky, kde sloupce patient_id, date_of_birth
obsahují vždy unikátní hodnotu, sloupec sex obsahuje jen 2 unikátní hodnoty, buď F nebo M.
Pokud chceme získat přehled, kolik je v tabulce žen a kolik mužů, můžeme sestavit 2
dotazy:
SELECT COUNT(*) FROM patients WHERE sex = 'F';
SELECT COUNT(*) FROM patients WHERE sex = 'M';
28
SQL standard však nabízí elegantnější způsob, jak tento výsledek získat v jediném
dotazu. Slouží k tomu klíčové slovo GROUP BY, za kterým specifikujeme název sloupce
nebo sloupců, podle kterých chceme data agregovat. GROUP BY se umísťuje v SQL dotazu
za definici podmínky (WHERE), případně za název tabulky, pokud podmínka není
specifikována. Počty pacientů dle pohlaví lze získat následovně:
SELECT sex, COUNT(*) FROM patients GROUP BY sex;
Výsledkem jsou 2 řádky (odpovídá počtu unikátních hodnot v agregovaném sloupci).
Agregační funkce COUNT počítá řádky zvlášť pro každou kategorii agregovaného sloupce.
Použitím klauzule GROUP BY značně omezujeme možnosti výrazů za klauzulí SELECT.
Můžeme zde uvést pouze názvy sloupců uvedených za GROUP BY a agregační funkce.
Častou chybou je pokus vložit za SELECT název neagregovaného sloupce:
SELECT sex, date_of_birth, COUNT(*) FROM patients GROUP BY sex;
Tento dotaz nedává logický smysl, databáze neví, jaké datum narození má zobrazit
(výsledek agregace jsou dva řádky, tabulka ale obsahuje 5 různých dat narození). Je ale
možné požadovat pro každé pohlaví nejstaršího a nejmladšího pacienta:
SELECT sex, MIN(date_of_birth) nejstarsi, MAX(date_of_birth) nejmladsi, COUNT(*)
FROM patients GROUP BY sex;
Agregovat lze podle více sloupců i podle modifikovaných sloupců. Následující dotaz
vrátí přehled počtu pacientů agregovaných přes pohlaví a přes dekádu data narození.
SELECT sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10) AS dekada,
COUNT(*) FROM patients
GROUP BY sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10)
Povšimněte si, že výraz, který tvoří agregovaný sloupec za SELECT, musí odpovídat
výrazu za GROUP BY. Výraz TRUNC(EXTRACT (YEAR FROM date_of_birth)/10)
nejprve extrahuje rok z data narození, tuto hodnotu podělí 10 a funkcí TRUNC provede
zaokrouhlení dolů, čímž dostáváme dekádu narození pacienta.
Co v případě, že bychom chtěli ve výsledku vidět jen záznamy s hodnotou COUNT(*)
větší než 1? Tuto podmínku nemůžeme specifikovat za klíčové slovo WHERE, protože
podmínky za WHERE se aplikují PŘED vlastní agregací na primární data, která do agregace
teprve vstupují. Filtrovat agregovaný záznam je možné pomocí HAVING, která se umísťuje
za GROUP BY výraz:
SELECT sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10) AS dekada,
COUNT(*) FROM patients
GROUP BY sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10)
HAVING COUNT(*) > 1
Podle výsledku agregačních funkcí je možné i třídit pomocí ORDER BY:
SELECT sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10) AS dekada,
COUNT(*) FROM patients
GROUP BY sex, TRUNC(EXTRACT (YEAR FROM date_of_birth)/10)
HAVING COUNT(*) > 1
ORDER BY COUNT(*)
29
Pořadí klíčových slov SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER
BY je dané a nelze je měnit.
4.2 Práce s více tabulkami
Dosud jsme pomocí SQL příkazů pracovali pouze s jednou tabulkou. Databáze jsou však
v drtivém počtu tvořeny sadou tabulek, jejichž struktura odpovídá modelované realitě.
Tabulky jsou mezi sebou svázané vazbou 1:1 nebo 1:n za pomoci cizích klíčů, viz kapitola
datový model. SQL standard umožňuje pracovat s více tabulkami pomocí tzv. join operace.
Mluvíme o spojování tabulek. Spojování tabulek je dvojího typu, existuje vnitřní a vnější
spojení. Rozdíl nejlépe ilustruje příklad, kdy máme 2 tabulky s vazbou 1: n, tabulka pacientů
a tabulka jejich vyšetření, každý pacient může mít 0 až n vyšetření, primárním klíčem v
tabulce pacientů je sloupec id_pacienta, který slouží jako cizí klíč v tabulce vyšetření:
Tabulka 8 Tabulka pacientů
Tabulka 9 Tabulka vyšetření
patient_id datum_vysetreni hmotnost
1 1.2.2012 66
1 1.6.2012 70
2 14.7.2013 69
Tabulka 10Výsledek vnitřního spojení
patient_id jmeno Prijmeni datum_vysetreni hmotnost
1 Jan Starý 1.2.2012 66
1 Jan Starý 1.6.2012 70
2 Karel Nový 14.7.2013 69
Tabulka 11Výsledek vnějšího spojení
patient_id jmeno prijmeni datum_vysetreni hmotnost
1 Jan Starý 1.2.2012 66
1 Jan Starý 1.6.2012 70
2 Karel Nový 14.7.2013 69
3 Olga Mladá
Výsledkem vnitřního spojení jsou řádky, které existují v obou spojovaných tabulkách,
řádky, které existují pouze v jedné z tabulek, jsou vynechány. Oproti tomu vnější spojení
umožňuje získat všechny řádky z jedné tabulky a k nim připojit existující řádky v druhé
patient_id jmeno prijmeni
1 Jan Starý
2 Karel Nový
3 Olga Mladá
30
tabulce. Jedna ze spojovaných tabulek je u vnějšího spojení řídící, k níž se dle podmínky váží
řádky druhé tabulky.
Pro získání dat z více tabulek slouží v SQL klíčové slovo JOIN, které se umísťuje mezi
názvy spojovaných tabulek. Následuje klíčové slovo ON, po kterém je nutné definovat způsob
propojení. Pokud použijeme samotné slovo JOIN, provede se vnitřní spojení:
SELECT p.id_pacienta, p.jmeno, p.prijmeni, v.datum_vysetreni, v.hmotnost
FROM pacienti p JOIN vysetreni v ON p.id_pacienta = v.id_pacienta
Protože pracujeme s více tabulkami, je nutné sloupce identifikovat plným jménem, které
se skládá z názvu tabulky a názvu samotného sloupce, který oddělíme tečkou. Místo plného
názvu tabulek můžeme definovat zkrácené pojmenování v části FROM, zkratku zapíšeme
přímo za název tabulky. Zkratku tabulky pak používáme ve všech částech SQL dotazu.
Způsob spojení tabulek je obvykle definováno podmínkou za klíčovým slovem ON.
Podmínka definuje, které řádky se spolu mají párovat. Pokud podmínku nedefinujeme, vzniká
tzv. kartézský součin, kdy se každý řádek jedné tabulky spojí s každým řádkem druhé tabulky.
Výsledná množina má pak m x n řádků, což u větších tabulek může být enormní počet. Toto
chování je ve většině případů nežádoucí, proto musíme být při definování podmínky spojení
velmi pozorní. Databáze se tak bude chovat i v případě, kdy dle spojovací podmínky
odpovídá jednomu řádku první tabulky více řádků v tabulce druhé. Hodnoty řádku první
tabulky se kopírují ke každému řádku druhé tabulky. Výsledkem spojení tabulek je n řádků,
kde n je u vnitřního spojení v rozsahu 0 až m * n, u vnějšího spojení v rozsahu m až m * n.
Pokud chceme provést vnější spojení tabulek, doplníme před JOIN jedno z dalších
klíčových slov: LEFT, RIGH nebo FULL. Pokud chceme, aby řídící byla uvedená první,
použijeme klíčové slovo LEFT JOIN, pokud chceme mít řídící druhou tabulku, použijeme
RIGHT JOIN. Extrémem je tzv. úplné spojení (full join), jehož výsledkem jsou všechny řádky
obou tabulek. Tento typ spojení však použijeme jen zřídka, navíc je pro databáze výkonnostně
nejnáročnější. V našem případě použijeme variantu LEFT JOIN:
SELECT p.id_pacienta, p.jmeno, p.prijmeni, v.datum_vysetreni, v.hmotnost
FROM pacienti p LEFT JOIN vysetreni v ON p.id_pacienta = v.id_pacienta
Alternativou k syntaxi JOIN ON je přímý výčet spojovaných tabulek za FROM a specifikace
spojovací podmínky za WHERE. Tato konstrukce může být v případech spojování více
tabulek lépe čitelná. Lze ji však standardně použít jen pro vnitřní spojení. Stejný výsledek
jako v prvním případě dostaneme i po spuštění této varianty:
SELECT p.id_pacienta, p.jmeno, p.prijmeni, v.datum_vysetreni, v.hmotnost
FROM pacienti p, vysetreni v WHERE p.id_pacienta = v.id_pacienta
4.2.1 3 a více tabulek
Pokud potřebujeme získat data z více jak dvou tabulek, syntaxe zůstává stejná, pomocí
výrazu JOIN připojíme další tabulku. Mějme tabulku RTG vyšetření, na kterých může být
zjištěna u daného pacienta 0 až n zlomenin. Tabulka zlomenin je vázána s tabulkou rtg
pomocí klíče vysetreni_id.
CREATE TABLE rtg
(
vysetreni_id NUMERIC(9),
31
patient_id VARCHAR(10),
)
CREATE TABLE zlomeniny
(
zlomenina_id NUMERIC(9),
vysetreni_id NUMERIC(9),
lokalizace VARCHAR(50)
)
Naplníme tabulky daty, pacient pat 1 měl 2 vyšetření, jedno negativní, druhé ukázalo 2
zlomené kosti. Pacient pat2 měl jedno vyšetření bez nálezu.
INSERT INTO rtg (patient_id, vysetreni_id) VALUES ('pat1', 1);
INSERT INTO rtg (patient_id, vysetreni_id) VALUES ('pat1', 2);
INSERT INTO rtg (patient_id, vysetreni_id) VALUES ('pat2', 3);
INSERT INTO zlomeniny (zlomenina_id, vysetreni_id, lokalizace) VALUES (1, 2,
'femur');
INSERT INTO zlomeniny (zlomenina_id, vysetreni_id, lokalizace) VALUES (2, 2,
'ulna');
Vnitrřní spojení bychom provedli takto:
SELECT * FROM patients p LEFT JOIN rtg ON p.patient_id = rtg.patient_id LEFT
JOIN zlomeniny z ON rtg.vysetreni_id = z.vysetreni_id
Výsledkem jsou pouhé 2 řádky, které obsahují popis dvou zlomeni pacienta pat1.
Pokud chceme získat přehled o všech pacientech, jejich RTG vyšetřeních a případně
zjištěných zlomeninách musíme obě spojení definovat jako vnější:
SELECT * FROM patients p LEFT JOIN rtg ON p.patient_id = rtg.patient_id LEFT
JOIN zlomeniny z ON rtg.vysetreni_id = z.vysetreni_id
V totmo případě je výsledkem 8 řádků, popis zlomenin je ovšem jen u dvou z nich, v
ostatních řádcích ve sloupci popis je hodnota NULL.
Vnitřní spojení více tabulek můžeme provést i bez klauzule JOIN:
SELECT * FROM patients p, rtg, zlomeniny z WHERE p.patient_id = rtg.patient_id
AND rtg.vysetreni_id = z.vysetreni_id
Obdobně postupujeme i při spojování většího množství tabulek. Maximálně možný počet
spojovaných tabulek závisí na databázovém systému.
4.3 Vnořené dotazy
Připomeňme si, co s pomocí SQL jazyka již umíme. Získat konkrétní hodnotu zvoleného
sloupce, filtrovat konkrétní řádek z libovolné tabulky, pomocí agregačních funkcí a klauzule
GROUP BY získat sumární přehled o obsahu zvolených sloupců a umíme propojit záznamy
ve více tabulkách pomocí výrazu JOIN ON. Pro čerpání primárních dat z databáze pro další
32
zpracování například ve statistickém software je to zcela dostačující. Zdaleka to ale není vše,
co relační databáze a standard SQL nabízejí. Pokročilé SQL začíná možností vnořených
(nested) dotazů.
Vnořený dotaz má stejnou strukturu jabo běžný dotaz, pouze je uzavřen v kulatých
závorkách a umístěn v nadřazeném dotazu na jednom z těchto míst:
Na místě výčtu sloupců mezi slovy SELECT a FROM
Na místě názvu tabulky za FROM
Jako součást podmínky za slovem WHERE
Za klíčovým slovem SELECT můžeme použít zanořený dotaz, který vrátí právě jeden
sloupec a právě jeden řádek. Tuto možnost využijeme, pokud chceme do přehledu či do
výpočtu získat výsledek agregační funkce, např. chceme procenticke zastoupení. Představme
si tabulku s daty o denní spotřebě léků, ze které chceme získat přehled, kolik procent se
vyčerpalo daný den.
SELECT datum, mnozstvi, mnozstvi / (SELECT SUM(mnozstvi) FROM spotreba) * 100
FROM spotreba
Uvedený dotaz je složen ze dvou částí. Základem je prostý SELECT do tabulky spotreba,
odkud získáme data sloupců datum a mnozstvi. Do tohoto dotazu je vložen vnořený dotaz,
který pomocí agregační funkce SUM získá celkové spotřebované množství. Tímto číslem
dělíme hodnotu každého řádku tabulky spotřeby a násobíme stem, čímž získáme spotřebu
daného dne v procentech.
Vnořený dotaz je také možné uvést za klíčové slovo FROM a použít ho tak místo názvu
tabulky. Tento postup použijeme při sestavování složitých dotazů, kdy začneme jednodušším
dotazem, jehož výsledek použijeme v nadřízeném dotazu k další manipulaci. Tento typ
zanoření použijeme také v případě, kdy potřebujeme rychle získat počet řádků, které vrací náš
dotaz. Mějme dotaz:
SELECT * FROM spotreba WHERE mnozstvi > 100
Pokud tabulka spotreba obsahuje tisíce a více řádků, netušíme, kolik řádků dotaz vrátil,
dokud nenecháme všechny výsledné řádky zobrazit, což je při ladění dotazů velmi
neefektivní. Nejrychlejší způsob, jak získat počet řádků laděného dotazu, je jeho zapouzdření
do vnořeného dotazu a aplikace agregační funkce COUNT:
SELECT COUNT(*) FROM (
SELECT * FROM spotreba WHERE mnozstvi > 100)
Platí, že vnořené dotazy na pozici za FROM je možné vždy spustit samostatně, tedy
nezávisle na nadřízeném dotazu. Zanoření je možné opakovat na další vyšší úrovni. Počet
možných zanoření je závislé na limitech daného databázového systému.
Třetím a nejčastějším umístění vnořeného dotazu je v podmínce za WHERE. Zde může
být využit jako operand podmínky nebo v kombinaci s výrazem (NOT) EXISTS jako
samostatná podmínka. Dotaz vkládáný jako operand může být umístěn buď přímo za operátor
(=, <, >, <>), nebo s použitím modifikátoru ANY nebo ALL. Pokud je vnořený dotaz přímo
za operátorem, musí dotaz vracet právě jeden sloupec a právě jeden řádek. Typicky se zde
používají dotazy s agregační funkcí. Pokud použijeme kromě operátoru také modifikátor,
zůstává omezení na jeden sloupec, ale řádků může dotaz vracet 0 až N. Poslední možností je
umístění vnořeného dotazů za výraz EXISTS. V této variantě není počet sloupců vnořeného
dotazu významný, používá se buď * nebo jakákoliv konstanta (1). Podle počtu vrácených
33
řádků se vyhodnotí pravdivost výrazu EXISTS. Pokud dotaz nevrátí žádný řádek, je výsledek
FALSE, pokud vrátí 1 až N řádků, je výsledek výrazu TRUE. Pokud použijeme negaci v
podobě výrazu NOT EXISTS je výsledek opačný.
U vnořených dotazů umístěných za WHERE budeme až na výjimky definovat podmínku,
která prováže vnořený dotaz s rodičovským dotazem. Mějme dvě tabulky, jedna s názvem
student obsahuje jména studentů, druhá tabulka s názvem zkouska obsahuje informace o
složených zkouškách jednotlivých studentů. Pomocí spojení (JOIN) těchto tabulek můžeme
získat přehled o absolvovaných zkouškách jednotlivých studentů. Co ale v případě, že chceme
získat seznam studentů, kteří doposud žádnou zkoušku nesložili a nemají žádný řádek v
tabulce zkouska. Právě v těchto případech využijeme vnořený dotaz s výrazem NOT
EXISTS.
SELECT * FROM student WHERE NOT EXIST (SELECT 1 FROM zkouska WHERE
student.uco = zkouska.uco)
Všimněme si podmínky student.uco = zkouska.uco. Pokud bychom ji vynechali, dostali
bychom neprázdný výsledek jen v případě, kdyby tabulka zkouska byla prázdná. Vložená
podmínka zajistí, že se bude tabulka zkouska prohledávat pro každé uco studenta zvlášť.
Častou chybou bývá opomenutí nebo chybná definice propojovací podmínky, což má za
následek zcela chybný výsledek dotazu. V propojovací podmínce spojujeme sloupce z
nadřazeného dotazu se sloupci vnořeného dotazu. Platí, že ve vnořeném dotazu se můžeme
odkazovat na všechny sloupce dotazu nadřízeného, ale nikoliv naopak, v nadřízeném dotazu
nesmí být žádný odkaz na v něm vnořené dotazy.
V SQL vede ke stejnému výsledku často několik cest. Pokud neřešíme rychlost dotazu,
záleží na našich preferencích, kterou cestu zvolíme. Například hledání nejstaršího studenta
můžeme řešit minimálně třemi způsoby:
SELECT * FROM student WHERE datum_narozeni = (
SELECT MIN (datum_narozeni) FROM student)
Tímto způsobem hledáme studenty, jejichž datum narození se rovná nejmenšímu
(nejstaršímu) datu v tabulce.
SELECT * FROM student WHERE datum_narozeni <= ALL (
SELECT datum_narozeni FROM student)
Tímto způsobem hledáme studenty, jejichž datum narození je menší nebo rovno než
všechny datumy v tabulce. Pokud nemá nikdo menší datum narození než já, jsem nejstarší.
SELECT * FROM student ridici WHERE NOT EXIST (
SELECT 1 FROM student vnoreny
WHERE ridici.datum_narozeni > vnoreny.datum_narozeni)
Pro každý řádek řídícího dotazu je prohledávána tabulka vnořeného dotazu (v našem
případě stejná tabulka student), zda obsahuje záznam s menším datem narození. Pokud takový
řádek neexistuje je splněna podmínka NOT EXISTS a daný řádek je zobrazen.
Jako složitější příklad můžeme uvést požadavek, kdy chceme vidět jména studentů, kteří
již absolvovali alespoň tři zkoušky a všechny na první pokus. Toto je opět příklad, kde k
výsledku povede více cest, podívejme se na jednu z nich. Potřebujeme nejprve vybrat ty
studenty, kteří mají v tabulce zkouska alespoň 3 řádky s různým kódem předmětu. Pokud se
spokojíme s uco studenta, vystačíme si s tabulkou zkouska.
SELECT uco, COUNT(DISTINCT predmet) FROM zkouska GROUP BY uco
34
Tento dotaz nám vrátí přehled o počtu zkoušek jednotlivých studentů. Klíčové slovo
DISTINCT zajistí, že se bude každý předmět počítat jen jednou. Studenti, kteří doposud
žádnou zkoušku nesložili, v seznamu nebudou, protože žádný záznam v tabulce nemají. To
nám nevadí, protože nás zajímají pouze studenti s alespoň třemi zkouškami. Abychom
vyfiltrovali studenty s jednou a dvěmi zkouškami, doplníme dotaz o podmínku. Podmínku
uvedeme nikoliv za WHERE, ale za klíčové slovo HAVING, protože již pracujeme s
agregovaným výsledkem (počet zkoušek v primární tabulce není). Doplněný dotaz vypadá
takto:
SELECT uco, COUNT(DISTINCT predmet) FROM zkouska GROUP BY uco
HAVING COUNT(DISTINCT predmet) >= 3
Nyní ověříme, že v našem seznamu není student s neúspěšnou zkouškou (žádný řádek s
F):
SELECT uco, COUNT(DISTINCT predmet) FROM zkouska
WHERE NOT EXIST (
SELECT 1 FROM zkouska vnoreny
WHERE vnoreny.uco = ridici.uco and vnoreny.znamka = 'F')
GROUP BY uco
HAVING COUNT(DISTINCT predmet) >= 3
Pokud se ptáte, proč jsme misto vnořeného dotazu nevložili přímo do původního dotazu za
WHERE podmínku znamka <> 'F', uvědomte si, že v tomto případě by nám v seznamu zůstal
student, který má 3 a více úspěšných zkoušek a libovolný počet neúspěšných. Jednoduchá
podmínka by pouze odfiltrovala jeho neúspěšné pokusy ještě před provedením operace
GROUP BY. My však chceme studenty bez F, proto je nutné podmínku prověřit v zanořeném
dotazu.
Nyní známe uco hledaných studentů a potřebujeme doplnit jméno. Připojíme k výslednému
dotazu tabulku student.
SELECT jmeno FROM student JOIN
(SELECT uco, COUNT(DISTINCT predmet) FROM zkouska
WHERE NOT EXIST (
SELECT 1 FROM zkouska vnoreny
WHERE vnoreny.uco = ridici.uco and vnoreny.znamka = 'F')
GROUP BY uco
HAVING COUNT(DISTINCT predmet) >= 3
) filtr ON student.uco = filtr.uco
Připojení jsme provedli tak, že jsme náš připravený dotaz zanořili a umístili místo názvu
tabulky na pozici za JOIN. Za uzavírací závorkou jsme si tento dotaz pojmenovali jako "filtr",
abychom mohli definovat spojovací podmínku za ON.
Otázky:
1) Proč je chybný následující SQL dotaz
35
SELECT sex, COUNT(*) FROM patients GROUP BY sex WHERE date_of_birth IS
NULL
2) Kolik řádků je výsledkem kartézského součinu tří tabulek?
3) Může být výsledkem spojení neprázdných tabulek prázdná množina ( 0 řádků)?
Cvičení:
1. Vložte do tabulky patients další záznam s hodnotou NULL místo data narození.
Vyzkoušejte chování všech SQL dotazů uvedených v části seskupovaní dat.
2. Najděte nejstaršího studenta čtvrtým způsobem
3. Přepište všechny varianty a najděte nejmladšího studenta
4. Najděte předmět, ze kterého žádný student nemá F.
5 Analytické a statistické funkce SQL
Výstupy z učení
Umí používat analytické SQL funkce
Umí používat statistické funkce SQL
V následující kapitole si popíšeme pokročilé funkce, které nabízí databázový systém
ORACLE a POSTGRESQL. Tyto specifické funkce nám umožňují řešit elegantně typ úloh,
které jsou pomocí standardního SQL často jen velmi obtížně řešitelné. Jde o úlohy, kde je
potřeba:
Pracovat s pořadím řádků
Odkazovat se na předchozí či následující řádky ve výsledku dotazu
Pracovat s agregovanými daty
V druhé části kapitoly pak budou představeny skutečné statistické funkce, které jsou
k dispozici v databázovém serveru ORACLE.
5.1 Funkce pro určení pořadí řádků ve výsledku - Ranking function
Velmi často potřebujeme v praxi stanovit pořadí záznamů ve výsledku. Pro setřídění
výsledků nám standard SQL nám nabízí klíčové slovo ORDER BY umísťované na konec
SELECT dotazu. Očíslovat výsledek můžeme v prostředí ORACLE pomocí pseudosloupce
ROWNUM.
SELECT ROWNUM poradi, jmeno, prijmeni FROM student
Pseudosloupec ROWNUM přiřazuje číslo výsledným řádkům. Bohužel přiřazení probíhá
ještě před finálním setříděním podle výrazu za ORDER BY, a proto následujícím způsobem
pořadí studentů dle abecedy nezískáme:
SELECT ROWNUM poradi, jmeno, prijmeni FROM student
ORDER BY prijmeni, jmeno
Pokud chceme číslovat až setříděný seznam, musíme dotaz zanořit:
36
SELECT ROWNUM poradi, jmeno, prijmeni
FROM (
SELECT jmeno, prijmeni FROM student
ORDER BY prijmeni, jmeno
)
Na další omezení narazíme, pokud potřebujeme vybrat záznamy podle pořadí, například
třetího studenta dle abecedy. V takovém případě musíme podmínku vložit až do třetí vrstvy
nadřazeného dotazu. Nelze napsat:
SELECT ROWNUM poradi, jmeno, prijmeni FROM student
WHERE rownum = 3
ORDER BY prijmeni, jmeno
ani
SELECT * FROM (
SELECT ROWNUM poradi, jmeno, prijmeni FROM student
ORDER BY prijmeni, jmeno
) WHERE poradi = 3
Důvodem je, že databáze pseudosloupec ROWNUM nastavuje, až když řádek splní
podmínku za WHERE, první řádek je vždy ROWNUM = 1, a protože tento řádek nesplňuje
podmínku ROWNUM = 3, na výstup se nedostane. Databáze tak projde všechny záznamy v
tabulce, ale žádný podmínku nesplní. Druhý problém je, že ROWNUM se nastavuje ještě před
setříděním přes ORDER BY. Databáze vybere tři řádky z tabulky, které terpve následně
setřídí. Výsledkem druhého dotazu je třetí řádek, který databáze našla, ale nikoliv nutně třetí
dle příjmení studenta. Náhoda často způsobí, že při letmém testování se může zdát, že dotaz
funguje, teprve při hlubší kontrole se ukáže, že jde o chybu.
Správně je:
SELECT * FROM (
SELECT ROWNUM poradi, jmeno, prijmeni FROM (
SELECT jmeno, prijmeni FROM student
ORDER BY prijmeni, jmeno
)
)
WHERE poradi = 3
Tedy nejprve setřídit, pak očíslovat a teprve ve třetí vrstvě filtrovat. Jak je vidět tato
konstrukce je dost komplikovaná a navíc neřeší variantu, kdy máme studenty se stejným
příjmením a tyto bychom chtěli označit stejným pořadovým číslem. Proto je pro tento typ
úloh výhodnější použít některou z tzv. ranking function. ORACLE i POSTGRESQL nabízí tři
funkce:
RANK()
DENSE_RANK()
37
ROW_NUMBER()
Funkce se liší způsobem, jakým řádky číslují v případě, kdy se objeví ve výsledku stejné,
dle pravidel třídění rovnocenné hodnoty. Funkce RANK() a DENSE_RANK() číslují stejné
hodnoty stejným pořadovým číslem. Funkce ROW_NUMBER přiděluje každému řádku
unikátní číslo, u shodných hodnot rozhoduje o pořadí náhoda. Rozdíl mezi RANK a
DENSE_RANK spočívá v tom, jaké pořadové číslo následuje po sérii shodných řádků.
Funkce DENSE_RANK pokračuje nepřerušenou číselnou řadou, funkce RANK přeskočí
odpovídající počet čísel. Vše osvětlí následující tabulka.
Tabulka 12 -Srovnání výsledků funkcí RANK, DENSE_RANK a ROW_NUMBER
Příjmení RANK() DENSE_RANK() ROW_NUMBER()
Mladý 1 1 1
Novák 2 2 2
Novák 2 2 3
Novák 2 2 4
Starý 5 3 5
Syntaxe všech tří funkcí je následující:
RANK () OVER (ORDER BY sloupec)
Za názvem funkce následují prázdné závorky, dále klíčové slovo OVER, za kterým
následuje definice třídění, podle kterého chceme určovat pořadí řádků. Definice za ORDER
BY v ranking funkci nemá žádnou vazbu k ORDER BY klauzuli na konci celého SQL dotazu.
Můžeme určovat pořadí zcela nezávisle na finálním setřídění výsledku.
V případě, kdy potřebujeme stanovit pořadí v jednotlivých kategoriích výsledku, lze
výraz OVER dále rozšířit o klauzuli PARTITION BY. Například pokud chceme číslovat
pořadí vyšetření jednotlivých pacientů podle data vyšetření.
SELECT patient_id, datum_vysetreni,
RANK() OVER (PARTITION BY patient_id ORDER BY datum_vysetreni) poradi
FROM vysetreni
Tabulka 13 - Číslování výsledku s klauzulí PARTITION BY
Patient_id datum_vysetreni poradi
PAT_1 12. 5. 2012 1
PAT_2 14. 5. 2012 1
PAT_2 23. 9. 2012 2
PAT_2 4. 2. 2013 3
PAT_3 15. 3. 2012 1
38
Ranking funkce lze v dotazu umístit mezi klíčová slova SELECT a FROM nebo jako
výraz pro třídění za závěrečné ORDER BY. Naopak nelze je umístit do podmínky za WHERE
či HAVING. Pokud chceme pomocí nich definovat podmínku, musíme použít vnořený SQL
dotaz.
5.2 Funkce pro pro přístup k předchozím a následným řádkům - LAG (),
LEAD ()
Další speciální operací, která je ve standardním SQL obtížně proveditelná, je práce s
jiným než aktuálně zpracovávaným řádkem v setříděném seznamu. Připomeňme, že
standardní funkce a operátory jako SUBSTR(), LN(), TRUNC() atd. pracují vždy s hodnotami
aktuálně zpracovávaného řádku. Co když ale chceme například porovnat číselnou hodnotu
jednoho řádku s předchozím řádkem, například sledujeme u pacientů změnu v počtu
leukocytů od předchozího vyšetření. Ve standardním SQL bychom museli pomocí JOIN
operace spojit tabulku vysetreni se sebou samou, abychom dostali na jeden řádek hodnotu
předchozího a následného vyšetření. Výrazně snadnější a přehlednější je využití speciálních
funkcí LAG() nebo LEAD(). Funkce LAG() nám umožňuje pracovat s předchozími záznamy,
funkce LEAD() s následnými záznamy. Syntaxe obou funkcí je shodná.
LAG (sloupec1, n, hodnota) OVER (ORDER BY sloupec2)
Prvním parametrem je výraz, nejčastěji název sloupce, jehož předchozí nebo následující
hodnota nás zajímá. Výraz může obsahovat libovolný operátor či standardní funkci. Druhým
parametrem je celé číslo, které udává, o kolik řádků se chceme vrátit nebo posunout vpřed.
Třetí nepovinným parametrem je hodnota, kterou chceme, aby funkce vrátila, pokud se
posune mimo hranice vybrané množiny řádků (tedy před první nebo za poslední řádek).
Výchozí hodnotou třetího parametru je NULL. Následuje výraz OVER s definicí setřídění a
případně seskupení zpracovávané množiny výsledků. Touto klauzulí určíme funkci LAG() či
LEAD(), co míníme předchozím a následným řádkem. Výraz ORDER BY v klauzuli OVER
nijak nesouvisí s finálním setříděním výsledku SQL dotazu, i když pro kontrolu správnosti
našeho výsledku bude nejčastěji výraz ORDER BY v klazuli OVER stejný jako na konci
celého SQL dotazu.
Mějme tabulku vyšetření se sloupci patient_id, datum_vysetreni a pocet_leukocytu. Zajímá
nás změna počtu leukocytů u každého pacienta oproti předchozímu vyšetření.
SELECT patient_id, datum_vysetreni, pocet_leukocytu,
LAG(pocet_leukocytu, 1) OVER
(PARTITION BY patient_id ORDER BY datum_vysetreni) predchozi_pocet,
pocet_leukocytu LAG(pocet_leukocytu,
1) OVER
(PARTITION BY patient_id ORDER BY datum_vysetreni) zmena
FROM vysetreni
Tabulka 14 - Ukázka výsledku funkce LAG()
patient_id datum_vysetreni pocet_leukocytu predchozi_pocet zmena
PAT_1 12. 5. 2012 7,4
PAT_2 12. 5. 2012 5,3
PAT_2 23. 9. 2012 2,4 5,3 -2,9
39
PAT_2 4. 2. 2013 3,7 2,3 1,4
PAT_3 15. 3. 2012 1,9
PAT_3 6. 9. 2012 4,5 1,9 2,6
5.3 Reportovací funkce
Databáze ORACLE i POSTGRESQL nabízí nadstavbu standarního SQL, které se
označuje jako window nebo jako reportovací (reporting) funkce. Výraz window znamená, že
funkce zpracovávají definovanou podmnožinu výsledku dotazu, tzv. okno (window). V
podstatě jde o aplikaci agregačních funkcí na vymezený rozsah řádků, který je nezávislý na
výrazu v sekci GROUP BY.
Podívejme se na častý případ, kdy potřebuje znát procentické zastoupení zvolené
kategorie v tabulce. Mějme tabulku pacientů se sloupcem označující pohlaví. Chceme získat
sumární přehled s procentickým zastoupením žen a mužů. Pro získání počtu jednotlivých
kategorií použijeme standardní seskupovací výraz GROUP BY. Abychom ale mohli vyjádřit
procentické zastoupení, potřebujeme zároveň celkový počet záznamů, což ve standardním
SQL můžeme provést pomocí vnořeného dotazu na pozici sloupce:
SELECT pohlavi, COUNT(*) pocet,
(SELECT COUNT(*) FROM pacient) celkem,
COUNT(*) * 100 / (SELECT COUNT(*) FROM pacient) procento
FROM pacient
GROUP BY pohlavi
Tabulka 15 - Výsledek seskupení s procentickým vyjádřením
pohlavi pocet Celkem procento
F 80 200 40
M 120 200 60
Pomocí reportovací funkce můžeme stejného výsledku dosáhnout bez vnořeného dotazu:
SELECT pohlavi, COUNT(*) pocet,
SUM (COUNT(*)) OVER () celkem,
COUNT(*) * 100 / SUM (COUNT(*)) OVER () procento
FROM pacient
GROUP BY pohlavi
Jak je vidět, jde o aplikaci agregační funkce (SUM) na výsledek jiné agregační funkce
(COUNT) s vymezením rozsahu agregace. Rozsah agregace je definován za klíčovým slovem
OVER, v našem případě agregujeme přes celou množinu, což je vyjádřeno prázdnými
závorkami. Můžeme však chtít vytvořit sumární report, kde bude procento mužů a žen
rozvedeno dle státní příslušnosti:
40
Tabulka 16 - Parciální procentické vyjádření
stat pohlavi Počet celkem procento
ČR F 50 160 31,25
ČR M 110 160 68,75
SR F 30 40 75
SR M 10 40 25
Tuto sestavu s parciálními součty získáme drobnou úpravou původního dotazu:
SELECT stat, pohlavi, COUNT(*) pocet,
SUM (COUNT(*)) OVER (PARTITION BY stat) celkem,
COUNT(*) * 100 / SUM (COUNT(*)) OVER (PARTITION BY stat)
procento
FROM pacient
GROUP BY stat, pohlavi
Window funkce nejsou vázány jen na agregační konstrukce s GROUP BY. Lze je použít i
v jednoduchých výpisech, kde chceme srovnat konkrétní hodnotu například s průměrem.
Mějme tabulku s aplikovanou léčbou konkrétního léku jednotlivým pacientům. Tabulka
obsahuje identifikaci pacienta, datum podání a množství podaného léku. V tabulárním reportu
chceme srovnávat jednotlivé aplikace s celkovým průměrem v celé tabulce. Tuto sestavu
získáme z databáze následovně:
SELECT patient_id, datum_podani, davka, AVG(davka) OVER () prumerna_davka
FROM lecba
Pokud bychom vynechali klauzuli OVER, hlásila by databáze chybu nesprávného použití
agregační funkce. V tomto případě je vše v pořádku a ve čtvrtém sloupci bude ve všech
řádcích stejná hodnota, která odpovídá průměrné dávce v celé tabulce lecba.
Window funkce využíváme také při výpočtech kumulativních součtů. Při kumulativním
součtu sečítáme všechny hodnoty vybraného sloupce od prvního řádku až po aktuální.
Například z tabulky lecba z předchozího příkladu chceme sledovat kumulativní spotřebu léku
v čase. Použijeme agregační funkci SUM() doplněnou o klauzuli OVER, ve které
specifikujeme pravidlo setřídění:
SELECT patient_id, datum_podani, davka,
SUM(davka) OVER (ORDER BY datum_podani) kumulativni_spotreba
FROM lecba
Tímto zajistíme, že funkce SUM() agreguje data od prvního záznamu až po aktuální
řádek. Jde o implicitně definované agregační okno.
Tabulka 17 - Kumulativní součet
patient_id datum_podani davka kumulativni_spotreba
PAT_1 12. 3. 2012 10 10
PAT_2 18. 4. 2012 20 30
41
PAT_3 19. 4. 2012 10 40
PAT_3 20. 6. 2012 20 60
PAT_4 2. 9. 2012 30 90
Rozsah agregace (agregační okno) lze specifikovat i explicitně, což umožňuje počítat
například klouzavý průměr. Klouzavý průměr je průměrná hodnota vypočítaná v časové řadě
v definovaném časovém okně. Obvykle počítáme průměrnou hodnotu z několika předchozích
hodnot. Toto explicitní okno definujeme za klauzuli ROWS BETWEEN, za níž můžeme
použít některou z následujících možností:
UNBOUNDED PRECEDING - všechny předchozí řádky
UNBOUNDED FOLLOWING - všechny následující řádky
CURRENT ROW - aktuálně zpracovávaný řádek
n PRECEDING - n předchozích řádků
n FOLLOWING - n následujících řádků
Když se vrátíme k příkladu s celkovou průměrnou dávkou, můžeme jej rozšířit o výpočet
klouzavého průměru z posledních tří předchozích aplikací následovně:
SELECT patient_id, datum_podani, davka, AVG(davka) OVER () celkova_prumerna_davka,
AVG(davka) OVER (ORDER BY datum_podani
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) klouzavy_prumer
FROM lecba
Tabulka 18 - Klouzavý průměr
patient_id datum_podani Davka klouzavy_prumer
PAT_1 12. 3. 2012 10 10
PAT_2 18. 4. 2012 20 15
PAT_3 19. 4. 2012 10 13,333333
PAT_3 20. 6. 2012 20 15
PAT_4 2. 9. 2012 30 20
Hodnota klouzavého průměru se spočítá jako součet 3 předchozích hodnot plus hodnota v
počítaném řádku podělený čtyřmi. U prvních tří řádků se počítá průměr z redukovaného počtu
dostupných řádků.
5.4 Statistické funkce
Analytické funkce jsou v databázovém systému ORACLE rozšířeny o základní sadu
statistických funkcí. Přehled nejvýznamnějších z nich uvádí tabulka.
Tabulka 19 - Vybrané statistické funkce v ORACLE
Funkce Popis Uspořádání
CORR Pearsonův korelační párové
42
koeficient
CORR_S Spearmanův korelační
koeficient
párové
STATS_BINOMIAL_TEST Binomický test binomické
STATS_F_TEST F – test nepárové
STATS_KS_TEST Kolmogorov-Smirnovův test nepárové
STATS_MW_TEST Mann Whitney test nepárové
STATS_ONE_WAY_ANOVA ANOVA -analýza rozptylu nepárové
STATS_T_TEST_* Varianty T testu párové i nepárové
STATS_WSR_TEST Wilcoxnův znaménkový test párové
REGR_INTERCEPT α koeficient rovnice lineární
regrese
párové
REGR_SLOPE β koeficient rovnice lineární
regrese
párové
Součástí následujícího textu není detailní vysvětlení statistických testů a funkcí ani
předpoklady pro adekvátní použití těchto funkcí. Jde o standardní testy a výpočty, jejichž
podrobné vysvětlení lze nalézt v publikacích věnovaných statistickému zpracování dat.
Vysvětlíme si pouze způsob použití těchto funkcí v SQL dotazech. Použití funkce je závislé
na uspořádání vlastního testu, zda se jedná o srovnávání hodnot v páru nebo o hodnocení
nepárové. Použití funkcí s párovým uspořádáním je přímočaré, funkcím přímo předáváme
názvy hodnocených sloupců. Funkce s nepárovým uspořádáním mají jeden parametr pro
vlastní data a druhý pro vysvětlovací (kategorizační) proměnnou.
5.4.1 Výpočet korelace
Použití funkcí CORR a CORR_S pro výpočet korelačního koeficientu je snadné. Funkce
vyžadují 2 paremetry, kterými jsou nejčastěji 2 sloupce, mezi nimiž chceme spočítat daný
korelační koeficient. Jde o agregační funkce, jejichž výsledkem je jeden řádek. Stejně jako
standardní agregační funkce je lze rozšířit o window klauzuli OVER (viz předchozí
podkapitola o reportovacích funkcích). Mějme tabulku pacientů se sloupci, které obsahují
naměřenou výšku a hmotnost jednotlivých pacientů. Korelační koeficienty mezi výškou a
hmotností spočítáme následovně:
SELECT CORR (vyska, hmotnost) pearson, CORR_S (vyska, hmotnost) spearman
FROM pacient
Koeficienty zvlášť pro muže a pro ženy spolu s koeficientem za celý soubor získáme
takto:
SELECT pohlavi, CORR (vyska, hmotnost) pearson,
CORR_S (vyska, hmotnost) spearman,
, CORR (vyska, hmotnost) OVER () pearson_vse
FROM pacient
GROUP BY pohlavi
43
Pearsonův korelační koeficient (funkce CORR()) je dostupný taktéž v databázi
POSTGRESQL verze 9.1.
5.4.2 Párové statistické testy
Mezi testy s párovým uspořádáním patří párový T-test (funkce
STATS_T_TEST_PAIRED()) a Wilcoxnův znaménkový test (funkce
STATS_WSR_TEST()). Tyto funkce mají 3 parametry, první dva jsou párově uspořádané
vstupní parametry, třetím parametrem je specifikace požadovaného výsledku ve formě jedné z
následujících textových konstant:
STATISTIC - výsledek testové funkce
ONE_SIDED_SIG - jednostranná míra významnosti
TWO_SIDED_SIG - oboustranná míra významnosti
Mějme tabulku, kde je uveden počet leukocytů před cytotoxickou léčbou a po cytotoxické
léčbě. Jde o klasické párové uspořádání, kde můžeme otestovat významnost změny počtu
leukocytů po provedené léčbě. Statistickou významnost získáme následovně:
SELECT
STATS_T_TEST_PAIRED (leu_pred_lecbou, leu_po_lecbe, 'TWO_SIDED_SIG')
t_test,
STATS_WSR_TEST (leu_pred_lecbou, leu_po_lecbe, 'TWO_SIDED_SIG')
wilcoxon
FROM lecba
Opět jde o agregační funkce vracející jeden řádek, rozšíření o klazuli OVER však není ve
verzi ORACLE 11g podporováno.
5.4.3 Nepárové statistické testy
Nepárové testy obecně testují proti sobě 2 nezávislé výběry, kde nulová hypotéza je
stanovena tak, že oba výběry pochází ze stejné populace a že mezi nimi není statisticky
významný rozdíl. Pokud test vyjde statisticky významně, zamítáme tuto nulovou hypotézu.
Oproti párovému uspořádání nemusí být velikost vzorku pro oba výběry stejná, N se může
lišit. Proto vstupní data těchto funkcí mají odlišný formát než funkce párových funkcí. Prvním
parametrem je název sloupce, který kategorizuje vlastní data do 2 vzorků (např. hodnoty z
prvního výběru mouhou být označeny "A", hodnoty z druhého výběru písmenen "B"). Pokud
sloupec obsahuje více jak dvě unikátní hodnoty, ohlásí databáze chybu). Druhý parametr je
název sloupce s vlastními daty, třetím parametrem je požadovaný výstup, stejně jako v
případě párových testů. Tabulka se vstupními daty pro nepárové testy vypadá následovně:
Tabulka 20 - Vstupní data pro nepárové testy
VZOREK_ID Hodnota
A 12
A 21
A 17
B 20
B 16
44
B 13
B 18
Porovnání vzorků A a B pomocí funkce Mann Whitney testu a nepárového T-testu
provedeme takto:
SELECT STATS_MW_TEST (vzorek_id, hodnota, 'TWO_SIDED_SIG') mw,
STATS_T_TEST_INDEP (vzorek_id, hodnota, 'TWO_SIDED_SIG') t_test
FROM tabulka
Výsledkem je hladina významnosti p.
5.4.4 Jednofaktorová analýza rozptylu (one way ANOVA)
ANOVA je statistická metoda, která umožňuje porovnání více než 2 vzorků.
Jednofaktorová ANOVA představuje nejjednodušší případ analýzy rozptylu, kdy analyzujeme
účinek jednoho faktoru na zkoumanou závislou proměnnou. Databáze ORACLE nabízí pro
jednofaktorovou ANOVA analýzu funkci STATS_ONE_WAY_ANOVA(). Vstupní data jsou
stejná jako v případě nepárových testů, pouze kategorizační proměnná může obsahovat více
než dvě unikátní hodnoty. Liší se také nabídka možností pro třetí parametr, který určuje
výstupní hodnotu funkce. Můžeme volit z těchto možností:
Tabulka 21 - Možné výstupy funkce STATS_ONE_WAY_ANOVA
Výstupní hodnota Popis
SUM_SQUARES_BETWEEN Suma čtverců mezi skupinami
SUM_SQUARES_WITHIN Suma čtverců uvnitř skupin
DF_BETWEEN Stupeň volnosti mezi skupinami
DF_WITHIN Stupeň volnosti uvnitř skupin
MEAN_SQUARES_BETWEEN Mean squares mezi skupinami
MEAN_SQUARES_WITHIN Mean squares uvnitř skupin
F_RATIO Poměr MSB/MSW
SIG Míra významnosti
Mějme tabulku pacientů se sloupcem, který určuje stádium onemocnění v době diagnózy
a sloupec s celkovým přežitím v měsících od diagnózy. Vliv stádia na přežití pomocí
jednofaktorové ANOVA analýzy posoudíme takto:
SELECT STATS_ONE_WAY_ANOVA(stadium, preziti, 'F_RATIO') f_ratio,
STATS_ONE_WAY_ANOVA(stadium, preziti, 'SIG') p_value
FROM patients
Opět jde o agregační funkci, výsledkem je tedy jeden řádek s hodnotou testu a
statistickou významností.
45
5.4.5 Binomický test
Pomocí binomického testu můžeme otestovat, zda procentický výskyt zkoumaného jevu
odpovídá očekávané frekvenci. Můžeme tak například otestovat, zda procento mužů v naší
tabulce pacientů odpovídá očekávaným 50 procentům všech pacientů:
SELECT STATS_BINOMIAL_TEST (sex, 'M', 0.5, 'EXACT_PROB') exaktni,
STATS_BINOMIAL_TEST (sex, 'M', 0.5, 'TWO_SIDED_PROB' ) oboustranna
FROM patients
Prvním parametrem je sloupec s kategoriální proměnou, druhý určuje testovanou kategorii,
třetí parametrem je očekávaný podíl výskytu dané kategorie, čtvrtým parametrem
specifikujeme požadovaný výstup. Kategoriální proměnná musí obsahovat právě dvě unikátní
hodnoty. Varianty pro čtvrtý parametr jsou následující:
Tabulka 22 - Možné výstupy funkce STATS_BINOMIAL_TEST
Výstupní hodnota Popis
TWO_SIDED_PROB Hodnota oboustranné pravděpodobnosti
EXACT_PROB Hodnota exaktní pravděpodobnosti
ONE_SIDED_PROB_OR_MORE Hodnota jednostranné pravděpodobnosti
(větší než)
ONE_SIDED_PROB_OR_LESS Hodnota jednostranné pravděpodobnosti
(menší než)
5.4.6 Lineární regrese
Databázový systém ORACLE i POSTGRESQL nám umožňuje snadno provést nad daty
lineární regresi a vypočítat alfa a beta koeficienty regresní rovnice. Slouží k tomu funkce
REGR_INTERCEPT() a REGR_SLOPE(), které očekávají na vstupu dva parametry, prvním
je název sloupce se závislou spojitou proměnou, druhým je sloupec s nezávislou proměnnou.
46
Otázky:
1) Kterou z ranking funkcí nemůžeme použít, pokud chceme, aby pořadí posledního
záznamu odpovídalo celkovému počtu záznamů?
2) Jaký bude výsledek funkce LAG(sloupec, 100) OVER () nad tabulkou, která má pouze
10 řádků?
3) Je možné počítat pomocí window funkcí kromě klouzavého průměru i klouzavou
směrodatnou odchylku?
Cvičení:
1) Vytvořte si jednoduchou tabulku, která bude obsahovat datumový, textový a číselný
sloupec. Vložte do tabulky malý počet řádků a vyzkoušejte popsané reportovací a
statistické funkce, tak abyste byli schopni výsledek nezávisle ověřit.
2) Vyzkoušejte, jak se funkce chovají, pokud je některá hodnota v tabulce NULL.
6 Vyhledávání v textu
Výstupy
Umí použít operátor LIKE pro jednoduché prohledávání textu
Rozumí termínu regulární výraz a umí jednodušší výraz sestavit
Umí sestavit SQL dotaz s regulárním výrazem
Dokáže pomocí SQL a regulárního výrazu extrahovat potřebnou informaci z
textové informace
V této kapitole se seznámíme s databázovými prostředky, které nám umožňují
prohledávat textové řetězce, tedy hodnoty uložené ve sloupcích s obecným datovým typem
CHAR, VARCHAR, VARCHAR2, případně CLOB. Představen bude jednak operátor LIKE,
jednak tzv. regulární výrazy.
6.1 Standardní funkce INSTR a operátor LIKE
Funkci INSTR v databázi ORACLE, respektive STRPOS v případě POSTGRESQL, jsme
si představili již ve třetí kapitole. Pro připomenutí tato funkce umožňuje prohledávat text na
výskyt specifikovaného podřetězce, kterým může být jeden až N znaků. Výsledkem funkce je
pozice nalezeného podřetězce nebo nula v případě, kdy podřetězec nebyl nalezen. Funkce má
dva povinné parametry, prohledávaný text a hledaný podřetězec. Ve funkci INSTR v případě
potřeby můžeme využít další dva parametry, kterými jsou startovací pozice vyhledávání a
pořadí výskytu:
SELECT INSTR('strč prst skrz krk', 'r', 5, 2) FROM DUAL
Tento příklad hledá druhý výskyt písmena 'r' od páté pozice řetězce 'strč prst skrz krk'.
Výsledkem je 13, což je pozice druhého písmena 'r' hledaného od pátého znaku, tedy 'r' ve
slově skrz. Výchozí pozice může být i záporné číslo, v tom případě se pozice počítá od konce
řetězce a vyhledávání probíhá od konce na začátek:
SELECT INSTR('strč prst skrz krk', 'r', -5, 2) FROM DUAL
Příkaz provede hledání od pátého znaku od konce, tedy od písmene 'z', směrem k začátku
řetězce a výsledkem je pozice 7, tedy písmeno 'r' ve slově prst.
Funkci INSTR můžeme použít i v definici podmínky WHERE:
47
SELECT * FROM tabulka WHERE INSTR(sloupec, 'r', 1, 2) > 0
Tento příklad vrátí řádky tabulky, které ve sloupci obsahují alespoň dvě písmena "r"
(hledáme pozici druhého písmena 'r').
Obvyklejším způsobem vyhledávání řádků, které obsahují v textovém sloupci určitý
podřetězec, je použití operátoru LIKE a zástupných znaků. Jako zástupný znak se v případě
ORACLE a POSTGRESQL databáze používá znak "_" (podtržítko) a znak "%" (procento).
Podtržítko nahrazuje právě jeden libovolný znak, procento 0 až N libovolných znaků. Syntaxe
operátoru LIKE je následující:
sloupec LIKE '%podřetězec%'
Operátor LIKE používáme při definici vyhledávacích podmínek za klíčovým slovem
WHERE:
SELECT * FROM tabulka WHERE sloupec LIKE '_rk'
Tento příkaz najde všechny řádky, které obsahují ve sloupci třípísmenné slovo končící na
"rk".
V případě, kdy potřebujeme vyhledávat v textu samotný zástupný znak, definujeme pro
operátor LIKE ještě tzv. ESCAPE znak, který když umístíme před zástupný znak, vrátí
zástupnému znaku jeho původní význam. Pokud tedy potřebujeme najít řádky, které obsahují
ve sloupci symbol procento, definujeme podmínku následovně:
SELECT * FROM tabulka WHERE sloupec LIKE '%\%%' ESCAPE '\'
Jako ESCAPE znak zde slouží zpětné lomítko, které zbavuje druhý znak procenta jeho
funkce zástupného znaku. První a třetí znak procento jsou interpretovány jako zástupné
znaky, procento se tedy může ve sloupci vyskytovat kdekoliv (může ho předcházet i
následovat libovolné množství jiných znaků).
Pomocí operátoru LIKE bychom se mohli pokusit hledat řetězce obsahující datum
následujícím zpsůsobem:
SELECT * FROM tabulka WHERE sloupec LIKE '%__.__.____%'.
Hledáme takto dva znaky, tečku, dva znaky, tečku a čtyři znaky umístěné libovolně v
textu. Tento způsob nám však může vrátit mnoho falešně pozitivních výsledků (např. IP
adresa 88.45.12.45 bude taká vyhovovat uvedenému vzoru) a naopak mnohé řádky přehlédne
(např. 1.2.2000 má pouze jednu číslici před první i druhou tečkou). Pro dosažení lepších
výsledků musíme použít tzv. regulární výrazy.
6.2 Regulární výrazy
Regulární výrazy je pokročilá technika prohledávání textu, se kterou se setkáme ve
většině programovacích jazyků, v pokročilých textových editorech a taktéž v databázových
systémech. Regulárním výrazům jsou věnované samostané publikace, v této kapitole se
seznámíme pouze se základními konstrukcemi a se způsobem použití v databázi ORACLE a
POSTGRESQL.
6.2.1 Základy regulárních výrazů
Na regulární výraz se můžeme dívat jako na rozšíření operátoru LIKE. Jde o textovou
šablonu, která se skládá z:
hledaných znaků
48
zástupných znaků
kvantifikátorů
operátorů
modifikátorů
Zatímco operátor LIKE má pouze 2 zástupné znaky, u regulárních výrazů je nabídka širší.
Zástupným znakem můžeme odlišit např. číslici od písmena nebo tzv. bílého znaku (mezera,
tabulátor). Přehled základních zástupných znaků uvádí Tabulka 23.
Tabulka 23 - Zástupné znaky v regulárních výrazech
Zástupný znak Význam
. (tečka) Jakýkoliv znak
^ Začátek řetězce
$ Konec řetězce
\d Číslice
\D Vše kromě číslice
\w Písmeno, číslice, podtržítko
\W Doplněk k \w
\s Bílý znak – mezera, tabulátor
\S Doplněk k \s
Pokud potřebujeme v textu hledat samotný zástupný znak v původním významu, tedy
například tečku, musíme před hledaný znak umístit zpětné lomítko. Toto pravidlo platí pro
všechny speciální znaky regulárních výrazů.
'^\^\..$'
Uvedeným výrazem hledáme tříznakové řetězce, které začínají "^." a libovolným
následujícím znakem.
Zástupný znak zastupuje vždy právě jeden znak v prohledávaném řetězci. Toto chování
můžeme změnit pomocí tzv. kvantifikátorů, které v regulárním výrazu umístíme těsně za daný
zástupný znak. Přehled kvantifikátorů je uveden v Tabulka 24.
Tabulka 24 - Kvantifikátory v regulárních výrazech
Kvantifikátor Význam
* 0 – n opakování ("greedy" chování)
*? 0 – n opakování ("nongreedy" chování)
+ 1 – n opakování ("greedy" chování)
+? 1 – n opakování ("nongreedy" chování)
? 0 nebo 1 opakování
{m} Přesně m opakování
49
{m,} m nebo více opakování
{m,n} Minimálně m, maximálně n opakování
Spojením zástupného znaku "." a kvantifikátoru "*" dostáváme regulární výraz, který
pokrývá libovolný textový řetezec. Tuto kombinaci používáme ve dvou variantách:
"hladová" (greedy)
"nehladová" (nongreedy)
Pokud použijeme hladovou variantu, bude se hledat shoda s co nejdelším řetězcem,
naopak nehladová varianta hledá shodu s co nejkratším řetězcem. Blíže se na tento problém
podíváme v další části kapitoly věnované nahrazování podřetězců s použitím regulárních
výrazů.
Pokud bychom hledali v textu datum, mohli bychom použít tento regulární výraz:
'\d{1,2}\.\d{1,2}\.\d{2,4}'
Hledáme jednu až dvě číslice jako den, následuje tečka, jedna až dvě číslice na pozici
měsíce, tečka a dvě až čtyři číslice na pozici roku. Pokud by komponenty datumu oddělovaly
kromě tečky i mezery, rozšířili bychom výraz o mezeru s otazníkem za každou tečku:
'\d{1,2}\. ?\d{1,2}\. ?\d{2,4}'
Ani tento výraz však není stále ideální, protože den i měsíc může ve skutečnosti na první
pozici obsahovat jen vybrané číslice, konkrétně den 0, 1, 2 nebo 3, měsíc pouze 0 nebo 1.
Tento problém nám pomohou řešit tzv. operátory regulárních výrazů. Jejich přehled je uveden
v Tabulka 25.
Tabulka 25 - Operátory regulárních výrazů
Operátor Význam
| nebo
[abc] Jeden z uvedených znaků (a nebo b nebo
c)
[^abc] Libovolný znak kromě uvedených (vše
kromě a b c)
(abc) Uzavření skupiny znaků - blok
\1, \2, \3, ... Odkaz na první, druhý, třetí blok
Pro specifikaci vybraných znaků můžeme využít buď operátor svislítko "|" nebo
operátor hranatých zvorek. Upravený výraz pro hledání datumu může vypadat takto:
'(0|1|2|3)?\d\.(0|1)?\d\.\d{2,4}'
nebo takto:
'[0123]?\d\.[01]?\d\.\d{2,4}'
V tomto případě jsou obě varianty rovnocenné, rozdíl by byl, pokud bychom kombinovali
operátor se zástupným znakem. Zatímco operátor svislítko zástupné znaky interpretuje,
operátor hranatých závorek nikoliv. Výraz hledající v textu číslici nebo bílý znak proto musí
vypadat takto:
50
(\d|\s)
Oproti tomu výraz "[\d\s]" bude hledat v textu znaky "\" nebo "d" nebo "s".
Pomocí kulatých závorek můžeme vybranou skupinu znaků uzavřít do bloku a na ten se
pak následně odkazovat pomocí zpětného lomítka a čísla pořadí bloku. Tuto techniku
použijeme, pokud hledáme například repetitivní vzor a chceme ho definovat co nejobecněji.
Například pokud hledáme opakování tří stejných číslic, můžeme napsat:
'(\d)\1\1'
Říkáme tím, že chceme najít číslici (\d), za kterou se má opakovat stejný znak (\1). Výraz
je podstatně kratší než rovnocenný výraz s použitím operátoru svislítka a výpisem všech
variant:
'(111|222|333|444|555|666|777|888|999|000)'
Pomocí odkazů můžeme hledat text, který začíná dvěmi nečíselnými znaky a končí
stejnými znaky v opačném pořadí:
'^(\D)(\D).*\2\1$'
Poslední komponentou regulárních výrazů jsou modifikátory, které mění chování celého
procesu vyhledávání. Základním modifikátorem je volba, zda chceme při vyhledávání
rozlišovat velikost písmen. Pokud ano, jde o "case sensitive" hledání, pro které se používá
znak "c", pokud ne, jde o "case insensitive" označované znakem "i".
6.2.2 Použití regulárních výrazů v databázi ORACLE
Podpora regulárních výrazů v databázi ORACLE zahrnuje funkce vyhledávání řetězců,
hledání a extrakci podřetězce i nahrazování podřetězce za jiný text.
Funkce pro vyhledávání řádků v tabulce, které ve sloupci obsahují text odpovídající
specifikovanému regulárnímu výrazu, se v databázi ORACLE nazývá REGEXP_LIKE (). Její
použití je následující:
SELECT * FROM tabulka WHERE REGEXP_LIKE(sloupec,'reg. vyraz', modifikator)
Hledáme-li v tabulce řádky obsahující ve zvoleném textovém slouci datum, použijeme
tento příkaz:
SELECT * FROM tabulka WHERE REGEXP_LIKE (sloupec,
'[0123]?\d\.[01]?\d\.\d{2,4}', 'c');
Výsledek funkce REGEXP_LIKE je hodnota true v případě, že daný řádek tabulky
obsahuje ve sloupci text odpovídající regulárnímu výrazu.
Regulární výraz můžeme použít i pro vyhledání či extrakci podřetězce z textu. Slouží k
tomu funkce, které jsou obdobou textových funkcí SUBSTR() a INSTR(), pouze místo
hledaného pevného řetězce používáme regulární výraz. Funkce pro extrakci podřetězce je
definována takto:
REGEXP_SUBSTR(text, 'reg. vyraz', hledat_od, vyskyt, modifikator)
Pomocí této funkce extrahujeme ze sloupce podřetězec specifikovaný regulárním
výrazem, hledání probíhá od specifikované pozice (třetí parametr), hledá se n-tý výskyt
(čtvrtý parametr) při zohlednění modifikátorů specifikovaných posledním parametrem.
Povinné parametry jsou první dva. Výsledem funkce je extrahovaný podřetězec nebo
NULL.Zatímco funkci REGEXP_LIKE můžeme použít pouze při definování vyhledávací
51
podmínky, ostatní REGEXP funkce můžeme použít v SQL dotazech na všech místech jako
standardní funkce.
Extrakci datumu z textu provedeme tak, že stejný regulární výraz použijeme ve funkci
REGEXP_LIKE() i REGEXP_SUBSTR():
SELECT REGEXP_SUBSTR(sloupec, '[0123]?\d\.[01]?\d\.\d{2,4}') datum FROM
tabulka
WHERE REGEXP_LIKE(sloupec, '[0123]?\d\.[01]?\d\.\d{2,4}')
Pokud chceme získat pouze pozici podřetězce místo samotného podřetězce, použijeme
místo funkce REGEXP_SUSBSTR() funkci REGEXP_INSTR(). Její parametry jsou shodné,
pouze návratová hodnota je pozice prvního znaku podřetězce nebo nula.
U extrakce podřetězců se vrátíme k pojmům "greedy" a "nongreedy", které jsme zmínili u
přehledu kvantifikátorů. Mějme situaci, kdy chceme z textu extrahovat text, který je uveden v
závorkách. Text v závorkách může obsahovat libovolné znaky, například výsledek
cytogenetického vyšetření.
SELECT REGEXP_SUBSTR('translokace t(9;22)', '\(.*\)') FROM DUAL
Pokud text obsahuje pouze jeden pár závorek, funkce správně vrátí podřetězec "(9;22)".
Pokud ale prohledávaný text obsahuje více závorek, projeví se "hladovost" kvantifikátoru "*":
SELECT REGEXP_SUBSTR('translokace t(9;22) (Ph-chromozom) ', '\(.*\)') FROM
DUAL
Výsledkem je "(9;22) (Ph-chromozom)", protože výraz ".*" byl roztažen na maximální
počet znaků uzavřených mezi první otevírací závorkou a druhou uzavírací závorkou. Pokud
chceme získat jen obsah první závorky, musíme použít "nehladový" kvantifikátor "*?":
SELECT REGEXP_SUBSTR('translokace t(9;22) (Ph-chromozom) ', '\(.*?\)') FROM
DUAL
Pokud chceme hledaný podřetězec nahradit jiným textem, použijeme funkci
REGEXP_REPLACE(). Její syntaxe je následující:
REGEXP_REPLACE(text, reg.výraz, novy_text, hledat_od, vyskyt, modifikator)
Oproti REGEXP_SUBSTR je tu rozdíl v třetím parametru, kterým je text nahrazující
nalezený vzor. Parametr "vyskyt" specifukuje, kolikátý nález se má nahradit, pokud uvedeme
nulu (výchozí hodnota), nahradí se všechny výskyty. Nahrazovaný text může obsahovat
odkazy na bloky specifikované ve vyhledávaném regulárním výrazu. To nám umožní
například převést český formát datumu na formát (rok-měsíc-den):
SELECT REGEXP_REPLACE(sloupec, '([0123]?\d)\.([01]?\d)\.(\d{4})', '\3-\2-\1') datum
FROM tabulka
WHERE REGEXP_LIKE(sloupec, '[0123]?\d\.[01]?\d\.\d{2,4}')
Poslední významnou funkcí z REGEXP rodiny je REGEXP_COUNT(), která vrací počet
nalezených výrazů v prohledávaném textu. Její syntaxe je:
REGEXP_COUNT(text, reg.výraz , hledat_od, modifikator)
Význam parametrů je stejný jako v případě funkce REGEXP_SUBSTR().
52
6.2.3 Použití regulárních výrazů v databázi POSTGRESQL
V databázi POSTGRESQL najdeme místo funkce REGEXP_LIKE() operátor "~"
(vlnka), který provádí porovnání řetězce s regulárním výrazem s ohledem na velikost písmen
(case sensitive), zatímco operátor "~*" porovnává shodu bez ohledu na velikost písmen (case
insesitive).
SELECT * FROM tabulka WHERE sloupec ~ '[0123]?\d\.[01]?\d\.\d{2,4}'
respektive
SELECT * FROM tabulka WHERE sloupec ~* '[0123]?\d\.[01]?\d\.\d{2,4}'
Funkce REGEXP_SUBSTR() je v POSTGRESQL zastoupena funkcí SUBSTRING(),
jejíž syntaxe je následující:
SUBSTRING(text, reg.vyraz)
Extrakci datumu z textového sloupce bychom tedy v databázi POSTGRESQL provedli
takto:
SELECT SUBSTRING(sloupec, '[0123]?\d\.[01]?\d\.\d{2,4}') datum FROM tabulka
WHERE sloupec ~ '[0123]?\d\.[01]?\d\.\d{2,4}'
Oproti funkci REGEXP_SUBSTR() je tu užitečná výhoda, že můžeme ze
specifikovaného regulárního výrazu extrahovat pouze omezenou část, kterou uzavřeme do
kulatých závorek. Zatímco tedy předchozí příklad vrátí celé datum, v následujícím příkladu
můžeme drobným doplněním regulárního výrazu získat pouze rok z nalezeného datumu:
SELECT SUBSTRING(sloupec, '[0123]?\d\.[01]?\d\.(\d{2,4})') datum FROM tabulka
WHERE sloupec ~ '[0123]?\d\.[01]?\d\.\d{2,4}'
Shodný název jak v databázi ORACLE tak POSTGRESQL mají funkce pro nahrazení
nalezeného podřetězce za jiný text. Jde o funkci REGEXP_REPLACE(), kde rozdíl je pouze
ve volitelných parametrech. Syntaxe v POSTGRESQL je:
REGEXP_REPLACE (text, reg.vyraz, novy_text [, priznaky ])
V prohledávaném textu je nahrazen nalezený vzor za nový text. Pomocí příznaků
ovlivňujeme chování funkce. Nejdůležitější příznaky shrnuje Tabulka 26:
Tabulka 26 - Přehled nejvýznamnějších příznaků funkce REGEXP_REPLACE v POSTGRESQL
Příznak Význam
G Nahradit všechny výskyty regulárního výrazu
I Porovnávání bez ohledu na velikost písmen
C Porovnávání s ohledem na velikost písmen
Pokud chceme v textu zamaskovat čísla hvězdičkou, můžeme použít toto řešení:
SELECT REGEXP_REPLACE ('Rodné číslo 770922/1234', '\d', '*', 'g') FROM
GENERATE_SERIES(1,1)
Příznak "g" zajistí, že budou zaměněny všechny nalezené číslice.
53
6.2.4 Shrnutí
Regulární výrazy jsou velmi mocným nástrojem při operacích s textem. K nevýhodám
této techniky patří těžká čitelnost výsledných výrazů a časté hlavu lámající chování
složitějších konstrukcí.
V této kapitole byla popsána základní syntaxe regulárních výrazů a práce s nimi v
prostředí ORACLE a POSTGRESQL. Nejde však o vyčerpávající popis, pro další možnosti
této techniky je třeba prostudovat dokumentaci k vybranému databázovému systému.
Otázky:
1) Co znamená výraz case-sensitive?
2) Bude se lišit výsledek vyhledávání podmínek „sloupec = 'text'” a “sloupec like
'text'”
3) Co je výsledkem dotazu
SELECT * FROM WHERE REGEXP_LIKE(UPPER(sloupec), '^text')?
Cvičení:
1) Pokuste se pomocí funkce a regulárního výrazu získat obsah hranatých závorek
z textového řetězce „Hemoglobin: 110 [90-140]”.
2) Pomocí jakého regulárního výrazu byste hledali v textu telefonní čísla?
54
7 SQL skripty, uživatelské procedury a funkce
Výstupy
Chápe, co jsou SQL skripty, SQL procedury a funkce a rozdíl mezi nimi
Dokáže sestavit a spustit SQL skript
Rozumí základním prvkům jazyka PL/SQL
Umí vytvořit jednoduchou uživatelskou proceduru a funkci v jazyce PL/SQL
Jednotlivé SQL příkazy je velmi často potřeba spouštět v menších či větších sériích.
Typickým případem je vytváření databázové struktury či manipulace s daty pomocí DML
příkazů INSERT, UPDATE, DELETE. Nejjednodušším způsobem seskupení SQL příkazů je
vytvoření SQL skriptu. Nejedná se o nic jiného než o seřazení potřebných SQL příkazů za
sebou do textového souboru. Jednotlivé příkazy se standardně oddělují středníkem (;).
Můžeme kombinovat DDL příkazy (CREATE, ALTER, DROP) s DML příkazy. Na konec
skriptu umísťujeme potvrzovací příkaz COMMIT. Spuštění SQL skriptu lze provést různými
způsoby. Nejsnazší je spuštění v grafickém klientu, kam do příkazového okna nakopírujeme
příslušný skript a stiskneme tlačítko pro spuštění skriptu. Ukázku spuštění jednoduchého
skriptu lze vidět na obrázku Obrázek 5 (ORACLE) a Obrázek 6 (POSTGRESQL).
55
Obrázek 5 - SQL skript v SQLdeveloperu (ORACLE)
Obrázek 6 - SQL skript v pgAdmin (POSTGRESQL)
SQL skript lze spustit také z řádkového klienta. V ORACLE klientovi sqlplus spustíme
připravený skript, který jsme uložili do souboru script.sql, tím, že napíšeme cestu a název
souboru se skriptem a umístíme před něj znak zavináč (@).
SQL > @/oracle/scripts/script.sql
Pokud chceme spustit skript přímo z příkazové řádky operačního systému, spustíme
sqlplus s parametrem:
SQLPLUS login/heslo @/oracle/scripts/script.sql
V případě databáze POSTGRESQL je nejen ke spouštění skriptů k dispozici řádkový
klient psql. Spuštění SQL skriptu z prostředí psql provedeme příkazem \i:
postgres=# \i script.sql
56
Pokud chceme spustit SQL skript přímo z operačního systému, zavoláme psql
následovně:
PSQL -U login -f script.sql
7.1 Uložené procedury a uživatelské funkce
Sekvence DML příkazů můžeme také uložit jako objekt databáze formou uložené
procedury nebo uživatelské funkce. Stejně jako tabulky jsou uložené procedury a funkce
součástí databáze, mají svého vlastníka a přístup k nim se řídí přiděleným oprávněním. Stejně
jako tabulky se vytvářejí příkazem CREATE a ruší příkazem DROP. Rozdíl mezi uloženou
procedurou a uživatelskou funkcí není velký. Databáze POSTGRESQL má pouze uživatelské
funkce, které ale dokáží totéž co uložené procedury v ORACLE. V databázi ORACLE jedním
z rozdílů mezi procedurou a funkcí spočívá ve způsobu spouštění. Zatímco uživatelskou
funkci voláme stejně jako standardní funkci (např. SUBSTR() nebo ROUND()) v těle SQL
dotazu, uloženou proceduru spouštíme jako procedurální kód, což si ukážeme v této kapitole.
Databázové systémy často kromě SQL jazyka podporují i specifický procedurální jazyk.
Pomocí něj lze provádět operace, které v SQL provést nelze nebo které jsou příliš
komplikované. Procedurálním jazykem na rozdíl od SQL zpracováváme řádek po řádku
pomocí programových smyček a podmíněných výrazů. Procedurální kód nám také umožňuje
provádět vstupně/výstupní operace jako je ukládání dat do souboru na disk, odesílání emailem
apod. V uložených procedurách a uživatelských funkcích kombinujeme procedurální jazyk s
SQL příkazy. Procedurální jazyky jsou ještě více produktově specifické než jazyk SQL.
Procedurální jazyk v ORACLE se označuje jako PL/SQL, jazyk v PostgreSQL jako PGSQL.
Základní koncept je v obou případech stejný, odlišnosti jsou však v syntaxi, jak si ukážeme
dále.
7.2 Základy databázového procedurálního jazyka
Databázový procedurální jazyk vychází ze stejného konceptu a používá stejné prvky jako
jakýkoliv standardní procedurální programovací jazyk. V následujícím textu se seznámíme s
těmito jeho základními prvky:
• Oddělovače bloku kódu a oddělovač příkazů
• Práce s proměnnými
• Podmíněný výraz
• Programová smyčka
• Volání jiných procedur či funkcí
• Zpracování výjimek
Procedurální kód PL/SQL je vždy ohraničen klíčovými slovy BEGIN na začátku a END
na konci. Pro oddělení jednotlivých příkazů se používá středník (;) včetně finálního slova
END. Za klíčovým slovem BEGIN se naopak středník nevkládá. BEGIN a END vymezují
tzv. blok kódu, který může obsahovat další zanořený blok opět ohraničený slovy BEGIN a
END. Stejně jako v SQL funguje symbol "--" k oddělení jednořádkových komentářů,
víceřádkové komentáře uzavíráme mezi "/*" a "*/".
BEGIN
--toto je jednořádkový komentář
57
/* toto je
víceřádkový komentář */
END;
V procedurálním kódu se využívají tzv. proměnné pro uchování a přenos zpracovávaných
hodnot. Proměnná je obdobou sloupce tabulky, má své jméno a datový typ, základní typy
proměnných však mohou přenášet v danou chvíli jen jednu hodnotu. Datové typy jsou stejné
jako v případě SQL. Proměnné musíme deklarovat ještě před úvodním slovem BEGIN, kde
uvedeme název proměnné a její datový typ oddělený mezerou, jednotlivé proměnné
oddělujeme středníkem:
i NUMBER (5);
str VARCHAR2 (100);
BEGIN
....
END;
Proměnné přiřadíme hodnotu pomocí operátoru přiřazení, kterým je ":=". Pomocí
standardních operátorů můžeme provádět základní aritmetické operace. Proměnné můžeme
využívat na místě konstant v SQL příkazech:
i NUMBER (5);
str VARCHAR2 (100);
BEGIN
i:=1; str := 'text';
i:= (i-14) * 9875 + 456;
str := str || ' pripojeny text';
DELETE FROM tab WHERE sloupec = i AND sloupec2 = str;
INSERT INTO tab2 (sloupec, sloupec2) VALUES (i, str);
END;
Jednou ze základních technik v procedurálním programování je větvení kódu dle
definované podmínky pomocí konstrukce IF-THEN-ELSE. V prostředí PL/SQL má
podmínková konstrukce následující tvar:
IF podminka THEN
prikaz1;
ELSE
prikaz2;
END IF;
Pokud je podmínka splněna provede se příkaz1 (obecně sada příkazů mezi THEN a
ELSE), pokud podmínka platná není, provede se příkaz 2 (sada příkazů mezi ELSE a END).
58
Část ELSE je nepovinná, naopak při potřebě vyhodnotit postupně více podmínek, můžeme
konstrukci rozšířit o ELSEIF (deklarační část proměnných leu a grade je vynechána):
IF leu >= 3000 THEN
grade := 'I';
ELSIF leu < 3000 AND leu >= 2000 THEN
grade := 'II';
ELSIF leu < 2000 and leu >= 1000 THEN
grade := 'III';
ELSE
grade := 'IV';
END IF;
V konstrukci IF/ELSIF/ELSE se provede kód vždy jen jednoho ramena. Pokud je při
průchodu splněna podmínka více než jednoho ramena, provede se pouze první se splněnou
podmínkou. Pokud není splněna žádná podmínka, provede se část ELSE. Konstrukci
IF/ELSEIF můžeme nahradit za podmíněný výraz CASE, který známe z SQL.
Dalším prvkem procedurálního programování jsou programové smyčky, které nám
umožní provádět určitou část kódu opakovaně. Smyček je v PL/SQL několik typů, my si
ukážeme jednu z nejvíce využívaných, která umožňuje procházet výsledek SQL dotazu řádek
po řádku. Její syntaxe je následující:
FOR vektor IN (SELECT_dotaz) LOOP
prikaz;
END LOOP;
Tato smyčka využívá speciální proměnnou (vektor), která se při každém průchodu naplní
hodnotami jednoho řádku z výsledné množiny specifikovaného SELECT dotazu. Na
jednotlivé hodnoty (sloupce) se odkazujeme jako na sloupce tabulky, pouze místo názvu
tabulky používáme název proměnné (vektoru):
FOR k IN (SELECT patient_id, sex, date_of_birth FROM patients) LOOP
IF k.date_of_birth > SYSDATE THEN
INSERT INTO tabulka_chyb (patient_id, popis_chyby)
VALUES (k.patient_id, 'Chybné datum narození');
END IF;
IF k.sex <> 'M' AND k.sex <> 'F' THEN
INSERT INTO tabulka_chyb (patient_id, popis_chyby)
VALUES (k.patient_id, 'Chybné pohlaví');
END IF;
END LOOP;
Uvedená smyčka projde postupně všechny řádky tabulky patients a provádí příkazy mezi
klíčovými slovy LOOP a END LOOP, v našem případě zkontroluje správnost vyplnění
pohlaví a datumu narození. Zjištěné chyby zapisuje do tabulky tabulka_chyb. Pokud by
59
tabulka patients byla prázdná, a tedy zdaný SQL dotaz by nevrátil žádný řádek, celý FOR
blok by se při běhu programu přeskočil. Vektor v tomto typu smyčky na rozdíl od běžných
proměnných nemusíme dopředu deklarovat, za ukončením END LOOP se však již na něj
nelze odkazovat. Smyčku lze předčasně ukončit použitím příkazu EXIT, který umístíme do
vhodné podmínky.
PL/SQL podporuje i další typy smyček jako je nekonečná smyčka LOOP .. END LOOP,
smyčka pro dopředu neurčený počet průchodů WHILE-LOOP či smyčka FOR-LOOP pro
předem určený počet průchodů. Detaily lze najít v dokumentaci k databázi ORACLE.
Uvnitř PL/SQL procedury můžeme volat jiné uložené procedury. Buď naše vlastní nebo
některou z procedur z bohaté knihovny databázové systému. Proceduru spustíme prostým
uvedením jejího jména a případnou specifikací parametrů, které uzavřeme do kulatých
závorek. Procedury a funkce mohou být seskupeny do tzv. balíku (package), v tom případě při
volání procedury vkládáme před jejich název i název balíku oddělený tečkou. V následujícím
příkladě spustíme proceduru PUT_LINE, která je součástí balíku DBMS_OUTPUT. Spustíme
ji s textovým parametrem:
BEGIN
DBMS_OUTPUT.PUT_LINE('Všechno špatně' );
END;
Balík procedur DBMS_OUTPUT je součástí databázového systému ORACLE. Jeho
procedura PUT_LINE vypisuje na obrazovku obsah předaného parametru a slouží nejčastěji k
výpisu ladících zpráv uložených procedur a funkcí.
Během provádění procedurálního kódu může dojít k chybě, například když se pokusíme
dělit nulou nebo přiřadit do proměnné hodnotu, která neodpovídá jejímu datovému typu.
Těmto chybám lze částečně předcházet pomocí podmínkových konstrukcí, ale existuje i jiná
varianta řešení chybových stavů. Jde o tzv. zachytávání výjimek a obsloužení chybového
stavu. Pokud dojde při běhu PL/SQL k chybě, dojde k přerušení vykonávání kódu na
chybovém řádku a generuje se výjimka (exception). Pokud je na konci bloku kódu, kde chyba
nastala, sekce pro zpracování výjimek, přesune se vykonávání kódu sem. Každá vzniklá
výjimka s sebou nese identifikaci chyby, která ji způsobila. V sekci výjimek pak lze reagovat
na jednotlivé druhy chyb:
BEGIN
...
...
EXCEPTION
WHEN ZERO_DIVIDE THEN
-- osetreni deleni nulou
WHEN VALUE_ERROR THEN
-- osetreni chyb pri konverzi mezi datovymi typy
WHEN OTHERS THEN
-- osetreni vsech ostatnich chyb
END;
Předdefinovaných druhů výjimek je více, detaily lze nalézt v dokumentaci databázového
systému. Často však vystačíme pouze s ramenem WHEN OTHERS, kdy chceme pouze
zaregistrovat jakoukoliv vzniklou chybu. Reakcí na chybu může být provedení INSERT
příkazu do tabulky chyb nebo nastavení výstupních parametrů tak, aby signalizovali volající
proceduře vznik chyby. Pokud chyba není ošetřena v sekci výjimek, propaguje se do
nadřízeného bloku kódu nebo do volající procedury, kde opět je možné její zpracování. Pokud
není zachycena a ošetřena nikde, vykonávání celé procedury je ukončeno s chybovým
hlášením. Pokud je výjimka zachycena, pokračuje vykovávání programu za sekcí výjimek za
60
koncovým END, tedy vykonávání kódu se již nevrátí na původní místo, kde chyba vznikla.
Jelikož však bloky kódu mohou být zanořené, můžeme elegantně navázat na chybový stav a
pokračovat dále ve vykonávání procedury:
BEGIN
BEGIN
INSERT INTO tab1....
INSERT INTO tab2.....
INSERT INTO tab3....
DBMS_OUTPUT.PUT_LINE('Všechno OK' );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Něco se nepovedlo, ale..' );
END;
DBMS_OUTPUT.PUT_LINE('... jedeme dál' );
...
END;
Pokud v uvedeném příkladu dojde k chybě při provádění některého z INSERT příkazů,
přeskočí vykonávání do sekce EXCEPTION a následně pokračuje program dál. Pokud k
chybě nedojde, je sekce EXCEPTION přeskočena.
Výjimky generuje databáze při vzniku chyb, je možné ale také výjimku vyvolat cíleně
přímo v kódu voláním příkazu RAISE.
7.3 Vytváření uživatelských procedur a funkcí
Jak bylo řečeno, uložené procedury a funkce jsou objekty databáze stejně jako tabulky.
Vytváříme je příkazem CREATE PROCEDURE, resp. CREATE FUNCTION. Syntaxe
těchto příkazů je následující:
CREATE PROCEDURE jmeno_proc (parametry) IS
i NUMBER; -- deklarace proměných
BEGIN
–tělo procedury
END;
CREATE FUNCTION jmeno_funkce (parametry) RETURN datovy_typ IS
i NUMBER;
BEGIN
--tělo funkce
RETURN vysledek;
END;
61
Místo příkazu CREATE můžeme použít CREATE OR REPLACE, který v případě, že
procedura či funkce již existuje, provede její nahrazení. Při vytváření funkce na rozdíl od
procedury musíme specifikovat datový typ výsledku funkce. Výsledek funkce spočítáme v
těle funkce v libovolné proměnné, na konci funkce označíme zvolenou proměnnou jako
proměnnou obsahující finální výsledek pomocí příkazu RETURN. Tímto příkazem
vykonávání funkce končí. Parametry procedur a funkcí jsou proměnné, pomocí kterých
předáváme proceduře či funkci vstupní hodnoty při jejím volání. U procedur mohou
parametry sloužit i k předávání výsledků (OUTPUT parametry). Parametrů může být 0 až N,
stejně jako interní proměnné mají svůj název a datový typ. Definují se za názvem procedury a
funkce a vzájemně jsou odděleny čárkou.
Jako příklad funkce můžeme uvést uživatelskou funkci, která ze dvou datumů spočíta
věk, tedy rozdíl datumů v celých rocích.
CREATE OR REPLACE FUNCTION age (datum1 DATE, datum2 DATE) RETURN
NUMBER
IS
roku NUMBER;
BEGIN
roku := ABS(TRUNC(MONTHS_BETWEEN(datum1, datum2) / 12));
RETURN roku;
END;
Na vstupní hodnoty použijeme ORACLE funkci MONTHS_BETWEEN, výsledek
podělíme dvanácti, odřízneme desetinnou část a funkcí ABS zajistíme, že výsledek je kladné
číslo pro případ, že předáme funkci datumy v opačném pořadí.
Tuto funkci můžeme pak následně použít jako jakoukoliv jinou funkci v SQL dotazech:
SELECT age(date_of_birth, SYSDATE) vek FROM patients
Jako uloženou proceduru si můžeme definovat kód, který provádí hromadné mazání (sérii
DELETE příkazů) tabulek v našem schématu.
CREATE PROCEDURE uklid () IS
BEGIN
DELETE FROM tab1;
DELETE FROM tab2;
DELETE FROM tab3;
END;
Abychom spustili uloženou proceduru z databázového klienta, musíme její volání
vymezit jako PL/SQL blok pomocí BEGIN a END. Spuštění procedury uklid bude vypadat
takto:
BEGIN
uklid();
END;
62
7.4 Procedurální jazyk PG/SQL
Procedurální jazyk databáze POSTGRESQL se označuje jako PG/SQL nebo také plpgsql
a od PL/SQL databáze ORACLE se odlišuje v několika bodech. Předně databáze
POSTGRESQL podporuje pouze uživatelské funkce, nikoliv procedury. Nicméně pomocí
funkcí lze dosáhnou stejného efektu jako pomocí procedur v PL/SQL.
Funkce PG/SQL definujeme následovně:
CREATE FUNCTION nejaka_funkce() RETURNS NUMERIC AS $$
DECLARE
promenna NUMERIC := 30;
BEGIN
RAISE NOTICE 'Promenna obsahuje cislo %', promenna;
-- % je ve vypisu nahrazeno obsahem promenne
RETURN promenna;
END;
$$ LANGUAGE plpgsql;
Na příkladu lze vidět některé odlišnosti (červeně) v syntaxi oproti PL/SQL. Celý
procedurální kód je uzavřen mezi symboly "$$", na konci je pak navíc specifikace použitého
jazyka za klíčovým slovem LANGUAGE. Deklarace proměnných je uvozena klíčovým
slovem DECLARE. Příkaz RAISE, který v ORACLE slouží výhradně pro vyvolání výjimky,
se v POSTGRESQL využívá i k zobrazení zpráv. RAISE NOTICE pouze zobrazí
specifikovaný text, zatímco RAISE EXCEPTION vyvolá výjimku.
Operátor přiřazení (:=) je shodný s PL/SQL, stejně jako podmínkové konstrukce IFELSIF-ELSE.
Podporovány jsou taktéž smyčky LOOP a WHILE. Smyčka FOR pro
procházení řádků výsledku SQL dotazu má drobné odlišnosti od verze v PL/SQL:
CREATE OR REPLACE FUNCTION smycka() RETURNS integer AS $$
DECLARE
k RECORD;
BEGIN
FOR k IN SELECT * FROM patients LOOP
RAISE NOTICE 'pacient %', k.patient_id;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
Předně proměnná použitá jako vektor musí být deklarována jako každá jiná proměnná,
datový typ pro kurzor je RECORD. Druhý drobnější rozdíl je, že SQL dotaz ve FOR smyčce
není třeba uzavírat do závorek.
63
Uživatelskou funkci voláme jako standardní funkce pomocí SQL dotazů. Pokud chceme
jen vynutit její spuštění, použijeme konstrukci:
SELECT nazev_funkce() FROM GENERATE_SERIES(1,1)
nebo zkrácenou variantu
SELECT nazev_funkce()
Tento SQL dotaz zajistí právě jedno spuštění uživatelské funkce.
Co se týká výjimek, je způsob jejich zachytávání shodný s PL/SQL, liší se pouze seznam
možných typů systémových výjimek.
Otázky:
1. Lze pomocí SQL skriptu vytvořit PL/SQL proceduru/funkci?
2. Lze pomocí PL/SQL procedury vytvořit SQL skript?
3. Lze PL/SQL proceduru sdílet s jinými uživateli databáze?
Cvičení:
1. Napište skript, kterým vytvoříte libovolnou tabulku, vložíte do ní řádek, následně
smažte všechny řádky a zrušte vytvořenou tabulku. Vyzkoušejte opakovaně
spuštění skriptu.
2. Vytvořte uživatelskou funkci s názvem sude_cislo (x NUMBER), která pro
vstupní sudé číslo vrátí 1, pro liché nulu.
3.
64
8 Export a import dat
Výstupy:
Umí exportovat data z databázového klienta
Zná databázová rozhraní označovaná jako ODBC, OLED DB
Umí načíst data do databáze z textového souboru
Zná základní ovládání nástroje SQLLDR
Umí pracovat s externími tabulkami ORACLE
Hlavním úkolem databázového systému je uchovávat data a na vyžádání je poskytovat
oprávněným uživatelům. Doposud jsme data pouze prohlíželi ve výstupním okně klienta na
našem monitoru. V této kapitole si ukážeme, jak data exportovat ven z databáze, a to buď do
souboru, nebo přímo do jiné softwarové aplikace jako je například statistický program.
Ukážeme si také, jak data naopak efektivně do databáze vkládat.
8.1 Export dat pomocí databázového klienta
Databázový klient nám prostřednictvím SELECT příkazu zpřístupňuje data uložená v
databázi. V základním režimu tato data pouze zobrazuje na monitor. Jednou z věcí, kterou se
klienti liší, je nabídka jiného způsobu poskytnutí (exportu) dat.
Grafický klient ORACLE SQLDeveloper nabízí následující možnosti exportu dat:
csv
delimited
fixed
html
insert
loader
pdf
text (tabulátor)
XLS
XML
K této nabídce se dostaneme, pokud v tabulce s výsledkem dotazu klikne pravým
tlačítkem myši a zvolíme volbu Export:
65
Obrázek 7 - Export dat v programu SQLDeveloper
"Html" a "PDF" typ exportu jsou určené pro prezentační výstupy, ostatní typy jsou
určeny pro další zpracování nebo import do jiné databáze či software. Varianta "insert"
exportuje data ve formátu INSERT příkazů, které můžeme spustit jako skript a přenést tak
data do jiné databáze. Obdobně typ "loader" exportuje data ve formátu určeném pro načtení
do jiné ORACLE databáze prostřednictvím aplikace SQLLDR, o které si povíme dále v této
kapitole. Exporty typu "csv" a "text" jsou podskupinou exportu "delimited". Jde vždy o
export dat do textového souboru, kde jsou jednotlivé sloupce exportované tabulky odděleny
vybraným oddělovacím znakem. V případě "csv" je to čárka, v případě "text" je to znak
tabulátoru, v případě "delimited" exportu si můžeme oddělovací znak zvolit. Varianta
"fixed" exportuje data taktéž jako textový soubor, místo oddělovače ale mají sloupce vždy
stejnou šířku, všechny hodnoty jsou zarovnané mezerami na maximální možnou šířku
sloupce. XLS export zapíše data ve formátu MS Excel. XML typ exportu vytvoří z dat XML
soubor. XML formátem se budeme zabývat v samostatné kapitole.
Jednodušší řádkový ORACLE klient SQLPLUS umožňuje přesměrovat výstup z
monitoru do textového souboru pomocí příkazu SPOOL, za který uvedeme cestu k
výstupnímu souboru.
SPOOL jmeno_soboru
Po spuštění tohoto příkazu bude veškerý výstup na monitor zapisován do uvedeného
souboru. Ukončení zápisu provedeme příkazem SPOOL OFF.
V PostgreSQL v klientovi pgAdmin je možné výsledek dotazu exportovat přes funkci
Exportovat v menu programu Soubor. Jde o variantu "delimited" exportu do textového
66
souboru. V řádkovém klientu můžeme využít příkaz COPY, který uloží obsah tabulky do
specifikovaného textového souboru.
COPY patients TO 'C:/vystup.txt'
Ve výsledném textovém souboru jsou sloupce odděleny znakem tabulátoru. V uvedeném
příkladu je v plném názvu výstupního souboru skutečně normální lomítko, nikoliv obrácené,
jak je obvyklé v systému Windows. Pokud chceme exportovat výsledek SQL dotazu, musíme
ho uzavřít do kulatých závorek:
COPY (SELECT patient_id FROM patients) TO 'C:/vystup.txt'
8.2 Univerzální databázová rozhraní
8.2.1 ODBC
Další možností jak získat data z databáze pro další zpracování je načtení do aplikací přes
databázová rozhraní ODBC nebo OLEDB. ODBC je starší rozhraní, které umožňuje načítání
dat z databáze do aplikací systému Windows (podporu ODBC najdeme však i v systému
Linux) prostřednictvím tzv. ODBC ovladače (driveru), který je obvykle dodáván výrobcem
databáze. ODBC standard nám umožní čerpat data stejným způsobem z nejrůznějších
databází. Odlišností a specifika databázových systémů řeší právě zmíněný ODBC ovladač.
ODBC rozhraní je součástí systému Windows. Konfiguraci provádíme přes Nástroje pro
administrátory - ODBC rozhraní (Windows 7). Zde v kartě nalezneme seznam instalovaných
ovladačů (Obrázek 8).
Obrázek 8 - Správce ODBC zdrojů v MS Windows
Konkrétní konexi do cílové databáze vytvoříme buď v kartě Uživatelské DSN nebo
Systémové DSN podle toho, zda chceme, aby spojení bylo dostupné pouze nám nebo všem
uživatelům našeho počítače. Ve zvolené kartě klikneme na tlačítko přidat a vybereme ze
67
seznamu adekvátní ovladač. Pokud v nabídce požadovaná ovladač nenalezneme, musíme
získat jeho instalační soubor a provést instalaci. Po dvojkliknutí na název ovladače se otevře
konfigurační okno, které se liší podle zvoleného ovladače. Konfigurační okno ORACLE
ODBC ovladače ukazuje Obrázek 9.
Obrázek 9 - Konfigurační okno ORACLE ODBC ovladače
Položkou "Data source name" identifikujeme spojení. Tento název budeme následně
používat při připojování do databáze. "Description" je nepovinný popis spojení. "TNS Service
name" je specifikum ORACLE databáze a identifikuje nám cílový databázový server.
Položka "UserID" nastavíme na databázový login, který budeme při připojení používat. Není
to však nutné, login a heslo se standardně specifikují až v momentě připojování do databáze.
Ostatní nastavení ORACLE ODBC ovladače můžeme ponechat ve výchozím nastavení.
Nadefinované připojení můžeme otestovat stisknutím tlačítka "Test Connection". Po kliknutí
na "OK" máme připraveno ODBC zdroj, který můžeme začít používat.
ODBC připojení podporuje například MS Excel. Přes aplikaci Microsoft Query lze načíst
data z databáze do zvoleného listu (Obrázek 10). Po kliknutí na tuto volbu je nám nabídnut
seznam nadefinovaných ODBC spojení. Zvolíme to, které jsme nadefinovali v předchozím
kroku a v následujícím okně vyplníme náš login a heslo do databáze (Obrázek 11). Po
úspěšném připojení můžeme definovat SQL SELECT dotaz, jehož výsledek bude přenesen do
excelového listu.
68
Obrázek 10 - ODBC připojení jako zdroj dat v MS Excel
Obrázek 11 - Připojování do ORACLE databáze přes ODBC spojení
8.2.2 OLEDB
OLEDB je novější obdobou ODBC rozhraní. Opět potřebujeme ovladač pro daný
databázový systém, který zajistí, že stejným způsobem přistupujeme k uloženým datům bez
ohledu na výrobce databáze. Rozdíl oproti ODBC z uživatelského pohledu je v konfiguraci
připojení. To se obvykle provádí v konfiguračním souboru s příponou .udl. Pokud v systému
Windows poklepeme na takovýto soubor, objeví se dialog konfigurace OLEDB připojení k
databázi. V prvním okně je seznam nainstalovaných OLEDB driveru, další okna se liší podle
konkrétního driveru (Obrázek 12).
69
Obrázek 12 - Okno konfigurace OLEDB připojení
Připojení je možné definovat i bez konfiguračního souboru přímo v cílové aplikaci.
OLEDB je modernější, rychlejší a snadněji konfigurovatelné rozhraní než ODBC, nicméně
nabídka ovladačů je o něco nižší a ne všechny jsou plně funkční, zvláště ty dodávané třetí
stranou. Pro ORACLE je nejvhodnější využívat ovladač přímo od ORACLE.
Také OLEDB připojení je možné použít pro přenos dat do MS Excel, ve verzi 2007 a
vyšší najdeme nabídku opět v menu "Data", skupina tlačítek "Načíst externí data", skupina
funkcí "Z jiných zdrojů", varianta Průvodce datovým připojením.
8.3 Import dat
8.3.1 Import pomocí INSERT příkazů
Základní metoda vkládání dat do databáze je prostřednictvím příkazu INSERT. Jde o
univerzální metodu, která se hodí pro vkládání menšího objemu dat. Insert příkazy lze sestavit
v textovém editoru, pomocí programovacích prostředků nebo třeba v aplikaci MS Excel
pomocí vzorců. Tuto metodu si ukážeme na jednoduchém příkladu, kdy máme v excelovém
souboru číselný sloupec a sloupec s textem. Do třetího sloupce sestavíme vzorec:
Tabulka 27 - Ukázka tvorby INSERT příkazů v MS Excel pomocí vzorců
Číselný sloupec Textový sloupec Vzorec
1 text1 ="INSERT INTO tabulka (cislo, text) VALUES (" &
A2 & ",'" & B2 & "');"
70
2 text2 ="INSERT INTO tabulka (cislo, text) VALUES (" &
A3 & ",'" & B3 & "');"
3 text3 ="INSERT INTO tabulka (cislo, text) VALUES (" &
A4 & ",'" & B4 & "');"
Vzorec v MS Excel začíná vždy znakem "=", pokud vzorec obsahuje text, musí být
uzavřen mezi uvozovky. Symbol "&" spojuje text s odkazem na obsah vybraného pole. Odkaz
je tvořen písmenem sloupce a číslem řádku (A2 = sloupec A, druhý řádek). Nesmíme
zapomenout na to, že příkaz INSERT vyžaduje, aby vkládaná textová hodnota byla
ohraničena apostrofy. Pomocí vzorců získáme následující složený text INSERT příkazů:
INSERT INTO tabulka (cislo, text) VALUES (1,'text1');
INSERT INTO tabulka (cislo, text) VALUES (2,'text2');
INSERT INTO tabulka (cislo, text) VALUES (3,'text3');
Máme připraven importní skript, který pomocí databázového klienta spustíme, viz
kapitola o SQL skriptech.
8.3.2 Import dat pomocí SQLLDR (ORACLE)
Metoda vkládání dat pomocí sestavovaných INSERT příkazů je vhodná do cca 10 tisíc
záznamů. Pro větší objem dat je vhodnější použít specializovaný software pro import data,
tzv. datovou pumpu. Více či méně kvalitních datových pump je ke stažení velké množství, pro
práci s databází ORACLE je však nejspolehlivější použít aplikaci SQLLoader (spouští se
jako sqlldr) přímo od firmy ORACLE. Tato aplikace je součástí instalace databázového
klienta a patří mezi databázové utility. Je primárně určena k importu dat z textového souboru
do databáze. Jde o řádkovou aplikaci, která se ovládá přes kontrolní soubor. Pomocí něj
specifikujeme strukturu importovaných dat i cílovou tabulku. Importovat lze textový soubor
se sloupci oddělenými specifickým oddělovačem i textové soubory s pevnou strukturou.
Možnosti konfigurace aplikace SQLLoader jsou velice široké, podíváme se na nejdůležitější
prvky. Mějme textový soubor (zdroj.txt), který obsahuje 3 sloupce, jeden číselný, druhý
textový, třetí bude obsahovat datumy, sloupce jsou od sebe odděleny středníkem. Než
začneme s importem, musíme nejprve vytvořit cílovou tabulku:
CREATE TABLE importovana_data
(
cislo NUMBER(10,0),
text VARCHAR2(50),
datum DATE
);
Nejstručnější konfigurační soubor aplikace SQLoader musí obsahovat minimálně odkaz
na zdrojový soubor, cílovou tabulku, specifikaci oddělovače sloupců a seznam importovaných
sloupců:
LOAD DATA INFILE "C:\zdroj.txt"
APPEND INTO TABLE importovana_data
FIELDS TERMINATED BY ";"
71
(cislo, text, datum DATE "DD.MM.YYYY")
Tímto zápisem v konfiguračním souboru říkáme datové pumpě, aby načetla data ze
souboru zdroj.txt, který obsahuje 3 sloupce oddělené středníkem, a zapsala je do tabulky
importovana_data. Výčtem sloupců v kulatých závorkách specifikujeme pořadí sloupců ve
zdrojovém souboru a u datumu navíc specifikujeme formát. Konfigurační soubor lze dále
rozšířit o sekci OPTIONS, pomocí které specifikujeme chování datové pumpy při importu.
Nejčastější konfigurovatelné vlastnosti shrnuje Tabulka 28:
Tabulka 28 - Nejčastěji používané konfigurovatelné vlastnosti SQLLDR
Vlastnost Význam
SKIP=n Přeskočit na začátku n řádků (např. vynechat záhlaví)
ERRORS=n Ukončit import po dosažení n chyb
ROWS=n Provést commit po importu n řádků
DIRECT=true Volba rychlejší cesty importu
Pokud chceme, aby se při importu ignoroval první řádek v importovaném souboru a aby
import skončil při první chybě, vložíme na začátek konfiguračního souboru tento řádek:
OPTIONS (SKIP=1, ERRORS=0)
Je důležité zmínit, že i v případě, že import skončí chybou, bude proveden commit na
úspěšně importované řádky. Toto chování je nepříjemné, pokud importujeme data do
neprázdné tabulky, kdy při neúspěšném importu nemůžeme jednoduše vše smazat a začít
znovu a zároveň nechceme v cílové tabulce duplicity. V tomto případě se doporučuje nastavit
vlastnost ERROR na velmi vysoké číslo, aby došlo ke zpracování všech korektních řádků.
Chybové řádky budou aplikací zapsány do soubor.bad souboru, čímž vznikne sekundární
importní soubor, který pro vyřešení příčin chyb můžeme opět zkusit importovat. Tuto iteraci
opakujeme, dokud se nám nepodaří importovat všechny řádky. Druhou možností je provádět
import do prázdné pracovní tabulky a teprve z ní pomocí SQL příkazu INSERT SELECT
převést data do cílové tabulky. U extrémně velkých souborů, kdy samotný import trvá hodiny
a opakování importu je časově neefektivní, si při předčasném ukončení importu pomáháme
nastavením parametru SKIP, který nastavujeme za poslední úspěšně importovaný řádek,
případně těsně za detekovaný chybový řádek.
Pomocí přepínače DIRECT řídíme metodu importu. Výchozí metoda (DIRECT=false) je
metoda INSERT příkazů, kdy SQLLDR ze vstupního souboru sestavuje INSERT příkazy,
které spouští. Touto metodou lze bez problémů zpracovat soubory v řádu sta tisíc řádků, ale v
případě větších souborů je již tato cesta časově náročná. U velkých souborů se doporučuje
využít přímou cestu importu (DIRECT=true). Tato varianta je rychlejší, nicméně nekontrolují
se některá integritní omezení a může se tak do databáze dostat řádek, který by při standardní
cestě byl odmítnut. U velkých souborů je proto zcela na místě provádět import do pracovních
tabulek, zde provést nezbytné kontroly a teprve následně přenášet data do cílových tabulek.
8.3.3 Import přes externí tabulky (ORACLE)
Jako alternativu při importu dat z textového souboru můžeme v případě databáze
ORACLE použít techniku označovanou jako externí tabulka (external table). Tato metoda je
vhodná, pokud provádíme import dat v definované struktuře opakovaně. Podstatou této
metody je, že se díváme na importní textový soubor jako na tabulku, která je přímo součástí
72
databáze, jde o databázový objekt. Tento typ objektu vytvoříme pomocí příkazu CREATE
TABLE ORGANIZATION EXTERNAL. V tomto příkazu popíšeme umístění a strukturu
textového souboru a s vytvořeným objektem můžeme následně pracovat jako s běžnou interní
tabulkou pomocí příkazu SELECT. Platí zde však jistá omezení, mezi hlavní patří skutečnost,
že zdrojový soubor je nutné umístit přímo na databázový server do vyhrazeného adresáře
(naproti tomu import přes SQLLDR můžeme provádět odkudkoliv, kde máme
nainstalovaného ORACLE klienta). Dále oproti interní tabulce nelze použít příkazy INSERT,
UDATE, DELETE pro manipulaci s daty uvnitř externí tabulky. Nad externí tabulkou taktéž
nelze vytvářet indexy pro optimalizaci vyhledávání.
Abychom mohli používat externí tabulky, musíme mít nejdříve na serveru vytvořený a
zpřístupněný importní adresář. Tuto operaci obvykle provádí správce databáze:
CREATE OR REPLACE DIRECTORY
IMPORT_DIR AS
'/home/oracle/import';
GRANT READ, WRITE ON DIRECTORY IMPORT_DIR TO uzivatel;
Do tohoto adresáře nakopírujeme zdrojový soubor a přistoupíme k vytvoření objektu
externí tabulky. Syntaxe tohoto příkazu připomíná konfigurační soubor SQLLDR aplikace,
nicméně není to to samé. Ukážeme si vzor příkazu pro import souboru s fixní velikostí
sloupců (pozicový formát) a vzor pro import souboru, kde sloupce jsou odděleny vybraným
oddělovačem. V obou případech zdrojový soubor obsahuje 3 sloupce, jeden číselný, druhý
textový, třetí je datum. Pro pozicový soubor s názvem data_fix.txt použijeme následující
příkaz:
CREATE TABLE data_fix
(
cislo NUMBER,
jmeno VARCHAR2(20),
datum DATE
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY IMPORT_DIR
ACCESS PARAMETERS
(RECORDS DELIMITED BY NEWLINE
FIELDS (
cislo CHAR(3),
jmeno CHAR(6),
datum CHAR(10) DATE_FORMAT DATE MASK "dd.mm.yyyy"
)
)
LOCATION ('data_fix.txt')
73
);
Příkaz obsahuje dvakrát jména importovaných sloupců, liší se datové typy. V prvním
případě jde o definici, jak se na data chceme dívat ze vnitř databáze, v druhém případě za
klíčovým slovem FIELDS popisujeme situaci ve zdrojovém textovém souboru. Zde jsou
všechny prvky pouze text s vymezenou délkou, proto používáme datový typ CHAR s
vymezením délky. V případě datumu doplňujeme specifikaci formátu ve stejné notaci jako v
případě funkce TO_DATE. Po vytvoření externí tabulky si data můžeme prohlédnout
běžným SELECT příkazem:
SELECT * FROM data_fix
Pokud jsou sloupce ve zdrojovém souboru definovány pomocí oddělovacího znaku,
musíme příkaz pro vytvoření externí tabulky doplnit o specifikaci oddělovače:
CREATE TABLE data_delimited
(
cislo NUMBER,
jmeno VARCHAR2(20),
datum DATE
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY IMPORT_DIR
ACCESS PARAMETERS
(RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ";"
(
cislo CHAR(3),
jmeno CHAR(6),
datum CHAR(10) DATE_FORMAT DATE MASK "dd.mm.yyyy"
)
)
LOCATION ('data_delimited.txt')
);
Vytvoření objektu externí tabulky nám nebrání manipulovat se zdrojovým souborem.
Můžeme ho pomocí nástrojů operačního systému bez problémů nahradit a opětovně použít
SELECT příkaz pro prohlédnutí dat. Pokud SELECT příkaz skončí chybou, najdeme bližší
vysvětlení v log souboru, který se vytvoří na serveru v importním adresáři. Z uvedeného
vyplývá, že externí tabulky jsou užitečným nástrojem pro pravidelně opakovaný import dat,
nicméně neobejdeme se bez spolupráce s administrátorem databáze, resp. serveru.
74
8.3.4 Import do POSTGRESQL
Textový soubor lze do POSTGRESQL importovat pomocí COPY příkazu ve variantě:
COPY cilova_tabulka FROM 'zdrojovy_soubor'
Podporován je import textového souboru, kde sloupce jsou odděleny specifikovaným
jednoznakovým oddělovačem. Ten specifikujeme za klíčovým slovem DELIMITER,
výchozím oddělovacím znakem je tabulátor, který nemusíme explicitně specifikovat. V jiných
případech je nutné příkaz COPY uvést včetně oddělovače:
COPY patients FROM 'C:/vystup.txt' DELIMITER ';'
Existují ještě další možnosti specifikace formátu zdrojového souboru, nicméně nabídka je
nesrovnatelně menší než v případě ORACLE databáze a její datové pumpy SQLLDR. Příkaz
COPY podporuje také binární formu exportu a importu, tuto variantu lze použít však pouze
při přenosu dat mezi dvěma POSTGRESQL databázemi.
Otázky:
1) Je možné přes ODBC či OLEDB data do databáze zapisovat nebo jen číst?
2) Jsou všechny ODBC drivery všech databází součástí operačního systému?
3) Je SQLLoader rychlejší variantou pro import velkého objemu dat než přímé INSERT
dotazy?
4)
75
9 Základy XML
Výstupy:
Ví, co je XML a rozumí základním prvkům tohoto jazyka
Zná způsoby ukládání XML dokumentů v databázi ORACLE
Dovede extrahovat konkrétní informaci z XML dokumentu pomocí SQL
Zná možnosti vytvoření XML dokumentu z relačních dat v databázi
Databázové systémy jsou primárně určeny k ukládání dat. Data je však často třeba sdílet,
vyměňovat a migrovat z jednoho systému do druhého. Základní možností importu a exportu
dat z databáze jsme si ukázali v předchozí kapitole. Jedním z formátů, který můžeme pro
export dat použít v klientovi Sqldeveloper je XML formát. Právě tomuto formátu je věnována
tato kapitola. Seznámíme se s jeho základy, s nástroji pro jeho zpracování, se souvisejícími
technologiemi a v neposlední řadě se způsobem zpracování tohoto formátu v databázi.
XML formát je textový formát, kde přenášené datové položky jsou obklopeny popisnými
položkami tzv. tagy:
obsah
XML formát tak připomíná HTML jazyk webových stránek, nicméně je tu jeden zásadní
rozdíl. Názvy tagů v XML jsou určovány autorem dané konkrétní datové struktury nikoliv
samotným XML standardem. XML standard ve svém základu standardizuje pouze několik
věcí. Mezi ně patří například způsob přenosu speciálních znaků. Speciálními znaky myslíme
znaky, které mají v samotném XML standardu speciální význam, např. znaky "<" a ">"
ohraničující názvy tagů. Pokud tyto znaky chceme zapsat do XML v původním významu,
musíme je nahradit specifikovaným výrazem. Jejich přehled uvádí Tabulka 29.
Tabulka 29 - Vyhrazené znaky v XML a jejich kódování
Vyhrazený znak Náhradní výraz
< menší než <
> větší než >
& ampersand &
' apostrof '
" uvozovky "
9.1 Prvky XML
Základním prvky XML formátu jsou
Elementy
Atributy
Komentáře
Instrukce
76
Element je základní jednotka struktury XML dokumentu. Má své jméno a obsah. Jméno
je uzavřeno mezi symboly < a >, např. . Jménem elementu je ohraničen
obsah elementu, mluvíme o otevíracím a uzavíracím tagu. Uzavírací tag se od otevíracího liší
pouze lomítkem před jménem elementu. Jednoduchý element včetně obsahu vypadá takto:
obsah elementu
Element může být i prázdný, bez obsahu:
Zjednodušený zápis prázdného elementu vypadá takto:
Jméno elementu nesmí obsahovat mezery a musí začínat písmenem. Bez problémů je
možné používat číslice a podtržítko. Znaky s diakritikou ve jménech elementu jsou sice
standardem povoleny, nicméně doporučuje se jim vyhýbat stejně jako jiným
nealfanumerickým znakům. Rozlišují se velká a malá písmena, je tedy odlišné od
Obsahem elementu může být libovolný text, pozor je třeba dávat jen na speciální znaky,
které jsou uvedeny v Tabulka 29.
Text obsahující větší množství vyhrazených znaků je možné bez nahrazování vložit do
XML dokumentu jako CDATA blok. Pokud obsah elementu uvedeme mezi
"", budou zmíněné speciální znaky brány jako znaky standardní, bez
speciálního významu. Do CDATA bloku je možné uvést cokoliv s výjimkou ukončovací
sekvence "]]>". CDATA blok nám poslouží třeba v případě, kdy chceme přenášet zdrojový
kód webové stránky v HTML.
Element může obsahovat zanořený další element, čímž vzniká charakteristická stromová
struktura XML dokumentu.
2014-12-21
21.4
mg
Možnosti zanoření jsou neomezené, platí však, že na nejvyšší úrovni může být jen jeden,
tzv. kořenový (root) element. Zakázán je také překryv elementů. Následující zanoření tedy
není povoleno
77
S uvedenou stromovou strukturou také souvisí termíny rodiče (parent), děti (children),
sourozenci (siblings), potomci (descendants) a předci (ancestors), které jsou používané k
vyjádření vzájemných vztahů mezi elementy. V uvedeném příkladu jsou elementy v
následujících vztazích:
Tabulka 30 - Vztahy mezi elementy v XML struktuře
pacient vysetreni datum_vysetreni vysledek
pacient - rodič/dítě předek/potomek předek/potomek
vysetreni - rodič/dítě rodič/dítě
datum_vysetreni - sourozenci
Elementy mohou obsahovat tzv. atributy, které upřesňují informaci přenášenou v těle
elementu. Atributy se zapisují do otevíracího tagu elementu, jejich hodnoty se uvádí v
uvozovkách za znakem "=".
obsah elementu
9
V XML dokumentu je možné uvádět komentáře, které se při zpracování ignorují a slouží
pouze k uvedení vysvětlivek. Komentáře musí být ohraničeny tímto způsobem:
Posledním prvkem, který se objevuje v XML dokumentech, jsou procesní instrukce
(Processing instructions). Jak název napovídá, jde o instrukce, které říkají aplikaci, která
dokument zpracovává, jak se zachovat. Procesní instrukce jsou uzavřeny mezi symboly a
?>. Každý XML dokument musí obsahovat na svém začátku tuto procesní instrukci:
Často tato úvodní instrukce bývá doplněna o znakovou sadu, která je použita pro
kódování znaků v dokumentu. Standardem je kódování UTF-8.
9.2 Jmenné prostory
Jak bylo na začátku kapitoly zmíněno, názvy elementů a atributů definuje autor struktury.
Pokud definujeme strukturu sami pro sebe, nebudeme mít s názvy tagů problém, protože je
vše v naší režii. XML je ale primárně určeno pro výměnu informací mezi různými partnery a
je proto víc než pravděpodobné, že například element bude mít jiný význam pro
statistika, jiný pro technika a jiný pro umělce. Pokud se na definici struktury podílí dvě či více
stran, snadno může dojít k tomu, že v XML dokumentu se objeví elementy se stejným
názvem, ale s rozdílným významem. Pokud tedy definujeme vlastní XML strukturu, budeme
chtít jasně označit naše elementy od elementů našich partnerů. K tomuto slouží tzv. jmenné
prostory (namespace). Jmenný prostor definujeme v kořenovém elementu XML dokumentu,
kde použijeme tzv. URI identifikátor a zkratku, kterou budeme dále v dokumentu označovat
elementy a atributy patřící k našemu jmennému prostoru. URI (unique resource ident)
identifikátor je obvykle URL odkaz (např. http://iba.muni.cz), u kterého předpokládáme, že
nebude použit jiným autorem. Využíváme název internetové domény, která nám patří. Použité
78
URI nemusí směřovat (a obvykle ani nesměřuje) na žádný existující objekt v internetu, jde
jenom o to použít unikátní identifikátor našeho jmenného prostoru a zabránit kolizi s jiným
autorem.
Použití jmenného prostoru není povinné, ve standardizovaných XML strukturách je však
důležité. XML dokument může obsahovat elementy a atributy z jednoho, ale také z více
jmenných prostorů, všechny jsou uvedeny jako atributy "xmlns" v kořenovém elementu.
9.3 XML schéma
Jak jsme již řekli, samotný XML formát nedefinuje jaké elementy a atributy máme v
XML dokumentu použít. K vydefinování konkrétní struktury se využívá buď jazyka DTD
(Document Type Definition) nebo novějšího a modernějšího XML schématu. Oba přístupy
spočívají v tom, že pomocí specifického jazyka popíšeme, jak má námi definovaný XML
dokument vypadat, tedy jaké elementy a atributy má obsahovat, jak mají být elementy
zanořené a v jakém pořadí se mají v dokumentu vyskytovat. XML schéma oproti DTD
podporuje i datové typy a můžeme tedy určit, jaká data jsou povolena coby obsah
jednotlivých elementů a atributů. Zatímco DTD definice má vlastní jazyk a syntaxi, definice
XML schématu je opět XML dokument, kde pomocí definovaných elementů a atributů
popisujeme strukturu našeho cílového dokumentu. Odkaz na DTD či XML schéma uvádíme v
kořenovém elementu našeho dokumentu. Při zpracování XML dokumentu pomocí XML
procesoru či v XML editoru dojde k tzv. validaci XML dokumentu. XML dokument je
validní, pokud splňuje definice uvedené v připojeném XML schématu. XML schéma nabízí
velmi rychlou a efektivní cestu, jak ověřit, že XML dokument obsahuje očekávaná data.
Nelze od něj čekat sofistikované kontroly na vnitřní integritu dat, ale na odfiltrování
chybných datových typů či chybějících dat je velmi užitečné.
9.4 XPath
Abychom mohli pracovat s jednotlivými prvky XML dokumentu či je vyhledávat,
potřebujeme jazyk, který nám umožní se v XML struktuře dokumentu pohybovat a odkazovat
se na požadované prvky. Tímto prostředkem je jazyk XPath, který definuje operátory a funkce
pro uvedené účely. Pomocí XPath konstrukce můžeme odkazovat libovolný prvek XML
79
dokumentu a to buď v podobě absolutní či relativní cesty. Základní prvky jazyka XPath
shrnuje tabulka:
Tabulka 31 - Základní prvky XPath výrazů
Výraz XPath Příklad Popis
/ /pacient/vysetreni/
datum_vysetreni
Odkaz na kořenový uzel, za lomítkem
následuje název kořenového elementu.
Lomítkem taktéž oddělujeme dětské elementy
od rodičovských. Uvedený příklad odkazuje
na všechny datumy vyšetření, které odpovídají
uvedené cestě.
// //datum_vysetreni odkaz na element v libovolné hierarchické
úrovni, příklad odkazuje na všechny datumy
vyšetření bez ohledu na absolutní cestu.
@ /pacient/@id Odkaz na atribut, příklad odkazuje na atribut
id kořenového elementu pacient.
.. //datum_vysetreni/
../vysledek
Odkaz na rodičovský element. Uvedený
příklad nejprve nalezne libovolně umístěný
element datum_vysetreni, následně se odkáže
na rodiče a z něj na jeho další dětský element
vysledek. Jde tedy o ukázku odkazu na
sourozence elementu datum_vysetreni.
* //vysetreni/* Odkaz na všechny dětské elementy
@* /pacient/@* Odkaz na všechny atributy
S uvedenými výrazy bychom vystačili, pokud by se každý element či atribut objevoval v
XML dokumentu pouze jednou. Reálně se však elementy (kromě kořenového) mohou
mnohokrát opakovat, náš příklad může obsahovat stovky vyšetření a jejich výsledky, kdy
názvy elementů budou stejné, měnit se bude pouze obsah elementů. V této situaci použijeme
k bližšímu určení elementu tzv. predikáty XPath jazyka. Jde vlastně o filtrační podmínku,
kterou uvedeme ve výrazu uzavřenou v hranatých závorkách. V predikátech můžeme využívat
jednak hodnoty elementů a atributů, jednak pořadí elementu v dokumentu. K dispozici je také
řada funkcí, které můžeme v predikátech využít. Příklady predikátů v XPath výrazech uvádí
tabulka.
Tabulka 32 - Predikáty XPath výrazů
Výraz XPath Popis
//pacient[@id="245"] Odkaz na element pacient, jehož id atribut
má hodnotu 245
//vysetreni[1] První element pacient v XML dokumentu
//vysetreni[last()] Poslední element pacient v XML dokumentu
//pacient[1]/vysetreni[last()] Poslední vyšetření prvního pacienta
//vysetreni[vysledek>20]/datum_vysetreni Odkaz na datum vyšetření, jehož výsledek
80
byl větší než 20.
//vysetreni[year-from-date(datum_vysetreni)
= 2014]
Všechna vyšetření, která byla provedena v
roce 2014
9.5 Práce s XML v databázi
Jelikož je XML dokument textový soubor, můžeme ho vždy v databázi uložit v běžném
textovém (VARCHAR, případně CLOB) sloupci. Tím bychom ale přišli o všechny nástroje,
které se XML souvisí. Dnešní databáze proto nabízí speciální datový typ pro uložení XML
dokumentů a jejich fragmentů. V ORACLE jde o datový typ XMLTYPE. Sloupec tabulky
tohoto typu může obsahovat nejen správně formátovaný XML dokument, ale taktéž tzv. XML
fragment, který nesplňuje všechny podmínky korektního XML dokumentu, například není
vyžadován právě jeden kořenový element. Tabulku, která obsahuje textový sloupec a sloupec
pro ukládání XML vytvoříme pomocí následujícího příkazu:
CREATE TABLE jmeno_tabulky
(
velky_text CLOB,
xml_sloupec XMLTYPE
);
Hodnoty mezi sloupci můžeme převádět následovně:
UPDATE jmeno_tabulky SET xml_sloupec = XMLTYPE(velky_text);
UPDATE jmeno_tabulky SET velky_text = xml_sloupec. Getclobval();
Existuje několik cest, jak vložit XML dokument do databáze. U malých dokumentů je
nejjednodušší cesta přes klasický INSERT příkaz, kdy můžeme vkládat dokument přímo do
XMLTYPE sloupce nebo případně přes textový sloupec a následný přesun pomocí výše
uvedeného UPDATE příkazu. Pro větší soubory v ORACLE lze využít utilitu sqlldr. Pro
opakované nahrávání XML dokumentů stejného typu do ORACLE je možné využít XML
repozitář (XML DB repository). Tento prvek je rozhraním mezi souborovým systémem
serveru a vnitřní strukturou databáze. Pomocí XML schématu můžeme svázat schématu
odpovídající XML dokument s XML tabulkou databáze. Ke XML repozitáři lze přistupovat
jako k síťovému disku a XML soubory tak lze snadno nakopírovat dovnitř. Pokud je XML
dokument validní z pohledu registrovaného XML schématu, je okamžitě viditelný jako řádek
v navázané databázové XML tabulce a můžeme s ním pracovat pomocí SQL příkazů.
9.5.1 Prohledávání a extrakce z XML
Jakmile máme XML dokument zaveden v databázi jako XMLTYPE, můžeme ho začít
prohledávat pomocí XPath výrazů a několika specifických funkcí. Jednoduché vyhledávání
celých dokumentů (řádků tabulky), které odpovídají XPath výrazu provedeme pomocí funkce
existsNode. Jejím prvním parametrem je XML sloupec, druhým parametrem XPath výraz.
Funkce vrací hodnotu 1, pokud je XPath výraz nalezen, v opačném případě vrací nulu.
Následující příkaz prohledá tabulku a vrátí počet řádků, které v XMLTYPE sloupci obsahují
XML s elementem STUDY_NAME.
81
SELECT COUNT(*) FROM jmeno_tabulky
WHERE existsNode(xml_sloupec, '//STUDY_NAME') = 1
Využít lze plnou šíři XPath výrazů včetně predikátů. Lze například hledat XML
dokumenty obsahující vyšetření s výsledkem rovno 57
SELECT COUNT(*) FROM jmeno_tabulky
WHERE existsNode(xml_sloupec, '//vysetreni[vysledek=57]') = 1
Pokud potřebujeme z XML dokumentu získat hodnotu konkrétního elementu či atributu,
využijeme funkci extract nebo extractvalue. Obě funkce mají stejné parametry jako funkce
existsNode. Liší se návratovou hodnotou. Funkce extract extrahuje z XML opět XML
fragment, zatímco extractvalue vrací jednoduchý datový typ. Funkce extractvalue je
náročnější na XPath výraz, který musí ukazovat na obsah právě jednoho elementu či atributu.
Nesmí tedy jít o množinu uzlů (XML fragment), v tomto případě volání funkce skončí
chybou. Naproti tomu výsledkem funkce extract je buď odkazovaný XML fragment nebo
NULL, pokud v dokumentu neodpovídá žádný prvek specifikovanému XPath výrazu.
Zatímco funkci existsNode využijeme za klíčovým slovem WHERE, extrahovací funkce
použije za příkazem SELECT:
SELECT extract(xml_sloupec,
'//vysetreni[vysledek=57]/datum_vysetreni').GetClobVal()
FROM jmeno_tabulky WHERE existsNode(xml_sloupec, '//vysetreni[vysledek=57]')
= 1
V uvedeném příkladu musíme použít funkci extract, protože pokud by XML dokument
obsahoval více vyšetření s výsledkem 57, příkaz by skončil chybou.
9.5.2 Jazyk XQuery
Pro složitější vyhledávání, kde již nevystačíme se samotným jazykem XPath, je k
dispozici jazyk XQuery (někdy označovaný jako XMLQUERY), který nabízí prakticky
plnohodnotný procedurální jazyk včetně práce s proměnnými, podmíněnými výrazy a
smyčkami. Dotazy v XQuery jazyce můžeme spouštět buď ve vybraných XML editorech
nebo v databázích podporujících standard SQL/XML:2006. Mezi takové patří i databáze
ORACLE od verze 11. XQuery je dle uvedeného standardu možné spouštět pomocí funkce
XQuery, jejímž parametrem je výraz XQuery jazyka. Následující příklad je malou ukázkou
XQuery výrazu, kde jsou tučně zvýrazněna klíčová slova:
for $i in //pacient
where $i/vysetreni[vysledek="45"]
return
Tento výraz můžeme spustit v databázi ORACLE nad tabulkou XMLTYPE sloupcem
následujícím způsobem:
SELECT
XMLQuery(
for $i in //pacient
where $i/vysetreni[vysledek="45"]
return '
82
PASSING t.xml_sloupec RETURNING CONTENT) vysledek_query
FROM jmeno_tabulky t
Definici Xquery jazyka lze nalézt na stránkách W3C konsorcia.
9.5.3 Generování XML
Databáze podporující standard SQL/XML nabízí také funkce na vytváření XML
dokumentů z relačně uložených dat. Jde o sadu funkcí, které začínají prefixem XML a pomocí
kterých můžeme sestavit libovolný XML dokument. Tři základní funkce jsou popsány v
tabulce:
Tabulka 33 - Základní funkce XML/SQL standardu
Funkce Příklad Popis
XMLELEMENT (název
elementu, obsah elementu)
nebo
XMLELEMENT (název
elementu, atributy elementu,
obsah elementu)
SELECT
XMLELEMENT(student,
jmeno || ' ' || prijmeni) FROM
student
Funkce vytváří XML
element. Může mít 2 nebo 3
parametry. Třetím
parametrem mohou být
definice atributů, viz funkce
XMLATTRIBUTES
XMLATTRIBUTES (obsah
atributu AS název atributu,
obsah atributu2 AS název
atributu2,...)
SELECT
XMLELEMENT(student,
XMLATTRIBUTES(uco as
"uco"), jmeno || ' ' || prijmeni)
FROM student
Atributy musí být vždy
součástí elementu, proto je
funkce XMLATTRIBUTES
používána vždy uvnitř
funkce XMLELEMENT.
Pomocí XMLATTRIBUTES
lze vytvořit 1 až N atributů
konkrétního elementu.
XMLAGG
(XMLELEMENT(...))
nebo
XMLAGG
(XMLELEMENT(...),
ORDER BY sloupec)
SELECT XMLELEMENT
(studie, XMLAGG(
XMLELEMENT (nazev,
XMLATTRIBUTES
(study_id as "id"),
study_title))) FROM studies
Funkce XMLAGG agreguje
více řádků vstupní tabulky do
jednoho zahnízděného XML
fragmentu. Agregované
záznamy je možné setřídit
pomocí výrazu za ORDER
BY
9.6 XML v biomedicíně
Na XML dokumenty narazíme dnes téměř všude. Kromě přenosu dat mezi dvěma
systémy se používá XML také jako formát konfiguračních souborů softwarových aplikací a
taktéž soubory kancelářských balíků jako je např. MS Office jsou ukládány v podobě
zazipovaných XML dokumentů dle standardu Office Open XML.
Pokud jde o výměnu dat v oblasti zdravotnictví, narazíme na standardy založené na XML
jak v České republice, tak v mezinárodních projektech. V České republice je k výměně
klinických a laboratorních dat mezi zdravotnickými zařízeními využívám standard DASTA.
Ten je dnes používán ve verzích 3 a 4, kdy nejvýznamnějším rozdílem mezi verzemi je, že
verze 3 je definována za pomocí DTD souborů, zatímco verze 4 již využívá XML schémat.
Standard DASTA je specifický pro Českou republiku, v zahraničí se ke stejnému účelu
83
prosadil standard HL7, který spravuje stejnojmenná organizace s centrálou v USA. HL7
standard využívá XML od verze 3, předchozí dodnes široce rozšířená verze 2.x využívá
vlastní specifickou syntaxi pro přenos dat. Další mezinárodní organizací, která využívá XML
pro definici standardů v oblasti výměny dat v klinickém výzkumu je CDISC. Příkladem
standardu, který tato organizace spravuje, je CDISC Operational Data Model (ODM). Tento
standard pomocí XML specifikuje, jakým způsobem se mají přenášet, případně archivovat,
data klinických studií včetně všech jejich aspektů, jako je podrobná definice struktury
hodnocených dat, data o autorství každého záznamu a podrobné auditní záznamy o každé
provedené změně v klinických záznamech, která byla v průběhu výzkumu v datech
provedena.
Otázky:
1) Je možné přes XML formát přenášet český text s diakritikou?
2) Je možné XML uložit do libovolné databáze?
3) Obsahuje XML standard specifické prvky pro biomedicínská data?
4) Je rozdíl mezi zápisem a
5) V jakém vztahu jsou elementy a ?
Úkoly:
1) Vytvořte v databázi tabulku studentů, která bude mít sloupce UCO, jmeno,
prijmeni, datum_narozeni. Navrhněte XML formát pro výměnu dat této tabulky.
Vytvořte v editoru vzorové XML.
2) Jak byste níže uvedené XML převedli do relační struktury. Jaké byste vytvořili
tabulky a s jakou strukturou?
Cyklofosfamid
Cyclophosphamide
L01AA01
C
1
8
600
mg/m2
iv
Methotrexát
Methotrexate
L01BA01
M
1
8
84
40
mg/m2
iv
3) Napište XPATH výraz, který se bude odkazovat na anglický název léku
10 Literatura k dalšímu studiu
Conolly T., Begg C., Holowczak R., 2009: Mistrovství - databáze Profesionální
průvodce tvorbou efektivních databází. Computer Press, Brno, 584 pp.
Lacko L., 2011: 1001 tipů a triků pro SQL. Computer Press, Brno, 416 pp.
Molinaro A., 2009: SQL - Kuchařka programátora. Computer Press, Brno, 574
pp.
Pokorný J., Mlýnková I., Nečaský M., Richta K., Toman K., Toman V., 2008:
XML technologie - Principy a aplikace v praxi. Grada Publishing, Praha, 268 pp.
Šimůnek M., 1999: SQL kompletní kapesní průvodce. Grada Publishing, Praha,
248 pp.