Dotazovací jazyk SQL a PL/SQL 8. Přednáška
Úvod do PL/SQL Proprietární rozšíření firmy Oracle Usazuje se i v ne-oracle RDBMS Procedurální rozšíření SQL Přidává základní programátorskou logiku a kontrolu SQL Procedurální jazyk
Proč PL/SQL SQL je deklarativní jazyk Na některé specifické operace potřebujeme programovat ve vhodném paradigmatu (podporujícím struktury) Přináší možnost programovat v SQL klasickou cestou (ala Pascal či C) Založen na limitech SQL pro jejich odstranění
Využití PL/SQL Programový kód lze v Oracle vkládat na celou řadu míst: přímé volání PL/SQL v rámci SQL uložené procedury funkce programové balíky spouště (triggery) metody objektových tříd
Lexikální jednotky Identifikátory Klíčová slova Oddělovače Literály Komentáře
Klíčová slova Další klíčová slova naleznete v dokumentaci. (http://docs.oracle.com/cd/b19306_01/em.102/b40103/app_oracle_rese rved_words.htm)
Použití klíčových slov Co se stane, když použijete klíčové slovo jako identifikátor?
Identifikátory Pojmenování PL/SQL objektů Pravidla pro tvorbu pojmenování objektů Maximální počet znaků je 30 Musí začínat písmenem Mohou obsahovat znaky: písmena, číslice, $, _ a # Nesmí obsahovat mezery Všechny identifikátory jsou Case Sensitive
Identifikátory
PL/SQL Best Practices
PL/SQL Best Practices Konvence pro pojmenování identifikátorů Proměnné budou začínat v_název Konstanty začínají c_název Parametry (předávané funkcím a procedurám) p_název Kurzory mívají příponu název_cur Odsazuje kód dle významu Vřele doporučuji dodržovat výše zmíněná doporučení
Objekty PL/SQL Balíky Funkce Konstanty Kurzory PL/SQL tabulky Procedury Proměnné Výjimky Záznamy
Základní struktura Každý programový kód v PL/SQL má následující strukturu: [ DECLARE -- declarations ] BEGIN -- statements [ EXCEPTION -- handlers ] END;
Ahoj Světe! BEGIN DBMS_OUTPUT.PUT_LINE('Ahoj Světe!'); END;
Komentáře v PL/SQL Každý programovací jazyk podporuje komentáře, které vám umožní psát přehledně a strukturovaně Jednořádkový komentář -- komentář Víceřádkový komentář /* komentář */
Proměnné Musí se před prvním použitím deklarovat Podporované datové typy: libovolný SQL typ mimo LOB identifikátoru (BLOB, CLOB, BFILE) speciální PL/SQL typy (BINARY_INTEGER, PLS_INTEGER, BOOLEAN) nebo jejich podtypy složené datové typy (RECORD, VARRAY, TABLE) Podrobnosti viz http://docs.oracle.com/cd/b28359_01/appdev. 111/b28370/datatypes.htm
Deklarace proměnné Příklad na deklaraci proměnné: DECLARE cislo NUMBER(4); i PLS_INTEGER; ks POSITIVE NOT NULL := 3; uspech BOOLEAN DEFAULT TRUE; text VARCHAR2(3000); BEGIN -- programový blok END;
Základní skalární datové typy Znakové: CHAR, VARCHAR2, LONG; Číselné: NUMBER, PLS_INTEGER, BINARY_INTEGER, BINARY_FLOAT; Datové: DATE, TIMESTAMP, TIMESTAMP WITH TIMEZONE; Logické: BOOLEAN; %TYPE slouží pro přiřazení stejného typu jako má např. sloupec tabulky (viz později)
Přiřazení hodnoty do proměnné Existují tři možnosti přirazení hodnoty do deklarované proměnné: příkazem pro přiřazení hodnoty ( := ) výběrem hodnoty z dotazu, jehož výsledkem je jeden řádek (konkrétní hodnota): SELECT AVG(sal) INTO my_sal FROM emp; výstupního hodnota procedury (tzv. OUT nebo IN OUT parametr procedury): adjust_salary(7788, my_sal);
Užití proměnné Proměnné je možné dále užívat jak v PL/SQL, tak v SQL kódu (příkazy SQL mohou být volně užívány v PL/SQL bloku) Typické užití proměnné: výpočet výrazu parametry v SQL (přímým zadáním do SQL) předání parametru to procedury či funkce podmínky a cykly výraz, iterátor Proměnné jsou deklarovány (platné) jen pro následující blok (viditelnost, tzv. scope) Databázové položky mají před proměnnými přednost (PL/SQL vychází z SQL)
Konstanty Představují read-only proměnné Musí být inicializovány v okamžiku deklarace ( DEFAULT, := ) stejně jako NOT NULL proměnné Klíčové slovo CONSTANT Př. DECLARE pi CONSTANT REAL DEFAULT 3.141592653589793238462643383279502;
PL/SQL implicitní konverze
PL/SQL explicitní konverze Pro explicitní konverzi slouží k tomu určené funkce.
PL/SQL Best Practices Pro deklarace používejte Smysluplná jména Jeden identifikátor na řádku Využívejte NOT NULL omezení pro proměnné, které musí obsahovat hodnotu Nepoužívejte pro název proměnné název sloupce Používejte %TYPE deklarace pro proměnné, které dědí vlastnosti dříve použitých (deklarovaných) proměnných
Zanořování bloků
Pojmenování bloků
SQL funkce v PL/SQL
SQL funkce v PL/SQL
Operátory
Řídící struktury Mezi řídící struktury patří: rozhodování (if), způsob vyhodnocování vícehodnotové rozhodování (case) iterace (nekonečná loop) cyklus s podmínkou v průběhu či na konci (exit-when-loop) cyklus s podmínkou na začátku (while-loop) sekvenční cyklus (for-loop) řízení cyklů řízení posloupnosti příkazů a skoky příkaz NULL
Základní rozhodování Větvení (rozhodování) v programovém kódu realizuje podmíněný příkaz IF IF podmínka THEN -- posloupnost příkazů [ ELSIF podmínka THEN -- posloupnost příkazů ] [ ELSE -- posloupnost příkazů ] END IF;
Způsob vyhodnocování Oracle PL/SQL používá pro vyhodnocení výrazů na místě podmínek zkrácenou Booleanovu logiku tzn. vyhodnocuje zleva doprava a při prvním pozitivní úspěchu OR podmínky či prvním negativním úspěchu AND podmínky končí s vyhodnocováním příslušné části výrazu
Vícehodnotové rozhodování Nahrazení posloupnosti IF ELSIF nad stejnou posuzovanou proměnnou (selektor) Selektor musí být jednoduchá (jednohodnotová) proměnná První úspěšná WHEN ukončuje CASE CASE proměnná WHEN hodnota 1 THEN příkazy 1 ; WHEN hodnota 2 THEN příkazy 2 ; WHEN hodnota 3 THEN příkazy 3 ; [ ELSE příkazy ELSE ; ] END CASE;
Další možnosti CASE Není-li uvedena větev ELSE, je použita implicitní větev: ELSE RAISE case_not_found; Příkaz CASE lze identicky použít také v SQL pro určení hodnoty nebo ve výrazu PL/SQL V libovolném místě, kde je potřeba vybrat hodnotu se místo posloupnosti příkazů v daném případě uvede jedna hodnota Výraz CASE končí END místo END CASE
CASE s plnou podmínkou Tzv. searched CASE (podporována je tatáž funkce pro výraz CASE) umožňuje místo selektoru a množiny případů (hodnot) definovat případy podmínkou CASE WHEN podmínka 1 THEN příkazy 1 ; WHEN podmínka 2 THEN příkazy 2 ; [ ELSE příkazy ELSE ; ] END CASE;
Nekonečná iterace Základní tvar cyklu představuje nekonečnou iteraci (nekonečný cyklus) LOOP -- posloupnost příkazů END LOOP; Přerušení běhu iterace příkaz EXIT Obvyklá kombinace s rozhodováním (IF)
Cyklus s podmínkou na konci Kombinace IF a EXIT umožňuje přerušit cyklus kdykoliv v jeho průběhu Speciální případ je rozhodování s EXIT jako poslední příkaz cyklu tzv. cyklus s podmínkou na konci (typický repeat) Místo zdlouhavého zápisu IF EXIT lze užívat zápisu EXIT WHEN LOOP EXIT WHEN podmínka; END LOOP;
Pojmenovávání cyklů Jednotlivé cykly (LOOP) či vícehodnotové rozhodování (CASE) lze pojmenovat pomocí návěští, ukončit pak lze lib. cyklus <<outer>> LOOP LOOP EXIT outer WHEN podmínka; END LOOP; END LOOP outer;
Cyklus s podmínkou na začátku Jedná se o standardní while cyklus Iteraci je předřazena rozhodovací podmínka prováděná vždy před provedením těla iterace Možnost řízení pomocí EXIT WHILE podmínka LOOP -- posloupnost příkazů END LOOP;
Sekvenční cyklus Tzv. cyklus s pevným počtem opakování Iterátor (čítač) je implicitně deklarován jako typ INTEGER a je viditelný (scope) jen uvnitř cyklu Klasický for cyklus s hranicemi <l, h> FOR counter IN [ REVERSE ] l..h LOOP -- posloupnost příkazů END LOOP;
Posloupnost příkazů Není možné ukončit EXIT (slouží jen pro cykly), je nutné užít příkaz RETURN Pro skok na návěští lze užít příkazu GOTO Návěští definujeme pomocí <<label>> BEGIN GOTO insert_row; <<insert_row>> INSERT INTO emp VALUES END;
Omezení skoků Nelze provést skok do rozhodování, těla všech druhů iterací či podbloku Také nelze skákat mezi různými bloky stejné úrovně (např. THEN a ELSE blok) Rovněž nelze skákat z podprogramu do volajícího programu Naopak lze provést skok z uvedeného do vyšších programových bloků Skákat lze dopředu i zpět Také lze skákat z ošetření výjimek zpět
Zvláštní příkaz NULL Příkaz NULL představuje tělo podprogramu nevykonávajícího žádnou činnost Je užitečný tam, kde syntaxe nutí zadat příkaz či příkazy, ale my potřebujeme ponechat tuto část prázdnou Také je vhodný pro navrhování programu metodou shora-dolů NULL;
Kurzory Kurzor je pracovní oblast obsahující data (výsledná množina, tzv. result set), které lze dále využívat prostřednictvím operací nad kurzory Existují implicitní a explicitní kurzory Implicitní jsou jednořádkové SQL (INTO) Explicitní můžeme deklarovat v části DECLARE pomocí klíčového slova CURSOR Práce s kurzory se podobá souborům
Deklarace kurzoru Pomocí klíčového slova CURSOR vytvoříme ukazatel do pracovní oblasti s daty, která získáme pomocí SQL příkazu DECLARE CURSOR prvni_cur IS SELECT id_zamestnanci, prjmeni FROM zamestnanci WHERE plat > 15000 ORDER BY plat DESC;
Postup zpracování kurzoru Funguje stejně jako přístup k souborům Existují operace OPEN cursor FETCH cursor INTO record CLOSE cursor Postup práce spočívá v otevření, postupném vyčtení jednotlivých záznamů (čtení s posunem) a uzavření kurzoru Pro zjištění stavu kurzoru existují atributy
Atributy (modifikátory) kurzoru Existují následující atributy za jménem kurzoru: cursor%found obsahuje záznamy? cursor%notfound neobsahuje záznamy? cursor%isopen je otevřený? cursor%rowcount dosud zpracováno řádků Existují také atributy pro definice typů: tab%rowtype záznam typu řadku tabulky tab.column%type typ sloupce
DECLARE v_id zamestnanci.id_zamestnanci%type; v_prijmeni zamestnanci.prijmeni%type; CURSOR zam_cur IS SELECT BEGIN OPEN zam_cur; LOOP FETCH zam_cur INTO v_id, v_prijmeni; EXIT WHEN zam_cur%notfound; END LOOP; CLOSE zam_cur; END; Příklad vyčtení kurzoru
Najděte rozdíl DECLARE v_emp_id...; v_first_name...;... v_department_id...: CURSOR emp_cursor IS SELECT * FROM employees WHERE department_id =30; BEGIN OPEN emp_cursor; LOOP FETCH emp_cursor INTO v_emp_id, v_first_name,... v_department_id;... DECLARE CURSOR emp_cursor IS SELECT * FROM employees WHERE department_id = 30; v_emp_record emp_cursor%rowtype; BEGIN OPEN emp_cursor; LOOP FETCH emp_cursor INTO v_emp_record;...
Iterace kurzorem pomocí FOR Cyklus FOR zajišťuje průchod celým deklarovaným kurzorem Iterátor vzniká implicitně po dobu cyklu Otevření a zavření kurzoru zajistí samotný cyklus FOR r IN c1 LOOP dbms_output.put_line(r.name); END LOOP;
Parametrické kurzory Některé kurzory mohou být závislé na parametru (tzv. parametrické kurzory) Deklarují se s uvedením parametrů a jejich typů tyto parametry lze potom užít v SQL příkazu Při otevírání kurzoru (nebo v části IN cyklu FOR) potom specifikujeme konkrétní hodnoty pro parametry Parametry lze užít i v řetězcích typu LIKE
Příklad parametrického kurzoru DECLARE CURSOR c1(min IN NUMBER) IS SELECT emp_id, name FROM emp WHERE age > min ORDER BY age DESC; BEGIN OPEN c1(18);
Omezení kurzorů Kurzory nemohou zajistit variantní tvorbu SQL dotazu (např. přidání či vypuštění podmínky selekce či stanovení řazení) Jsou určeny pro statické SQL dotazy Existuje PL/SQL podpora také pro dynamické SQL dotazy, které jsou tvořeny za běhu Některé dynamické SQL dotazy (tzv. nativní) pracují podobně jako kurzory Musí být ale jednotného typu projekce
Definice kurzoru jako tzv. odkazu Zadefinování dotazu klauzulí FOR DECLARE TYPE typ_c1 IS REF CURSOR; c1 typ_c1; record emp%rowtype; BEGIN OPEN c1 FOR SELECT id FROM emp ; Užití nativního dynsql
Výjimky v PL/SQL Co když ale vložíme špatně název země?
Výjimky v PL/SQL - pokračování
Typy chyby Systémové chyby (např. zaplněný hard disk) Datové chyby (např. pokus o duplicitní vložení primárního klíče) Uživatelské chyby (např. snaha získat neexistující data) Mnoho dalších
Proč je potřeba tyto chyby ošetřovat? Ochránit uživatele (častý výskyt chybových hlášení může uživatele frustrovat, tak že uživatel přestane používat takovou aplikaci) Ochránit databázi (data by mohla být ztracena nebo přepsána) Zásadní chyby berou mnoho ze systémových zdrojů (neošetření chyb může způsobit časté dotazy uživatelů, a tak může být přetížena asistenční služba) Zpřehlednění kódu. Ošetření výjimek může být provedeno ve stejném bloku.
Chybová hlášení Oracle Oracle generuje chybová hlášení v rozsahu 0 až 19999 Uživatel si může dále dodefinovat chybová hlášení v rozsahu 20000 až 20999 Ke každé chybě lze stanovit text hlášení s délkou maximálně 2000 znaků Chyba 0 znamená úspěch Detekci chyb lze řešit v časti EXCEPTION
Příklady předdefinovaných výjimek NO_DATA_FOUND TOO_MANY_ROWS INVALID_CURSOR ZERO_DIVIDE DUP_VAL_ON_INDEX VALUE_ERROR Více na http://docs.oracle.com/cd/e11882_01/appdev.11 2/e10472/errors.htm#BABHDGGG
Řízení předdefinovaných výjimek Specifikováním akronymu výjimky (obecné pojmenování) v časti EXCEPTION zabráníme implicitní akci (konec PL/SQL kódu a oznámení chyby) a můžeme provést vlastní akci EXCEPTION ZERO_DIVIDE THEN -- ošetření příslušné výjimky END;
Vícenásobná detekce výjimek Je nutné v časti EXCEPTION použít výběr pomocí klauzule WHEN EXCEPTION WHEN ZERO_DIVIDE THEN -- ošetření WHEN OTHERS THEN -- ošetření END;
Úkol 1 Na základě zvoleného jména a příjmení vypište plat zaměstnance. V případě, že takový zaměstnanec neexistuje, vypište větu: Takovýto zaměstnanec neexistuje. V případě, že je více zaměstnanců s tímto jménem a příjmením vypište větu: Nelze jednoznačně určit, kterého zaměstnance máte na mysli.
Řízení nepředdefinovaných výjimek Je to podobné jako u předdefinovaných, pouze nemají předdefinované jméno. Jedná se o standardní výjimky s ORA-číslo chyby. Je možné si vytvořit vlastní pojmenování v části DECLARE spojením chybového čísla a jména pomocí funkce PRAGMA EXCEPTION_INIT
Řízení nepředdefinovaných výjimek pokračování Způsob, jak vyřešit tento problém je spojit chybu ORA-01400 s vlastím jménem
Řízení nepředdefinovaných výjimek pokračování 1. Deklarovat jméno výjimky v části deklarací. 2. Propojit deklarovanou výjimku se standardním chybovým číslem pomocí funkce PRAGMA EXCEPTION_INIT. 3. Použít deklarovanou výjimku v části ošetření výjimek.
Řízení nepředdefinovaných výjimek pokračování
Úkol 2 V rámci nepojmenovaného bloku se pokuste vložit do tabulky producenti nový řádek (úmyslně vynechte některý z povinných sloupců). Ošetřete, aby při vzniklé chybě bylo vypsáno: Nového producenta se nepodařilo vložit.
Uživatelem definované výjimky Mějme následující blok příkazů: Co se stane, kdy vyhledáváte neexistující oddělení? Tento kód nevyprodukuje žádnou chybu. Proto je potřeba definovat a vyvolat vlastí chybu
Uživatelem definované výjimky pokračování 1. Deklarace jména uživatelem definované výjimky v části deklarací: e_invalid_department EXCEPTION; 2. Použití RAISE k vyvolání výjimky v části příkazů: IF SQL%NOTFOUND THEN RAISE e_invalid_department; 3. V části zpracování výjimek odchytnout a zpracovat danou chybu: EXCEPTION WHEN e_invalid_department THEN DBMS_ OUTPUT. PUT_ LINE('CHYBA... ');
Uživatelem definované výjimky pokračování
Úkol 3 Prosřednictvím nepojmenovaného bloku změňte minimální plat na vámi načtenou hodnotu všem pracovním pozicím, které mají minimální plat nižší než je tato částka, avšak minimální plat nesmí být vyšší než maximální. V případě, že nebude provedena žádná změna vyvolejte vlastní výjimku a tuto výjimku zpracujte v části EXCEPTION.
RAISE_APPLICATION_ERROR Existuje procedura RAISE_APPLICATION_ERROR s parametry kód chyby a text chybového hlášení RAISE_APPLICATION_ERROR (error_number, message [, {TRUE FALSE}]); error_number je uživatelem specifikované číslo pro výjimku mezi 20000 a 20999 message je uživatelem specifikovaná zpráva výjimky maximální délky 2048 B. TRUE FALSE je volitelné. Pokud se použije TRUE, chyba se vloží do zásobníku chyb, při FALSE (defaultní) chyba nahradí všechny předchozí chyby.
Příklad užití
Úkol 4 Přepracujte předchozí úkol tak, aby se místo vysání informace o neprovedení změny vyskytla chybová hláška -20017
Příklad užití
Úkol 5 Spojte předchozí dva příklady. To znamená, že v případě, že nebude provedena změna, vyhodíte ERROR -20017. V části deklarací tento error propojte s Vámi deklarovanou výjimkou a tuto výjimku zpracujte.
Funkce SQLCODE a SQLERRM SQLCODE má smysl použít pouze v části EXCEPTION. Návratovou hodnotou je chybové číslo vyvolané chyby. SQLERRM(cislo) vrací chybové hlášení vztahující se k chybě s číslem cislo. Příklad použití: DBMS_OUTPUT.PUT_LINE (SQLCODE ' ' SQLERRM(SQLCODE));