-- KURSORY Kurzory pouzivame pro zpracovani vysledku prikazu SELECT (typicky po jednotlivych radcich). 1) Deklarace kurzoru: Nejprve je nutne kursor deklarovat v casti declare. Tim se vsak prikaz SELECT jeste nevykonava. Syntaxe deklarace kursoru: CURSOR cursor_name [(parameter[, parameter]...)] IS select_statement; Parametr vypada: cursor_parameter_name [IN] datatype [DEFAULT expression] Priklad: SQL> DECLARE SQL> CURSOR pr1 IS SELECT jmeno,mzda FROM zamestnanci WHERE id = 1; SQL> CURSOR pr2(jm VARCHAR2) IS SELECT * FROM zamestnanci WHERE jmeno LIKE jm; SQL> BEGIN ... 2) Otevreni a uzavreni kursoru ... BEGIN OPEN pr1; OPEN pr2('Pep%'); ... CLOSE pr1; CLOSE pr2; END; 3) Ziskani dat a) Prikaz FETCH FETCH jmeno_kursoru INTO promenna,... ; Priklad: DECLARE CURSOR pr1 IS SELECT * FROM zamestnanci; my_rec zamestnanci%ROWTYPE; BEGIN OPEN pr1; LOOP FETCH pr1 INTO my_rec; EXIT WHEN pr1%NOTFOUND; -- zpracovani dat ... END LOOP; CLOSE pr1; END; b) Kursorovy FOR cyklus DECLARE CURSOR pr1 IS SELECT * FROM zamestnanci; BEGIN FOR emp_rec IN pr1 LOOP ... END LOOP; END; -- Nebo primo bez deklarace kurzoru: BEGIN FOR emp_rec IN (SELECT * FROM zamestnanci) LOOP ... END LOOP; END; c) Prikaz BULK COLLECT: Prikaz FETCH ziska vzdy jen jeden radek vysledku. Pro ziskani vsech radku vysledku muzeme vyuzit prikaz BULK COLLECT (vyzaduje promennou typu tabulka - presneji kolekce). DECLARE TYPE NameTab IS TABLE OF zamestnanci.jmeno%TYPE; names NameTab; CURSOR pr1 IS SELECT jmeno FROM zamestnanci WHERE mzda > 5000; BEGIN OPEN pr1; FETCH pr1 BULK COLLECT INTO names; CLOSE pr1; -- zpracuj names ... FOR i IN names.first .. names.last LOOP DBMS_OUTPUT.PUT_LINE('Zamestnanec ' || names(i)); END LOOP; END; -- Nekdy je nejdrive nutne povolit tento vystup pomoci prikazu sqlplus: SQL> SET SERVEROUTPUT ON 4) Atributy %FOUND, %ISOPEN, %NOTFOUND, %ROWCOUNT nad kurzory %FOUND - posledni FETCH vratil nejaka data %NOTFOUND - posledni FETCH jiz nevratil zadna data %ISOPEN - kursor je otevren %ROWCOUNT - pocet radku vysledku %FOUND %ISOPEN %NOTFOUND %ROWCOUNT OPEN before exception FALSE exception exception after NULL TRUE NULL 0 First FETCH before NULL TRUE NULL 0 after TRUE TRUE FALSE 1 Next FETCH(es) before TRUE TRUE FALSE 1 after TRUE TRUE FALSE data dependent Last FETCH before TRUE TRUE FALSE data dependent after FALSE TRUE TRUE data dependent CLOSE before FALSE TRUE TRUE data dependent after exception FALSE exception exception Vyjimka, ktera je vyvolavana, ma symbolicke jmeno INVALID_CURSOR. Tez u implicitnich kursoru: DELETE FROM emp WHERE empno = my_empno; IF SQL%FOUND THEN -- prikaz delete byl uspesny INSERT INTO new_emp VALUES (my_empno, my_ename, ...); DELETE FROM emp WHERE ... IF SQL%ROWCOUNT > 10 THEN -- pokud jsem smazal vic nez 10 radku ... ... END IF; -- Implicitni kurzory jsou automaticky vytvareny prikazy -- INSERT, DELETE, UPDATE, FORALL, SELECT INTO, COMMIT a ROLLBACK. -- Jejich obsah se vztahuje vzdy k poslednimu prikazu. -- Ukol c.1 -- Uvazujte tabulku katalog, ktera ma schema: -- katalog(nazev VARCHAR2(60), vydavatel VARCHAR2(50), isbn VARCHAR2(20), porizeno DATE) -- Cilem ukolu je katalog importovat do nasich tabulek knihy a vytisky. -- Katalog je pristupny v tabulce xdohnal.katalog. CREATE OR REPLACE PROCEDURE importuj_katalog IS ... -- V nasledujich podukolech vzdy mente (doplnujte) tuto proceduru. -- Ukol c.1a -- Pomoci kurzoru vypiste nazvy knih a jejich ISBN z katalogu -- DBMS_OUTPUT.PUT_LINE(nazev || ', ' || isbn); -- Povolte si tisk DBMS_OUTPUT pomoci: SQL> SET SERVEROUTPUT ON -- Ukol c.1b -- Podle nazvu si dohledejte ID nasi knihy a to vypisujte, -- popr. info, ze nebyla nalezna. -- Ukol c.1c -- Finale: Doplnete proceduru tak, ze bude delat import. -- Pozn: I pro vydavatele musite najit jeho ID -- (predpokladejte, ze vydavatel existuje)