1. PL-SQL
A partire da SQL2 è possibile definire delle procedure dette “stored procedures”, che
permettono di specificare dei parametri da utilizzare per lo scambio di informazioni. Una
volta definita, è utilizzabile come se fosse parte dell’insieme dei comandi SQL
predefiniti. Questo rende SQL un linguaggio Turing-completo.
1. SEZIONI DI CODICE
• DECLARE
Apre una sezione dichiarativa, di variabili, nuovi tipi o funzioni
• BEGIN
È una sezione obbligatoria per iniziare una procedura
• EXCEPTION
Sezione dove si gestiscono eventuali errori
• END
Termina la sezione di PL/SQL
Per far eseguire la procedura, bisogna che ci sia una riga costituita da un ‘.’ e una successiva
con la clausola run.
2. DICHIARAZIONE VARIABILI
Si possono usare tipi già esistenti in SQL o crearne di nuovi. In quest’ultimo caso, si usa la
clausola %TYPE, che permette di dichiarare un dato dello stesso tipo di un altro
dato o di un attributo di una relazione, senza indicare esplicitamente quale. Una
variabile dichiarata ma non inizializzata ha il valore NULL.
• DECLARE prezzo NUMBER;
• DECLARE name VARCHAR(20) := “Homer Simpson”;
• DECLARE pi CONSTANT REAL := 3.14;
• DECLARE name username%TYPE := “John”
Assegna a name lo stesso tipo di username e lo inizializza a ‘John’
2. È possibile anche definire dei tipi di dato personalizzati, a partire da uno dei tipi base
forniti dal linguaggio. Si usa la clausola SUBTYPE.
• SUBSTYPE nome_tipo IS tipo_base
{ precisione, scala, valore minimo, valore massimo }
• SUBTYPE Balance IS NUMBER (8,2)
È anche possibile assegnare il valore di una o più variabili inserendo la clausola INTO in
una query di selezione.
Esempio 1
DECLARE
bonus NUMBER (8,2)
BEGIN
SELECT salary*0.1 INTO bonus
FROM employees
WHERE employee_id = 1;
DBMS_OUTPUT.PUT_LINE(‘bonus = “ || TO_CHAR(bonus));;
END;
Output: bonus = 210
3. DIRAMAZIONE
Il costrutto ‘if’ segue la normale sintassi di un qualsiasi linguaggio di programmazione (quindi
si possono usare le clausole IF – THEN – ELSE – ELSIF – END IF). Ad esso può
essere associato il costrutto ‘case’ (quindi CASE – WHEN – THEN).
Esempio 2
DECLARE
sales INTEGER := 37500;
bonus INTEGER := 0;
BEGIN
IF sales > 50000 THEN
bonus := 1500;
ELSIF sales > 35000 THEN
bonus := 500;
ELSE
bonus := 100;
END IF;
DBMS_OUTPUT.PUT_LINE(‘Sales = ‘ || sales || ‘, bonus = ‘ || bonus || ‘.’);
END;
Ouput: Sales = 37500, bonus = 500
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
3. Esempio 3
DECLARE
grade CHAR (1);
BEGIN
grade := ‘B’;
CASE
WHEN grade = ‘A’ THEN DBMS_OUTPUT.PUT_LINE(‘Excellent’);
WHEN grade = ‘B’ THEN DBMS_OUTPUT.PUT_LINE(‘Very Good’);
END;
Ouput: Very Good
4. CICLO
PL/SQL prevede delle istruzioni di loop; come in un linguaggio di programmazione, ci sono
i cicli a condizione iniziale (WHILE – LOOP – END LOOP) e i cicli enumerativi
(FOR – IN – END LOOP). Si possono anche creare cicli infiniti o comunque senza
condizione (LOOP – END LOOP).
Esempio 4
DECLARE
X NUMBER :=0;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(‘Inside: x= ’ || TO_CHAR(x));
x++;
EXIT WHEN x>1
END LOOP
DBMS_OUTPUT.PUT_LINE(‘After: x = ‘ || TO_CHAR(x));
END;
Ouput: Inside: x=0; Inside: x=1; After: x=2
Esempio 5
BEGIN
FOR I IN 1..3 LOOP
DBMS_OUTPUT.PUT_LINE(‘i);
END LOOP
END;
Ouput: 1,2,3
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
4. 5. STRUTTURE DATI VETTORIALI
PL/SQL supporta tipi di dato vettoriali, dette collections.
Tipo
Numero
elementi
Tipo
indice
Può essere
attributo in una
relazione
Strutture
equivalenti in altri
linguaggi
Array associativo Non specificato
String o
integer
No
Tabella non
ordinata/tabella hash
Varray (array a
dimensione
variabile)
Specificato Integer
Solo se definito nello
schema
Array, List
Tabella nidificata Non specificato Integer
Solo se definito nello
schema
Set, bag
Gli array associativi sono indicati per piccoli dizionari, da costruire ogni qualvolta una
procedura viene richiamata. Nei varray, il numero di elementi può essere variabile, da 0
fino a un limite massimo dichiarato (gli indici partono da 1); sono indicati quando è
noto il numero massimo di elementi e l’accesso a essi è di tipo sequenziale; sono, tuttavia,
poco efficienti quando gli elementi sono molti. Una tabella nidificata è indicata quando il
numero di elementi non è fissato, gli indici non sono consecutivi e alcuni elementi
possono essere cancellati o modificati ma non necessariamente tutti insieme.
Per una collezione si possono usare le seguenti clausole:
• DELETE(i): cancella l’i-esimo elemento
• DELETE(i,j): cancella dall’i-esimo al j-esimo elemento
• TRIM(n): cancella n elementi dalla fine
• EXTEND(n): aggiunge n elementi alla fine
• EXISTS(n): restituisce TRUE se l’elemento di indice n esiste
• FIRST/LAST: restituisce il primo/ultimo elemento
• COUNT/LIMIT: restituisce numero elementi/massimo numero elementi
• PRIOR/NEXT: restituisce elemento precedente/successivo
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
5. Esempio 6
--Dichiarazione array associativo
DECLARE
TYPE population IS TABLE OF NUMBER
INDEX BY VARCHAR2(64);
citypop population;
i VARCHAR(64);
-- Array associativo indicizzato da string, con population come variabile associativa e i
come variabile scalare
BEGIN
citypop(Bari) := 2000;
citypop(Napoli) := 7500;
citypop(Roma) := 10000;
i = citypop.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUPUT.PUT_LINE(‘Popolazione di ‘ || i ‘ || ‘=’ || citypop(i));
i = citypop.NEXT(i);
END LOOP;
END;
Ouput: Popolazione di Bari = 2000, Popolazione di Napoli = 7500, Popolazione di Roma = 10000
Esempio 7
--Dichiarazione varray
DECLARE
TYPE team IS VARRAY(11) OF VARCHAR(15)
Juventus team := team(‘Buffon’, ‘Chiellini’, ‘Barzagli’);
-- Varray con dimensione massima 11, costituito da stringhe
BEGIN
FOR i IN 1..11 LOOP
DBMS_OUTPUT.PUT_LINE(i || ‘. ‘ || juve(i));
END LOOP;
END;
Ouput: 1. Buffon, 2. Chiellini, 3. Barzagli
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
6. 6. RECORD E CURSORI
È possibile definire variabili strutturate, ovvero record, sia in maniera diretta sia attraverso
una relazione esistente mediante la clausola %ROWTYPE. È da specificare che un
attributo di un record non può essere a sua volta un record e se si assegna NULL a un
record allora ogni suo attributo sarà a sua volta assegnato come NULL. Inoltre, è
possibile assegnare il valore di un record a un altro record, soltanto se hanno lo stesso numero
di attributi.
Esempio 8
--Dichiarazione record in modo diretto
TYPE nometipo IS RECORD (
attributo1 tipo1 [:= default], attributo2 tipo2 [:= default],
);
variabile nometipo;
-- Dichiarazione record in modo indiretto
variabile tabella%ROWTYPE;
E’ possibile definire un tipo record che corrisponda ad una proiezione su una relazione
esistente, mediante l’uso di cursori. Si utilizza appositamente la clausola CURSOR – IS.
Un cursore è un riferimento a un’area di memoria privata che contiene
informazioni sull’esecuzione di una query. Un cursore, per essere utilizzato, va aperto
con la clausola OPEN (con la quale si acquisiscono le risorse necessarie e si
posiziona il cursore prima della tupla coi risultati) e poi va chiuso con la clausola
CLOSE (che rilascia le risorse acquisite). Un cursore si può dichiarare sia
implicitamente, assegnandogli il risultato di una query SQL, sia esplicitamente, con un nome
e dei parametri.
Un cursore dispone dei seguenti parametri:
Nome Tipo Valore
%ISOPEN BOOLEAN
FALSE prima di OPEN o
dopo CLOSE altrimenti
%FOUND BOOLEAN
NULL dopo OPEN ma prima
della prima FETCH, TRUE
se l’ultima FETCH ha
restituito una tupla, FALSE
altrimenti
%NOTFOUND BOOLEAN
NULL dopo OPEN ma prima
della prima FETCH, FALSE
se l’ultima FETCH ha
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
7. restituito una tupla, TRUE
altrimenti
%ROWCOUNT PLS_INTEGER
Il numero di tuple finora
restituite dal cursore tramite
FETCH
Esempio 9
DECLARE
CURSOR c IS
SELECT nome, cognome, telefono
FROM impiegato;
friend c%ROWTYPE;
BEGIN
friend.nome := ‘John’
friend.cognome :=’Smith’
friend.telefono := ‘123458685’
DBMS_OUTPUT.PUT_LINE(friend.nome || ‘ ‘ || friend.cognome ||);
END;
È possibile anche fare in modo che il valore di un record venga assegnato dal risultato di
una query SELECT, attraverso la clausola INTO. Alternativamente, il valore del record si
può assegnare da un cursore su una query e prendendo una tupla dell’insieme dei risultati
con la clausola FETCH – INTO, che non fa altro che acquisire, una alla volta, le
tuple contenute nel cursore. Quando il cursore giunge al termine del set, l’attributo
%NOTFOUND del cursore diventa vero e si può usare come condizione di uscita.
Esempio 10
DECLARE
TYPE recordtyp IS RECORD (nome impiegato.nome%TYPE, id lavoro%TYPE);
rec1 recordtyp;
BEGIN
SELECT nome, lavoro INTO rec1
FROM impiegato JOIN lavoro ON impiegato.id = lavoro.id
WHERE impiegato.id =1;
DBMS_OUTPUT.PUT_LINE(rec1.nome || ‘ fa il ‘ || rec1.id ||);
END;
Ouput: Luigi fa il cameriere
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
8. Esempio 11
DECLARE
TYPE emptyp IS RECORD (id impiegato.id%TYPE, salario impiegato.salario%TYPE);
CURSOR descsalary RETURN empttyp IS
SELECT impiegato.id, salario FROM impiegato
ORDER BY salario DESC;
emp emptyp;
BEGIN
OPEN descsalry;
LOOP
FETCH descsalary INTO emp;
EXIT WHEN descsalary%NOTFOUND
DBMS_OUTPUT.PUT_LINE(emp.id || ‘, €‘ || emp.salario ||);
END LOOP
CLOSE descsalary;
END;
Ouput: 1, €100
È possibile definire un cursore con dei parametri, i quali possono avere dei valori
predefiniti. I parametri attuali vanno passati attraverso la clausola OPEN.
• CURSOR cursor_name (parametro_formale1 tipo1 [valore default],
parametro_formale2 tipo2 [valore default], …)
• OPEN cursor_name (parametro_attuale1, parametro_attuale2…);
Gli elementi di un cursore possono essere consultati attraverso un FOR – IN.
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
9. 7. GESTIONE DELLE TRANSAZIONI
In PL/SQL un blocco esecutivo può contenere più transazione e una singola
transazione può estendersi per più blocchi.
Esempio 12
DECLARE
lucky PLS_INTEGER;
first impiegato.nome%TYPE;
last impiegato.cognome%TYPE;
dep dipartimento%ROWTYPE;
BEGIN
dep.dipartimento_id :=100;
dep.dipartimento_nome := ‘Ufficio sinistri’
INSERT INTO dipartimento VALUES dep;
SELECT COUNT(*) INTO lucky
FROM impiegato
WHERE cognome = ‘Fantozzi’ AND nome = ‘Ugo’;
IF lucky = 0 THEN
DBMS_OUTPUT.PUT_LINE(‘Mancano impiegati’);
ROLLBACK;
ELSE
UPDATE impiegato
SET dipartimento_id = dep.dipartimento_id
WHERE cognome = ‘Fantozzi’ AND nome = ‘Ugo’;
DBMS_OUTPUT.PUT_LINE(‘Mancano impiegati’);
COMMIT;
END IF;
END;
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
10. 8. SQL DINAMICO
L’SQL dinamico è una metodologia di programmazione per generare ed eseguire
istruzioni SWL a tempo di esecuzione. È utile quando occorre scrivere programmi
altamente flessibili, con strumenti di interrogazione ad hoc, o quando a tempo di
compilazione il testo di un’istruzione SQL non è completamente noto. PL/SQL ha
due modi per eseguire SQL dinamico:
• SQL nativo
• uso del package DMBS_SQL
SQL nativo si basa sulla clausola EXECUTE IMMEDIATE
• EXECUTE IMMEDIATE sql_string [INTO lista_variabili | BULK
COLLECT INTO variabile] [USING bind_variabile]
dove sql_string può essere qualsiasi istruzione SQL o un blocco PL/SQL; la clausola
INTO è usata solo se la query restituisce una sola tupla: in caso contrario, le tuple si
possono memorizzare in una struttura vettoriale con il BULK COLLECT; infine,
USIGN è usata quando la query contiene variabili di cui fare il binding
Esempio 13
DECLARE
query VARCHAR2(200);
first impiegato.nome%TYPE;
last impiegato.cognome%TYPE;
BEGIN
query := ‘SELECT nome, cognome FROM impiegato WHERE impiegato.id =:id’;
EXECUTE IMMEDIATE query INTO first, last
USING 2;
DBMS_OUTPUT.PUT_LINE(‘Nome: ‘ || first’);
DBMS_OUTPUT.PUT_LINE(‘Cognome: ‘ || last’);
END;
Ouput: Nome: Giovanni, Cognome: Verdi
In PL/SQL, un package è un oggetto appartenente allo schema di una base di dati, che
raggruppa definizioni di tipi, variabili, costanti, sottoprogrammi, cursori ed
eccezioni. Un package viene compilato e memorizzato nel DB, dove può essere usato da più
applicazioni. È praticamente l’equivalente di una libreria. Il package DBMS_SQL
permette l’esecuzione di istruzioni SQL dinamiche. Vengono usate le clausole
OPEN_CURSOR, PARSE, EXECUTE, CLOSE_CURSOR.
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)
11. Esempio 14
DECLARE
tableName VARCHAR(30) := ‘temp’;
sqlString VARCHAR(150);
c INTEGER;
ret_code INTEGER;
BEGIN
sqlString := ‘CREATE TABLE’ || tableName || ‘(attr1 NUMBER NOT NULL)’;
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, sqlString, DBMS_SQL.V7);
ret_code := DBMS_SQL.EXECUTE(c);
DBMS_SQL.CLOSE(c);
END;
Tratto dalle dispense di Sistemi Informativi (prof. Daniele Maggiore)