Databáze I Přednáška 9
Programování s SQL interaktivní verze SQL zadávání dotazů v operátorské konzoli konzole MySQL, Oracle SQL Developer hostitelská verze SQL (Embedded SQL) SQL začleněno do nějakého programovacího jazyka většinou staticky, tj. dotazy musí být známy při překladu programovací jazyk, který umožňuje používat příkazy SQL, se nazývá hostitelský
Programování s SQL dynamické SQL generování dotazu v programovacím jazyce za běhu programu (sestaven např. dle hodnoty v políčku formuláře zadaném uživatelem) nadstavba SQL v SŘBD obohacení SQL o výpočetní/procedurální rysy programovacích jazyků v rámci SŘBD Poznámka: vestavěné SQL do jazyků i nadstavby mají dnes většinou dynamický charakter
Příklady PL/SQL nadstavba SQL v SŘBD Oracle odvozená od jazyku ADA SPL nadstavba v Informix SQL/PSM SQL/Persistent Stored Modules standard ISO rozšíření SQL o procedury a funkce používané jako uložené procedury (stored procedures)
Příklady hostitelské SQL v jazycích podpora v Oracle C, Cobol, ADA, Fortran, Pascal MS Access Visual Basic začlenění do PHP začlenění do Javy balík java.sql
PL/SQL program v PL/SQL se skládá z bloků DECLARE /* Deklarace proměnných, typů a lokálních podprogramů. */ BEGIN /* Výkonná sekce: procedury a SQL kód. */ /* Toto je jediná sekce, která je v bloku povinná. */ EXCEPTION /* Oblast ošetření výjimek */ END;
Rysy PL/SQL deklarace proměnných a konstant <název_proměnné> <typ>; kolik_bere NUMBER(6,2); <název_konstanty> CONSTANT typ := hodnota; limit CONSTANT REAL := 5000.00; typy mohou být databázové (NUMERIC, VARCHAR) nebo PL (PLS_INTEGER)
Rysy PL/SQL přiřazení hodnot přímé operátorem přiřazení := cislo := 5; výsledek dotazu SELECT plat INTO kolik_bere FROM zamestnanci WHERE cislo_zamestnance = zam_id; výstupní nebo vstupně-výstupní parametr procedury
Rysy PL/SQL DECLARE prum_plat REAL; PROCEDURE zvedni_plat (procento INT, plat IN OUT REAL) IS BEGIN plat := plat*(1+procento/100); END; BEGIN SELECT AVG(plat) INTO prum_plat FROM zam; zvedni_plat(5,prum_plat);
Rysy PL/SQL kurzory odkazy na tzv. pracovní oblasti (data), což může být jeden nebo více řádek tabulky vybrané dotazem příklad explicitně deklarovaného kurzoru DECLARE CURSOR k1 IS SELECT id, prijmeni FROM zamestnanci WHERE cislo_oddeleni = 20; nad kurzory jsou definovány další operace (otevření/zavření/načtení další řádky)
Řídicí struktury v PL/SQL podmínky IF podmínka THEN příkazy END IF; IF podmínka THEN příkazy ELSE příkazy END IF; IF podmínka THEN příkazy ELSIF podmínka THEN příkazy ELSIF podmínka THEN příkazy ELSE příkazy END IF;
větvení Řídicí struktury v PL/SQL [<<label_name>>] CASE selektor WHEN klauzule1 THEN příkazy; WHEN klauzule2 THEN příkazy;... WHEN klauzulen THEN příkazy; [ELSE sled statementůn+1] END CASE [label_name];
cykly LOOP... Řídicí struktury v PL/SQL IF podmínka THEN... EXIT; -- vyskočí z cyklu END IF; END LOOP; LOOP... EXIT WHEN podmínka; END LOOP; WHILE podmínka LOOP příkazy END LOOP;
Další vlastnosti definice modulů (modularita) definice podprogramů procedury funkce anonymní bloky definice balíčků CREATE PACKAGE sdružují proměnné, kurzory a podprogramy definice objektový typů
Další vlastnosti obsluha výjimek DECLARE... vyjimka_chybi_p EXCEPTION; -- deklarace výjimky BEGIN... IF provize IS NULL THEN RAISE vyjimka_chybi_p; -- vyvolání výjimky END IF; bonus := (plat * 0.10) + (provize * 0.15); EXCEPTION WHEN vyjimka_chybi_p THEN... -- zpracování výjimky WHEN OTHERS THEN... -- zpracování ost. výjimek END;
Triggery kód v PL/SQL může být uložen např. jako trigger trigger spoušť kód, který se automaticky spouští v SŘBD na základě nějaké události manipulace s daty operace DELETE, INSERT, UPDATE. definice dat operace CREATE, ALTER, DROP jiná operace nad databází např. SERVERERROR, LOGON, LOGOFF, STARTUP, SHUTDOWN,...
Triggery CREATE [OR REPLACE ] TRIGGER trigger_name {BEFORE AFTER INSTEAD OF } {INSERT [OR] UPDATE [OR] DELETE} [OF col_name] ON table_name [REFERENCING OLD AS o NEW AS n] [FOR EACH ROW] WHEN (condition) DECLARE Declaration-statements BEGIN Executable-statements EXCEPTION Exception-handling-statements END;
Triggery trigger, který při změně platu vypíše původní a nový plat a rozdíl CREATE OR REPLACE TRIGGER display_salary_changes BEFORE DELETE OR INSERT OR UPDATE ON customers FOR EACH ROW WHEN (NEW.ID > 0) DECLARE sal_diff number; BEGIN sal_diff := :NEW.salary - :OLD.salary; dbms_output.put_line('old salary: ' :OLD.salary); dbms_output.put_line('new salary: ' :NEW.salary); dbms_output.put_line('salary difference: ' sal_diff); END;
Ukázka program, který provede odečtení částky pouze, je-li na účtu dostatečná částka DECLARE acct_balance NUMBER(11,2); acct CONSTANT NUMBER(4) := 3; debit_amt CONSTANT NUMBER(5,2) := 500.00; BEGIN SELECT bal INTO acct_balance FROM accounts WHERE account_id = acct FOR UPDATE OF bal; -- zamkne záznam IF acct_balance >= debit_amt THEN UPDATE accounts SET bal = bal - debit_amt WHERE account_id = acct; ELSE INSERT INTO temp VALUES (acct, acct_balance, 'Insufficient funds'); END IF; COMMIT; END;
SQL PSM SQL PSM Persistent Store Modules rozšiřuje SQL na výpočetní úplnost umožňuje definovat tzv. uložené procedury a funkce první zmínka - 1994 rozlišuje externí uložené procedury (jiný jazyk, běžící mimo server) a SQL procedury příklad byl již v přednášce o SQL99
Definice procedury tabulka ucty(cislo,zustatek) procedura vrátí zůstatek na účtu do parametru zu CREATE PROCEDURE vrat_zustatek (IN c_uctu INTEGER, OUT zust DOUBLE PRECISION) BEGIN SELECT zustatek INTO zust FROM ucty WHERE cislo = c_uctu; IF zustatek < 2500 THEN SIGNAL nizky_zustatek END IF END
Definice funkce CREATE FUNCTION vrat_zustatek (IN c_uctu INTEGER) RETURNS DOUBLE PRECISION BEGIN DECLARE zust DOUBLE PRECISION; SELECT zustatek INTO zust FROM ucty WHERE cislo = c_uctu; IF zustatek < 2500 THEN SIGNAL nizky_zustatek END IF; RETURN zust END
Poznámky: procedury a funkce se volají příkazem CALL příkaz SIGNAL vyvolá speciální handler
Hostitelská verze SQL v jazyce C Princip do zdrojového kódu v C/C++ se zapíší speciálně uvozené dotazy SQL zpravidla s příponou.pc speciální prekompilátor Pro*C/C++ (proc v Linuxu) nahradí tyto dotazy voláním funkcí ze standardní knihovny run-time (SQLLIB) a převede soubor.pc do "čistého" C/C++ dále se kód překládá jako obyčejný zdrojový kód C/C++. na konci se k němu linkuje knihovna SQLLIB
Psaní dotazů SQL do kódu v C 1. všechny dotazy SQL jsou uvozeny direktivou EXEC SQL a končí středníkem ; 2. proměnných používané v SQL musí být uvozeny dvojtečkou :
Psaní dotazů SQL do kódu v C SELECT EXEC SQL BEGIN DECLARE SECTION; int plat_sefa; EXEC SQL END DECLARE SECTION; /*... */ EXEC SQL SELECT plat INTO :plat_sefa FROM Zamestnanci WHERE id_zam = 123; /*... */ printf("sefuv plat: %d\n", plat_sefa);
Psaní dotazů SQL do kódu v C INSERT typedef struct { int id; char name[20]; } rec; EXEC SQL BEGIN DECLARE SECTION; Myrec rec; EXEC SQL END DECLARE SECTION; Myrec.id=5; strcpy(myrec.name,"novak"); EXEC SQL INSERT INTO Lide VALUES (:Myrec);
Deklarativní část deklarace se uvádějí mezi EXEC SQL BEGIN DECLARE SECTION; EXEC SQL END DECLARE SECTION; definují se zde hostitelské proměnné (host variables), které se budou používat pro komunikaci mezi programem a databází (načítání dat i zápis, indikátory)
Typy hostitelských proměnných char, int, short, long, float, double VARCHAR[n] pseudotyp pro řetězce, rozpoznávaný přímo prekompilátorem Pro*C/C++ struktura v C se 2 položkami: arr pole charů len délka pole
Implicitní konverze SQL VARCHAR DATE INTEGER NUMBER(P,S) CHAR(X) Pro*C/C++ VARCHAR[n], char VARCHAR[n], char int char, short, int, long, float, double, char[n], VARCHAR[n] char[n],varchar[n], int,short,long,float, double
Spojení s databází EXEC SQL BEGIN DECLARE SECTION; char username[20]; char password[20]; EXEC SQL END DECLARE SECTION; strcpy(username, "uzivatel"); strcpy(password, "heslo"); EXEC SQL CONNECT :username IDENTIFIED BY :password; /* práce s databází */ EXEC SQL COMMIT WORK RELEASE; /* potvrzení změn, odpojení */
Ukončení transakcí ukončení transakce bez odpojení EXEC SQL COMMIT; storno změn EXEC SQL ROLLBACK; ukončení transakce s odpojením EXEC SQL COMMIT WORK RELEASE; storno změn s odpojením EXEC SQL ROLLBACK WORK RELEASE;
Ošetření chyb pomocí indikátorových proměnných proměnné typu short, deklarovány v deklarační sekci stejně jako hostitelské proměnné jsou svázané vždy s jednou konkrétní hostitelskou proměnnou indikují, co je v hostitelské proměnné obsaženo za hodnotu (NULL flags, ořezání řetězce) v rámci dotazů SQL jsou také uvozeny : následují vždy bezprostředně za příslušnou hostitelskou proměnnou
Ošetření chyb EXEC SQL SELECT plat INTO :plat_sefa :plat_sefa_id FROM Zamestnanci WHERE id_zam = 123; /*... */ if (plat_sefa_id == 0) printf("plat: %d\n", plat_sefa);
Návratové kódy v indikátorových proměnných pro SELECT Indikátor Hostitelská proměnná -1 nedefinovaná hodnota (NULL v databázi) 0 korektní naplnění -2 vkládaná hodnota je větší než proměnná a není možné určit skutečnou velikost >0 vkládaná hodnota je větší než proměnná, skutečná velikost je hodnota indikátoru
Návratové kódy v indikátorových proměnných pro INSERT Indikátor Databáze -1 uložena hodnota NULL >=0 korektní naplnění
Ošetření chyb rozšíření: EXEC SQL WHENEVER podmínka akce pokud je splněna podmínka, provede se akce podmínky: NOT FOUND kritériu SELECT/FETCH neodpovídá žádná položka SQLERROR nějaký příkaz SQL EXEC skončil chybou
SQLWARNING akce Ošetření chyb nějaký příkaz SQL EXEC skončil s varováním CONTINUE pokud lze, pokusí se program pokračovat dalším příkazem DO <cmd> vykoná se příkaz <cmd>, typicky volání funkce / exit GOTO <navesti> skok na dané návěští
STOP Ošetření chyb volání exit(), ukončení programu, nepotvrzené akce jsou stornovány (rollback) EXEC SQL WHENEVER NOT FOUND tisk_chyby(); EXEC SQL SELECT studentname INTO :st_name FROM student WHERE studentid = :id; printf("jmeno: %s.\n", st_name);
SQL v PHP původně Personal Home Page od 1997 PHP Hypertext Preprocessor skriptovací jazyk na straně serveru založený syntakticky na jazycích C, Java, Perl (interpretovaný), používá se pro dynamické generování WWW stránek je volně dostupný a nezávislý na platformě má rozsáhlou knihovnu funkcí práce s řetězci, umí přistupovat k databázím pomocí SQL
1994-1995 Historie Rasmus Lerdorf sada skriptů v Perlu ke zpracování záznamů o přístupech na server 1997 rozšíření funkčnosti, implementace v jazyce C - PHP/FI nebo PHP2 PHP3 - Andi Gutmans a Zeev Suraski
2000 PHP4 interpret zvaný Zend Engine autoři Andi Gutmans a Zeev Suraski objekty 2004 PHP5 jádro Zend Engine 2.0 výjimky, jmenné prostory listopad 2011 uvolněna verze PHP5.4 RC1 verze 6 nebyla oficiálně vydána 2015 prosinec: verze PHP7.0 64 bitová
Zápis PHP skriptů skripty se zapisují do HTML stránky mezi značky <??> Příklad: <?php if (date("a")=="am")?> { echo "Dobré ráno!"; } else { echo "Dobré odpoledne!"; } /* date("a") vrátí AM nebo PM */
SQL v PHP PHP podporuje rozhraní API pro přístup k velkému počtu databází pomocí SQL (Oracle, Sybase, MySQL) Základní kostra práce s databázemi: vytvoření připojení k databázi zaslání SQL příkazu k provedení zpracování výsledku odpojení od databáze
Příklad: $spojeni = mysqli_connect("localhost", "jméno", "heslo","databaze"); if (mysqli_connect_errno($spojeni)) { echo "Nepodařilo se připojit k MySQL. <BR>\n"; } else { @$vysledek = mysqli_query($spojeni, "SELECT * FROM Zamestnanci ORDER BY Jmeno");
if (!$vysledek) { echo "Došlo k chybě při zpracování dotazu v databázi.<br>\n"; } else { echo "V tabulce Zamestnanci je ".mysqli_num_rows($vysledek)." záznamů.<br>\n"; } } while ($zaznam = mysqli_fetch_array($vysledek)) { echo $zaznam['osobnicislo']." ". $zaznam['jmeno']."<br>"; } mysqli_close($spojeni);
Příklad databáze uživatelů a přihlašovací formulář create table users (uname VARCHAR(20), pass VARCHAR(20);
Přihlašovací formulář <form action="login.php"> <fieldset style="width:300px"> <legend> Přihlášení </legend> <table align="center"> <tr> <TD>Uživatelské jméno:</td> <TD><INPUT TYPE=TEXT NAME=jmeno></td> </tr> <TR><TD>Heslo:</td> <TD><INPUT TYPE=password NAME=heslo></td> </tr> <tr> <td align="center" colspan="2"><button type="submit">odeslat</button></td> </TABLE> </FIELDSET> </form>
login.php <?php $jmeno=$_get["jmeno"]; $heslo=$_get["heslo"]; if($jmeno == '') { echo '<h1 align="center">nezadal jste žádné jméno</h1>'; exit(); } $spojeni = mysqli_connect('localhost', '', '', 'ukazka'); if (mysqli_connect_errno($spojeni)) { echo "Nepodařilo se připojit k MySQL. <BR>\n"; } else
login.php { $vysledek = mysqli_query($spojeni, 'SELECT * FROM users WHERE uname = \''. $jmeno.'\';'); if (!$vysledek) { echo "Došlo k chybě při zpracování dotazu v databázi.<br>"; mysqli_close($spojeni); exit(); } else { if(!$zaznam = mysqli_fetch_array($vysledek)) { echo '<h1 align="center">vaše uživatelské jméno není v databázi</h1>'; mysqli_close($spojeni); exit(); } else { if($zaznam['pass']!=$heslo)
login.php { echo '<h1 align="center">nesprávné heslo</h1>'; mysqli_close($spojeni); exit(); } else { $_SESSION['logged_in']=TRUE; $vysledek = mysqli_query($spojeni, 'SELECT * FROM infousers WHERE uname = \''. $jmeno.'\';'); if ($vysledek && $zaznam = mysqli_fetch_array($vysledek) ) { echo '<h1 align="center">vaše kontaktní údaje</h1>'; echo '<hr/>'; echo $zaznam['jmeno'].' '. $zaznam['prijmeni'].'<br>'; echo $zaznam['ulice'].'<br>'; echo $zaznam['psc']. " ". $zaznam['mesto'].'<br>'; echo 'e-mail: '. $zaznam['email'].'<br>'; } } } } mysqli_close($spojeni);