PL/SQL v příkladech / Procedury a funkce
Mezi pojmenované bloky zařazujeme procedury a funkce. Ty lze volat opakovaně. Deklarativní sekci uvnitř obou druhů metod značí klíčové slovo IS, které je povinné, další členění na výkonnou část a část pro zpracování výjimek je totožné s bloky anonymními.
Procedura
DECLARE
/* */ -- prázdný komentář jen pro optické oddělení interních metod
PROCEDURE mp_VypisAktualniRok
IS
l_Rok CHAR(4):=TO_CHAR(SYSDATE, 'YYYY'); -- lokální proměnná
BEGIN
dbms_output.put_line(l_Rok); -- 2016
END;
/* */
BEGIN
-- volání bezparametrické procedury
mp_VypisAktualniRok;
-- znovupoužití procedury
mp_VypisAktualniRok;
END;
Funkce
Hlavní rozdíl mezi procedurou a funkcí je v návratové hodnotě. Procedura pouze vykoná danou logiku (dá se přirovnat k void metodám v jiných jazycích), funkce navíc vrací hodnotu předem definovaného typu.
DECLARE
l_AktualniRok CHAR(4);
/* */
FUNCTION mf_VratAktualniRok
RETURN CHAR -- návratová hodnota
IS
BEGIN
RETURN TO_CHAR(SYSDATE, 'YYYY');
END;
/* */
BEGIN
-- přiřazení výsledku bezparametrické funkce do proměnné
l_AktualniRok:=mf_VratAktualniRok;
dbms_output.put_line(l_AktualniRok); -- 2016
END;
Parametry
Vstupní
Jak procedury, tak i funkce lze zobecnit přidáním vstupních parametrů. Můžou tak provádět stále stejný kód, ale pokaždé nad jinými daty.
DECLARE
l_Vysledek NUMBER;
/* */
FUNCTION mf_Vynasob(
p_A NUMBER -- vstupní parametry
, p_B NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN (p_A*p_B);
END;
/* */
BEGIN
-- předání parametrů do volání funkce
-- a uložení výsledku do proměnné
l_Vysledek:=mf_Vynasob(
p_A => 10
, p_B => 2
);
dbms_output.put_line(l_Vysledek); -- 20
END;
Při volání metod není nutné parametry explicitně vyjmenovávat. Lze se spolehnout na pořadí v definici, pokud se ale tato změní, program s největší pravděpodobností přestane fungovat. Vstupnímu parametru je také možné nadefinovat výchozí hodnotu přes operátor přiřazení.
DECLARE
l_Vysledek NUMBER;
/* */
FUNCTION mf_Umocni(
p_Zaklad NUMBER
, p_Exponent NUMBER:=2 -- výchozí hodnota
) RETURN NUMBER
IS
BEGIN
RETURN (POWER(p_Zaklad,p_Exponent)); -- Oracle funkce umocnění
END;
/* */
BEGIN
-- sekvenční předání parametrů
l_Vysledek:=mf_Umocni(8, 3);
dbms_output.put_line(l_Vysledek); -- 512
-- návratová hodnota fce jako vstupní parametr fce jiné
-- a vynechání druhého parametru (použití výchozí hodnoty)
dbms_output.put_line(mf_Umocni(8)); -- 64
END;
Výstupní
Procedurální jazyk PL/SQL dovoluje definovat takzvané výstupní parametry, které rozdíl mezi procedurou a funkcí do značné míry stírají. Používají se všude tam, kde nestačí jedna návratová hodnota.
DECLARE
l_Den CHAR(2);
l_Mesic CHAR(2);
l_Rok CHAR(4);
/* */
PROCEDURE mp_RozdelDatum(
p_Datum DATE
, o_Den OUT CHAR
, o_Mesic OUT CHAR
, o_Rok OUT CHAR
)
IS
BEGIN
-- do OUT parametru je povolené přiřazovat
o_Den:=TO_CHAR(p_Datum, 'DD');
o_Mesic:=TO_CHAR(p_Datum, 'MM');
o_Rok:=TO_CHAR(p_Datum, 'YYYY');
END;
/* */
BEGIN
mp_RozdelDatum(
p_Datum => SYSDATE -- 5.4.2016
, o_Den => l_Den
, o_Mesic => l_Mesic
, o_Rok => l_Rok
);
dbms_output.put_line(l_Den||'/'||l_Rok||'/'||l_Mesic); -- 05/2016/04
END;
Čistě z důvodu přehlednosti doporučuji používat funkci, pokud požadujeme vrácení jedné hodnoty, při dvou a více potom proceduru s OUT parametry.
DECLARE
l_Den CHAR(2);
l_Mesic CHAR(2);
l_Rok CHAR(4);
/* */
FUNCTION mf_RozdelDatum(
p_Datum DATE
, o_Den OUT CHAR
, o_Mesic OUT CHAR
) RETURN CHAR
IS
BEGIN
o_Den:=TO_CHAR(p_Datum, 'DD');
o_Mesic:=TO_CHAR(p_Datum, 'MM');
RETURN TO_CHAR(p_Datum, 'YYYY'); -- rok jako návratová hodnota
END;
/* */
BEGIN
-- možné, nicméně nepřehledné a nelogické
l_Rok:=mf_RozdelDatum(
p_Datum => SYSDATE -- 5.4.2016
, o_Den => l_Den
, o_Mesic => l_Mesic
);
dbms_output.put_line(l_Den||'/'||l_Rok||'/'||l_Mesic); -- 05/2016/04
END;
Vstupně/výstupní
Kombinací předchozích dvou typů získáme IN OUT parametr. Proměnná, která se takovému parametru předává, interně zkopíruje svoji hodnotu do metody a po dokončení zpracování výslednou hodnotu nakopíruje zpátky ven do proměnné. Vynucení předání pouze odkazu do paměti (tedy zamezení „kopírování“ hodnoty proměnné) zprostředkovává klauzule NOCOPY před definicí datového typu.
DECLARE
l_Datum DATE:=TO_DATE('01.01.2016', 'DD.MM.YYYY');
/* */
PROCEDURE mp_PrictiMesic(
b_Datum IN OUT DATE -- binární parametr
)
IS
BEGIN
b_Datum:=ADD_MONTHS(b_Datum, 1);
END;
/* */
BEGIN
mp_PrictiMesic(l_Datum);
dbms_output.put_line(TO_CHAR(l_Datum, 'DD.MM.YYYY')); -- 01.02.2016
END;
Přetěžování
Podobně jako v ostatních jazycích, tak i v PL/SQL lze metody přetížit, tzn. definovat několik se stejným názvem lišícím se pouze počtem nebo datovým typem parametrů.
DECLARE
/* */
FUNCTION mf_VratObvod(
p_A NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN (p_A*4);
END;
/* */
FUNCTION mf_VratObvod(
p_A NUMBER
, p_B NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN ((p_A+p_B)*2);
END;
/* */
BEGIN
-- čtverec
dbms_output.put_line(mf_VratObvod(10)); -- 40
-- obdelník
dbms_output.put_line(mf_VratObvod(10, 5)); -- 30
END;
Stored procedures
U rozsáhlejších aplikací doporučuji většinu byznys logiky soustředit do packagí (více v některém z dalších dílů). Na uložené procedury a funkce jde nicméně u mnoha převážně starších projektů stále narazit, a tak je vhodné se o nich alespoň v rychlosti zmínit.
CREATE OR REPLACE FUNCTION sf_VratAktualniRok RETURN CHAR AS BEGIN RETURN TO_CHAR(SYSDATE, 'YYYY'); END; /
SELECT sf_VratAktualniRok FROM dual ; -- 2016