PL/SQL v příkladech / Kompozitní datové typy
Pole
Problematika struktur, které jsou schopné pojmout více hodnot, je díky rozdělení databáze Oracle na SQL část a procedurální PL/SQL nadstavbu poněkud rozsáhlejší. Také jednotlivé verze databáze přináší drobné odlišnosti v použití daných struktur. Omezím se tedy jen na ukázku nejpoužívanějších typů, a to asociativního pole („index-by table“) deklarovaného v PL/SQL a vnořené tabulky („nested table“) deklarované na úrovni SQL.
Asociativní pole
PL/SQL nadstavba bohužel neobsahuje žádné předpřipravené typy pro pole čísel nebo řetězců, je tedy nutné pokaždé nejdřív provést deklaraci typu a až následně deklaraci vlastní proměnné (na použití typů deklarovaných v packagi se zaměřím v některém z dalších článků).
DECLARE
-- asociativní pole čísel indexované celočíselně
TYPE Number_TT IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
lt_Cislo Number_TT;
-- asociativní pole slov indexované řetězcem
TYPE Dictionary_TT IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(4000);
lt_Slovnik Dictionary_TT;
BEGIN
lt_Cislo(1):=NULL;
lt_Cislo(2):=200;
dbms_output.put_line(lt_Cislo(2)); -- 200
-- počet prvků v poli
dbms_output.put_line(lt_Cislo.COUNT); -- 2
lt_Slovnik('prague'):='praha';
lt_Slovnik('munich'):='mnichov';
dbms_output.put_line(lt_Slovnik('prague')); -- praha
-- existuje prvek na daném indexu?
IF (lt_Slovnik.EXISTS('vienna')) THEN
-- neexistuje, nic se nevypíše
dbms_output.put_line(lt_Slovnik('vienna'));
END IF;
-- smazání pole
lt_Slovnik.DELETE;
dbms_output.put_line(lt_Slovnik.COUNT); -- 0
END;
Stejně jako u skalární proměnné, i do kompozitního typu lze přiřazovat hodnoty pomocí SQL dotazu. K tomu je určen příkaz BULK COLLECT INTO.
DECLARE
TYPE Number_TT IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
lt_Employee_ID Number_TT;
BEGIN
-- načtení všech zaměstnaneckých čísel seřazených vzestupně
SELECT employee_id
BULK COLLECT INTO
lt_Employee_ID
FROM employees
ORDER BY employee_id ASC
;
-- existuje alespoň jedno?
IF (lt_Employee_ID.COUNT>0) THEN
dbms_output.put_line(lt_Employee_ID(1)); -- 100
END IF;
END;
Nested table
Dalším velmi používaným druhem pole je takzvaná nested-tabulka. Po deklaraci, která se provádí v SQL části, s ní může pracovat kdokoli s patřičnými právy. Není tedy omezena jen na konkrétní blok.
CREATE OR REPLACE TYPE Number_TT AS TABLE OF NUMBER; /
DECLARE lt_Cislo Number_TT; lt_JineCislo Number_TT; BEGIN -- inicializace lt_Cislo:=Number_TT(); -- alokace tří pozic lt_Cislo.EXTEND(3); -- naplnění lt_Cislo(1):=100; lt_Cislo(2):=200; lt_Cislo(3):=300; dbms_output.put_line(lt_Cislo.COUNT); -- 3 -- naplnění rovnou při inicializaci lt_JineCislo:=Number_TT(100, 200, 300); dbms_output.put_line(lt_JineCislo.COUNT); -- 3 END;
Poměrně zdlouhavou práci s inicializací a alokací paměti, která u asociativního pole odpadá, vyvažuje výhoda v možnosti dotazování se pomocí jazyka SQL. Pole stačí obalit do klíčového slova TABLE(), k hodnotě se přistupuje přes VALUE().
DECLARE
l_Soucet NUMBER;
l_Prumer NUMBER;
lt_Cislo Number_TT;
BEGIN
lt_Cislo:=Number_TT(100, 200, 300);
SELECT SUM(VALUE(t))
, AVG(VALUE(t))
INTO l_Soucet
, l_Prumer
FROM TABLE(lt_Cislo) t -- "t" slouží pouze jako alias
;
dbms_output.put_line(l_Soucet); -- 600
dbms_output.put_line(l_Prumer); -- 200
END;
Rekord
O něco pokročilejším kompozitním typem je rekord suplující klasickou strukturu, jakou známe z ostatních programovacích jazyků. Jde o proměnnou sdružující několik logicky souvisejících prvků.
DECLARE
-- deklarace typu
TYPE Employee_T IS RECORD(
emp_id NUMBER
, first_name VARCHAR2(20)
, last_name VARCHAR2(20)
);
-- deklarace proměnné daného typu
l_Employee Employee_T;
BEGIN
l_Employee.emp_id:=100;
l_Employee.first_name:='Steven';
l_Employee.last_name:='King';
dbms_output.put_line(l_Employee.last_name); -- King
END;
Datový typ rekord se hodí hlavně na dočasné uložení záznamu z tabulky. Pro načtení jednoho řádku se všemi sloupci tabulky slouží typ %ROWTYPE.
DECLARE
TYPE Employee_T IS RECORD(
emp_id employees.employee_id%TYPE
, first_name employees.first_name%TYPE
, last_name employees.last_name%TYPE
);
l_Employee Employee_T;
l_Job jobs%ROWTYPE; -- typ řádek
BEGIN
-- počet sloupců a typy se musí shodovat
SELECT employee_id
, first_name
, last_name
INTO l_Employee
FROM employees
WHERE (employee_id=100)
;
dbms_output.put_line(l_Employee.last_name); -- King
-- všechny sloupce
SELECT *
INTO l_Job
FROM jobs
WHERE (job_id='IT_PROG')
;
dbms_output.put_line(l_Job.job_title); -- Programmer
END;
A na závěr dva příklady – asociativní pole rekordů a asociativní pole řádků tabulky.
DECLARE
TYPE Employee_T IS RECORD(
emp_id employees.employee_id%TYPE
, first_name employees.first_name%TYPE
, last_name employees.last_name%TYPE
);
-- pole rekordů
TYPE Employee_TT IS TABLE OF Employee_T INDEX BY PLS_INTEGER;
-- vlastní proměnná typu pole rekordů
lt_Employee Employee_TT;
BEGIN
-- jen vybrané sloupce
SELECT employee_id
, first_name
, last_name
BULK COLLECT INTO
lt_Employee
FROM employees
;
dbms_output.put_line(lt_Employee.COUNT); -- 107
END;
DECLARE
-- pole řádků
TYPE Job_TT IS TABLE OF jobs%ROWTYPE INDEX BY PLS_INTEGER;
-- vlastní proměnná typu pole řádků
lt_Job Job_TT;
BEGIN
-- všechny sloupce, všechny řádky
SELECT *
BULK COLLECT INTO
lt_Job
FROM jobs
;
dbms_output.put_line(lt_Job.COUNT); -- 19
END;