![]() |
7. CVIČENÍ |
Než začnete pracovat s následujícími příklady, vytvořte si tabulku OSOBY a naplňte ji daty :
CREATE TABLE osoby ( os_cislo NUMBER(5), prijmeni VARCHAR2(30), jmeno VARCHAR2(30) ); INSERT INTO osoby VALUES (1, 'Otta', 'Max'); INSERT INTO osoby VALUES (2, 'Pesicka', 'Lada'); INSERT INTO osoby VALUES (3, 'Rohlik', 'Ondra'); COMMIT; SET SERVEROUTPUT ON
Kurzory
Kurzor je abstraktní datový typ umožňující procházet záznamy vybrané dotazem, který je s kurzorem spojen. Nad kurzorem jsou definovány následující operace :
Deklaraci a typické použití kurzoru ukazuje následující příklad :
DECLARE tmp osoby%ROWTYPE; CURSOR plist IS SELECT * FROM osoby; BEGIN OPEN plist; LOOP FETCH plist INTO tmp; EXIT WHEN plist%NOTFOUND; dbms_output.put_line(plist%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE plist; END; / |
Kurzor může mít i parametry :
DECLARE num INTEGER; CURSOR osoba (name IN VARCHAR2) IS SELECT os_cislo FROM osoby WHERE jmeno = name; BEGIN OPEN osoba('Max'); FETCH osoba INTO num; IF osoba%FOUND THEN dbms_output.put_line('Osobni cislo : '||num); END IF; CLOSE osoba; END; / |
S kurzory lze pracovat i jednodušším způsobem tak, že je spojíme s příkazem FOR - LOOP. V cyklu jsou postupně vybrány všechny záznamy kurzoru. Všimněte si, že proměnná cyklu není deklarována a její struktura odpovídá struktuře řádky vybrané kurzorem :
DECLARE CURSOR plist(num IN INTEGER) IS SELECT * FROM osoby WHERE os_cislo > num; BEGIN FOR p IN plist(1) LOOP dbms_output.put_line(p.jmeno||' '||p.prijmeni); END LOOP; END; / |
Možné je i takovéto použití :
BEGIN FOR p IN (SELECT * FROM osoby) LOOP dbms_output.put_line(p.jmeno||' '||p.prijmeni); END LOOP; END; / |
Deklaraci kurzoru lze provést i následujícím způsobem, pokud jej chcete předat jako parametr procedury nebo funkce.
DECLARE tmp osoby%ROWTYPE; TYPE t_crsr IS REF CURSOR RETURN osoby%ROWTYPE; plist t_crsr; BEGIN OPEN plist FOR SELECT * FROM osoby; LOOP FETCH plist INTO tmp; EXIT WHEN plist%NOTFOUND; dbms_output.put_line(plist%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE plist; END; / |
Předchozí deklarace specifikovala i řádkový typ, se kterým bude kurzor pracovat. Lze ovšem nadeklarovat i tzv. slabě typovaný kurzor :
DECLARE tmp osoby%ROWTYPE; TYPE t_crsr IS REF CURSOR; plist t_crsr; BEGIN OPEN plist FOR SELECT * FROM osoby; LOOP FETCH plist INTO tmp; EXIT WHEN plist%NOTFOUND; dbms_output.put_line(plist%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE plist; END; / |
Uložené procedury
Způsob vytvoření uložené procedury ukazuje následující příklad :
CREATE OR REPLACE PROCEDURE vypis_osoby AS tmp osoby%ROWTYPE; TYPE t_crsr IS REF CURSOR; plist t_crsr; BEGIN OPEN plist FOR SELECT * FROM osoby; LOOP FETCH plist INTO tmp; EXIT WHEN plist%NOTFOUND; dbms_output.put_line(plist%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE plist; END; / |
Proceduru můžete zavolat různými způsoby :
SET SERVEROUTPUT ON EXEC vypis_osoby; BEGIN vypis_osoby; END; /
Procedury můžou mít parametry, které mohou být :
CREATE OR REPLACE PROCEDURE najdi_osoby(id IN INTEGER) AS BEGIN FOR p IN (SELECT * FROM osoby WHERE os_cislo > id) LOOP dbms_output.put_line(p.jmeno||' '||p.prijmeni); END LOOP; END; / |
Volání procedury s parametrem :
EXEC najdi_osoby(1);
Procedura s vstupně-výstupním parametrem :
CREATE OR REPLACE PROCEDURE inc(a IN OUT INTEGER) AS BEGIN a := a + 1; END; / |
Volání procedury s vstupně-výstupním parametrem :
DECLARE a INTEGER; BEGIN a := 9; inc(a); dbms_output.put_line('Vysledek : '||a); END; / |
Uložené funkce
Vytvoření uložené funkce :
CREATE OR REPLACE FUNCTION secti(a IN INTEGER, b IN INTEGER) RETURN INTEGER AS BEGIN RETURN (a + b); END; / |
Volání funkce v DML příkazu :
SELECT secti(2, 3) FROM dual;
Volání fuknce v PL/SQL bloku :
DECLARE a INTEGER; b INTEGER; BEGIN a := 5; b := secti(a, 2); dbms_output.put_line('Vysledek : '||b); END; / |
Následující příklad ukazuje dva různé způsoby přiřazení skutečných parametrů formálním parametrům při volání procedurá a funkcí :
CREATE OR REPLACE FUNCTION spoj(a IN VARCHAR2, b IN VARCHAR2) RETURN VARCHAR2 AS BEGIN RETURN (a||' a '||b); END; / |
V prvním volání funkce SPOJ je přiřazení provedeno na základě pozice, v druhém volání na základě jména :
DECLARE tmp VARCHAR2(100); BEGIN tmp := spoj('Prvni', 'Druhy'); dbms_output.put_line('Vysledek : '||tmp); tmp := spoj(b => 'Prvni', a => 'Druhy'); dbms_output.put_line('Vysledek : '||tmp); END; / |
Uložené balíky procedur a funkcí
Uložené balíky procedur a funkcí slouží ke sdružení logicky spolu souvisejících procedur a funkcí. Mohou obsahovat i globální proměnné, jejichž platnost je omezena délkou aktuálního spojení s databází. Definice balíku představuje definici rozhraní balíku a těla balíku :
Rozhraní
CREATE OR REPLACE PACKAGE arithmetic AS usage INTEGER := 0; FUNCTION add(a IN INTEGER, b IN INTEGER) RETURN INTEGER; FUNCTION sub(a IN INTEGER, b IN INTEGER) RETURN INTEGER; PROCEDURE inc(a IN OUT INTEGER); END; / |
CREATE OR REPLACE PACKAGE BODY arithmetic AS FUNCTION add(a IN INTEGER, b IN INTEGER) RETURN INTEGER IS BEGIN usage := usage + 1; RETURN (a + b); END; FUNCTION sub(a IN INTEGER, b IN INTEGER) RETURN INTEGER IS BEGIN usage := usage + 1; RETURN (a - b); END; PROCEDURE inc(a IN OUT INTEGER) AS BEGIN usage := usage + 1; a := a + 1; END; END; / |
Příklad použití balíku :
DECLARE a INTEGER; b INTEGER; BEGIN a := 6; b := arithmetic.add(a, 3); arithmetic.inc(b); dbms_output.put_line('Vysledek : '||b); dbms_output.put_line('Pouzito '||arithmetic.usage||' krat.'); END; / |
Dynamické SQL
Pokud potřebujete dynamicky za běhu měnit DML nebo DDL příkazy (např. až za běhu se rozhodne se kterou tabulkou bude příkaz pracovat), použijete tzv. dynamické SQL. Příkaz lze sestavit jako řetězec znaků a ten předat ke zpracování. Následující příklad ukazuje použití dynamického SQL v kurzoru :
DECLARE stmt VARCHAR2(100); core VARCHAR2(80); cond1 VARCHAR2(20); cond2 VARCHAR2(20); tmp osoby%ROWTYPE; TYPE t_cur IS REF CURSOR; list t_cur; BEGIN dbms_output.new_line; core := 'SELECT * FROM osoby WHERE '; cond1 := 'os_cislo > 1'; cond2 := 'prijmeni = '||chr(39)||'Pesicka'||chr(39); stmt := core||cond1; dbms_output.put_line('------------------------------------------'); dbms_output.put_line(stmt); OPEN list FOR stmt; LOOP FETCH list INTO tmp; EXIT WHEN list%NOTFOUND; dbms_output.put_line(list%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE list; stmt := core||cond2; dbms_output.put_line('------------------------------------------'); dbms_output.put_line(stmt); OPEN list FOR stmt; LOOP FETCH list INTO tmp; EXIT WHEN list%NOTFOUND; dbms_output.put_line(list%ROWCOUNT||'. '||tmp.jmeno||' '||tmp.prijmeni); END LOOP; CLOSE list; END; / |
V PL/SQL nelze přímo volat DDL příkazy. Lze je však volat dynamicky :
CREATE OR REPLACE PROCEDURE zrus_tabulku(jmeno IN VARCHAR2) IS BEGIN EXECUTE IMMEDIATE 'DROP TABLE '||jmeno; END; / |
Řízení přístupu k uloženým procedurám a funkcím
Stejně jako u tabulek a pohledů lze řídit přístup k uloženým procedurám, funkcím a balíkům pomocí příkazu GRANT. Nelze řídit přístup k jednotlivým procedurám a funkcím v balíku :
GRANT EXECUTE ON secti TO PUBLIC; REVOKE EXECUTE ON spoj FROM jarda; GRANT EXECUTE ON arithmetic TO PUBLIC;
Pokud chceme, aby se procedura spouštěla s právy volajícího, použijeme klauzule AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE nove_heslo(pwd IN VARCHAR2) AUTHID CURRENT_USER IS BEGIN EXECUTE IMMEDIATE 'ALTER USER '||user||' IDENTIFIED BY '||pwd; END; / |