You are on page 1of 135

6.

CAPITULO VI

LENGUAJE DE PROGRAMACION PL/SQL DE ORACLE

6.1 Características de PL/SQL


Lenguaje de programación, utilizado para accesar bases de
datos ORACLE.

Extiende las capacidades de SQL, agregando elementos


encontrados en los lenguajes de programación procedurales:

• Variables y tipos
• Estructuras de control como: if – then – else, ciclos
• Procedimientos y funciones.

Basado en el lenguaje ADA: estructura de bloque, manejo de


excepciones, procedimientos, funciones, packages, etc.

Incorpora: encapsulamiento de datos, ocultamiento de


información, sobrecarga y manejo de excepciones.
6. 2

Arquitectura de ORACLE

ORACLE Tools
SQL

ORACLE 7

O/S

Data
6. 3

Herramientas de ORACLE

Herramientas de desarrollo Aplicaciones Aplicaciones de


de aplicaciones financieras Manufacturación
CASE* Dictionary Oracle General Ledger Oracle Work in Process
CASE* Designer Oracle Purchasing Oracle Costing
CASE* Generator Oracle Payables Oracle Bill of Materials
Oracle Graphics Oracle Assets Oracle Engineering
Oracle Terminal ... Oracle Master Scheduling
SQL* Plus
SQL* Forms
SQL* Menu
SQL* ReportWriter
SQL* TextRetrieval Herramientas para el
Pro* (precompiladores) soporte de decisiones
Oracle Card
Easy* SQL
SQL SQL* Calc
SQL* QMX
Utilidades de Base de ORACLE server y Data Query
Datos Diccionario de datos
Integrado
SQL* DBA PL / SQL Herramientas de
Export / Import Integración de Productos
SQL* Loader
ORACLE for 1-2-3
ORACLE for dBASE
ORACLE Soluciones de ORACLE for 4th Dimensión
relicencia de valores
agregados
Herramientas de
AI Productos de Automatización de oficinas
CAD/CAM/CIM/CAE Conectividad
Retail ORACLE* Mail
Distribution SQL* Net ORACLE* Alert
Project Management SQL* Connect ORACLE Post Card
... ORACLE Palm Link ORACLE Co Author
6. 4

Uso de SQL por parte de una aplicación.


6. 5

6.2 Motor PL/SQL


• Ejecuta bloques y subprogramas PL/SQL.

• El motor puede ser instalado en un servidor ORACLE o en


una herramienta de desarrollo de aplicaciones, tal como
SQL* Forms, SQL* Menu o SQL* ReportWriter.

• PL/SQL puede residir en dos ambientes:


El servidor ORACLE
Herramientas de ORACLE

Motor PL/SQL

Bloque Bloque Procedural Ejecutor


de sentencias
PL/SQL PL/SQL
procedurales
SQL

Motor PL/SQL
6. 6

Servidor

ORACLE
con PL/SQL

Bloque PL/SQL
SQL
IF ... THEN

SQL
ELSE

SQL
END_IF

APLICACIÓN
Servidor ORACLE con motor PL/SQL incluído.
6. 7

SQL* Forms

Motor PL/SQL
TRIGGER Bloque PL/SQL E
J S
Bloque PL/SQL DECLARE E E
P
Procedural C R
N
DECLARE Procedural U O
T
Procedural T C
E
Procedural BEGIN O E
N
Procedural R D
C
BEGIN SQL U
I
Procedural Procedural D R
A
SQL SQL E A
S
Procedural L
SQL END; E
S
END;

Servidor ORACLE Ejecutor de sentencias SQL

Motor PL/SQL en la herramienta SQL*Forms


6. 8

DECLARE
v_StudentRecord studens%ROWTYPE;
v_Counter BINARY_INTEGER;
BEGIN
v_Counter := 7;

SELECT *
INTO v_StudentRecord
FROM students
WHERE id = 10001;
END;

UPDATE classes
SET max_studetns = 70
WHERE department = ‘HIS’
AND course = 101;

Aplicación Cliente

Datos se envían a través de la red

Máquina PL/SQL
Ejecutor de sentencias
procedurales

Ejecutor de
sentencias SQL

Servidor de base de datos ORACLE

El motor PL/SQL en el servidor


6. 9

DECLARE
v_StudentRecord studens%ROWTYPE;
v_Counter BINARY_INTEGER;
BEGIN
v_Counter := 7;

SELECT *
INTO v_StudentRecord
FROM students
WHERE id = 10001;
END;

UPDATE classes
SET max_studetns = 70
WHERE department = ‘HIS’
AND course = 101;

Máquina PL/SQL
Ejecutor de sentencias
procedurales

Aplicación Cliente

Máquina PL/SQL
Ejecutor de sentencias
procedurales

Ejecutor de
sentencias SQL

Servidor de base de datos ORACLE

El motor PL/SQL en el cliente


6. 10

6.3 Versiones de PL/SQL


Versiones de ORACLE y PL/SQL
Versión Versión Características agregadas o cambiadas
Oracle PL/SQL
6 1.0 (Versión Inicial)
7.0 2.0 • Tipo de dato CHAR cambiado a longitud fija.
• Subprogramas (procedimientos, funciones,
paquetes y triggers).
• Tipos de datos compuestos definidos por el
usuario (tablas y registros).
• Comunicación intersesión con los paquetes
DBMS_PIPE y DBMS_ALERT.
• Salida en SQL*Plus o SQL*DBA con el
paquete DBMS_OUTPUT.
7.1 2.1 • Subtipos definidos por el usuario.
• Capacidad para funciones definidas por el
usuario en sentencias SQL.
• PL/SQL dinámico con el paquete
DBMS_SQL.
7.2 2.2 • Variables tipo cursor.
• Subtipos restringidos definidos por el usuario.
• Capacidad para planear procesamiento batch
PL/SQL con el paquete DBMS_JOB.
7.3 2.3 • Mejoramiento de las variables tipo cursor
(habilidad para buscar en el servidor).
• Archivos de I/O con el paquete UTL_FILE.
Atributos de tablas y tablas de registros
PL/SQL.
• Triggers almacenados en forma compilada.
6. 11

AL CONECTARSE A LA BASE DE DATOS:

Connected to:
Personal ORACLE7 Release 7.1.4.1.0 – Production Release
PL/SQL Release 2.1.4.0.0-Production

6.4 Estructura de bloque

La unidad básica en cualquier programa PL/SQL es un


BLOQUE.

Existen varias clases distintas de bloques:

• Bloques anónimos

• Bloques con nombre

• Subprogramas

• Triggers
6. 12

DECLARE

BEGIN

EXCEPTION

END;

Estructura de un bloque PL/SQL


6. 13

La estructura de un bloque considera:

• Sección de declaración

• Sección ejecutable

• Sección de manejo de excepciones

La codificación con PL/SQL considera:

• Los bloques pueden contener subloques, los que


pueden aparecer donde quiera que una declaración
ejecutable puede legalmente aparecer.

• Las sentencias terminan con un punto y coma (;)

• Los comentarios van precedidos de – ó rodeados por /* */

• Los objetos declarados existen dentro de un cierto


alcance ("scope").
6. 14

6.5 Declaración de tipos de datos y variables

A. Declaración de variables PL/SQL

Se utiliza la sección de declaración

DECLARE

BEGIN

Sección de declaración de un bloque PL/SQL


6. 15

B. Tipos de Datos PL/SQL

Tipos Escalares Tipos Compuestos

BINARY_INTEGER CHAR RECORD

POSITIVE ROWID TABLE

NATURAL
LONG

NUMBER VARCHAR
FLOAT O
VARCHAR2

DECIMAL
BOOLEAN

REAL
DATE

INTEGER
RAW

SMALLINT
LONG RAW

Tipos de datos PL/SQL


6. 16

C. Tipos de datos escalares


• CHAR
• VARCHAR2
• NUMBER
• BINARY_INTEGER
• BOOLEAN (TRUE, FALSE o NULL)
• NUMBER, CHAR, VARCHAR2 y DATE se
comportan de la misma manera que sus
correspondientes tipos de datos en la base de datos.

D. Tipos de datos compuestos


• TABLE (“Arreglo Unidimensional”)
• RECORD

E. Declaración de variables escalares

Sintaxis:

Identificador [CONSTANT] tipo_de_dato [NOT NULL] [:= expresión_plsql];


6. 17

Ejemplos:

DECLARE
firstname CHAR(20); ---- legal
lastname CHAR(20); ---- legal

• NUMBER/BINARY_INTEGER

cnt BINARY_INTEGER;
renueve NUMBER(9,2);
seconds_per_day CONSTANT NUMBER := 60*60*24;
running_total BINARY_INTEGER := 0;

• CHAR/VARCHAR2

mid_initial CHAR;
last_name VARCHAR2 (10) NOT NULL := ‘PEEBLES’;
company_name CONSTANT VARCHAR2(12) := ‘ORACLE’;
6. 18

• DATE

anniversary DATE := ’05 – JUL – 89’;


project_completion DATE;
next_checkup DATE NOT NULL := ’28 – DEC – 92’;

• BOOLEAN

over_budget BOOLEAN NOT NULL := FALSE;


available BOOLEAN := NULL;

F. Declaración de variables compuestas

1. TABLE
Sintaxis:

TYPE nombre_tipo IS TABLE OF


{tipo_columna | tabla.columna%TYPE } [NOT NULL]
INDEX BY BINARY_INTEGER;
Identificador nombre_tipo;
6. 19

Ejemplo:

DECLARE
TYPE ename_tab_type IS TABLE OF VARCHAR2 (10)
INDEX BY BINARY_INTEGER;
ename_tab ename_tab_type;

Nota:
• ename_tab representa una tabla
• Referencias se hacen mediante: ename_tab(i)

2. RECORD

Sintaxis:

TYPE nombre_tipo IS RECORD OF


(nombre_campo1 {tipo_campo | tabla.columna%TYPE} [NOT NULL],
nombre_campo2 {tipo_campo | tabla.columna%TYPE } [NOT NULL],
...);
identificador nombre_tipo;
6. 20

Ejemplo:
DECLARE
TYPE dept_rec_type IS RECORD OF
(deptno NUMBER(2) NOT NULL,
dname dept.dname%TYPE,
loc dept.loc%TYPE);
dept_rec dept_rec_type;

G. Atributos %TYPE y %ROWTYPE

Ejemplo: atributo %TYPE:

DECLARE
books_printed NUMBER(6);
books_sold books.printed%TYPE;
maiden_name emp.ename%TYPE;

Ejemplo: atributo %ROWTYPE:

DECLARE
dept_row dept%ROWTYPE;
6. 21

6.6 Asignación y conversión de tipos

A. Se asignan valores a variables compuestas y escalares.

Sintaxis:

a) Asignación escalar

variable_plsql := expresión_plsql;

b) Asignación de tablas

Nombre_tabla_plsql (valor_clave_primaria) := expresión_plsql;

c) Asignación de registros

nombre_registro.nombre_campo := expresión_plsql;
6. 22

Ejemplo:

DECLARE
TYPE dept_rec_type IS RECORD
(deptno NUMBER(2) NOT NULL,
dname dept.dname%TYPE);
TYPE ename_tab_type IS TABLE OF CHAR(10)
INDEX BY BINARY_INTEGER;
dept_rec1 dept_rec_type;
dept_rec2 dept_rec_type;
ename_tab ename_tab_type;
cnt BINARY_INTEGER;
over_budget BOOLEAN;
last_name VARCHAR2(10);
BEGIN
cnt := cnt + 1;
over_budget := TRUE;
last_name := ‘JONES’;
ename_tab(3) := last_name;
dept_rec1 := dept_rec2;
END;

B. Conversión de tipos de datos:


PL/SQL trata automáticamente de convertir los tipos de
datos, si el tipo de dato esperado no es utilizado.
6. 23

6.7 Alcance de las variables y constantes

DECLARE
credit_limit CONSTANT NUMBER (6,2) := 2000;
account NUMBER;

BEGIN
SUB-BLOQUE 1
DECLARE
account CHAR(10);
new_balance NUMBER(9,2);

BEGIN

new_balance account credit_limt


END;

SUB-BLOQUE 2
DECLARE
old_balance NUMBER(9,2);

BEGIN

old_balance account credit_limt

END;

account credit_limt
6. 24

6.8 Sentencias SQL dentro de PL/SQL


• INSERT
• UPDATE
• DELETE
• SELECT INTO

Sección ejecutable de un bloque

BEGIN

EXCEPTION

END;
6. 25

Debe considerarse lo siguiente:


• La sintaxis completa de ORACLE es soportada para
estas sentencias.
• Una variable PL/SQL puede ser colocada en cualquier
lugar donde legalmente pueda ser colocada una
constante.
• Un identificador primero es chequeado para ver si es
una columna en la base de datos. Si no lo es, se asume
que es un identificador PL/SQL.
• Estas sentencias no pueden aparecer como parte de una
expresión.
• No son soportadas sentencias DDL y algunas DCL de
SQL.
A. INSERT
DECLARE
my_sal NUMBER(7,2) := 3040.55;
my_ename CHAR(25) := ‘WANDA’;
my_hiredate DATE := ‘08-SEP-88’;

BEGIN
INSERT INTO emp (empno, ename, job, hiredate, sal, deptno)
VALUES (2741, my_ename, ‘CHAUFFEUR’, my_hiredate, my_sal, 20);
END;
6. 26

B. UPDATE

DECLARE
max_allowed CONSTANT NUMBER := 5000;
good_cust CHAR(8) := ‘VIP’;

BEGIN
UPDATE accounts SET credit_limit = max_allowed
WHERE status = ‘EMPLOYEE’ or status = good_cust;
END;

C. DELETE

DECLARE
year_var NUMBER := 1800;

BEGIN
DELETE FROM invention
WHERE year < year_var;
END;
6. 27

D. SELECT INTO
EMP
EMPNO ENAME SAL
SELECT
APLICACIÓN INTO 7844 TURNER 1500

VAR1 7654 MARTIN 1250

VAR2
VAR3

Sintaxis SELECT:

SELECT col1, col2, ... INTO var1, var2, ...


FROM nombre_tabla WHERE ...

Ejemplo SELECT:

DECLARE
part_name parts.name%TYPE;
num_in_stock parts.num%TYPE;
BEGIN
SELECT name, num INTO part_name, num_in_stock
FROM parts WHERE part_id = 624;

manipulación de los datos recuperados aquí

END;
6. 28

6.9 Referencias a funciones SQL incorporadas

Es posible referenciar a las funciones incorporadas, tanto en


sentencias SQL como en sentencias procedurales.

A. Referencia a funciones en sentencias SQL

• Numeric (por ejemplo: SQRT, ROUND, POWER)


• Character (por ejemplo: LENGTH, UPPER, SUBSTR)
• Date (por ejemplo: ADD_MONTHS, MONTHS_BETWEEN)
• Group (por ejemplo: AVG, MAX, COUNT)
• Conversion (Ejemplos: TO_CHAR, TO_DATE, TO_NUMBER)
• Misceláneas (Ejemplos: LEAST, NVL, DECODE)

Ejemplo: Uso de funciones en una sentencia DML

INSERT INTO phonebox (lastname) VALUES


(UPPER (my_last_name));
6. 29

B. Referencia a funciones en sentencias procedurales


• Funciones SQL listadas arriba, excepto el DECODE y
las funciones de grupo.
• Reporte de errores (por ejemplo: SQLCODE,
SQLERRM)
Ejemplo : Uso de funciones en una sentencia de asignación
X := SQRT(y);
lastname := UPPER(lastname);
age_diff := MONTHS_BETWEEN (birthday1, birthday2)/12;

6.10 Sentencias de control de código condicional e iterativo

BEGIN

EXCEPTION

END;

Sección ejecutable
6. 30

A. Ejecución de comparaciones lógicas

Comparaciones lógicas:

• Consisten de expresiones simples o complejas


separadas por operadores relacionales.
• Los resultados son siempre, TRUE, FALSE o NULL.

Comparaciones nulas:

• Cualquier cosa comparada con NULL, resulta en un


valor NULL.
• Un NULL en una expresión se evalúa a NULL
(excepto en concatenaciones).

Ejemplos:

5 + NULL - - evalúa a NULL

‘PL/’ | | NULL | | ‘SQL’ -- evalúa a PL/ SQL


6. 31

Tablas de verdad de los operadores lógicos

AND TRUE FALSE NULL OR TRUE FALSE NULL

TRUE TRUE T T T
T F N

T F N
FALSE F F F FALSE

T N N
NULL NULL
N F N

NOT

TRUE F

T
FALSE

N
NULL

B. Sentencia IF-THEN-ELSE
Sintaxis:

IF <condición> THEN
<secuencia de sentencias>
[ELSIF <condición> THEN
<secuencia de sentencias>]
-- ELSEIF`s pueden ser repetidos
[ELSE
<secuencia de sentencias> ]
END IF;
6. 32

Ejemplo de la sentencia IF-THEN-ELSE:

DECLARE
num_jobs NUMBER(7);

BEGIN
SELECT COUNT(*) INTO num_jobs FROM auditions
WHERE actorid = &&actor_id AND called_back = ‘YES’;

IF num_jobs > 90 THEN


UPDATE actor SET actor_rating = ‘OSCAR time’
WHERE actorid = &&actor_id;
ELSIF num_jobs > 75 THEN
UPDATE actor SET actor_rating = ‘Daytime soaps’
WHERE actorid = &&actor_id;
ELSE
UPDATE actor SET actor_rating = ‘Waiter’
WHERE actorid = &&actor_id;
END IF;

COMMIT;
END;
6. 33

C. Sentencias de ciclo

4 Tipos de Loops:
• Ciclo simple
• Ciclo FOR numérico
• Ciclo WHILE
• Ciclo FOR de cursores

1. Ciclo simple

Sintaxis de un ciclo simple:

LOOP
<secuencia de sentencias>
END LOOP; -- a veces llamado ciclo infinito

Sintaxis de Exit:

EXIT [when <CONDICIÓN> ];


6. 34

Ejemplos:

DECLARE
ctr NUMBER(3) := 0;
BEGIN
LOOP
INSERT INTO LOG VALUES
(ctr, ‘ITERACION COMPLETA’);
ctr : = ctr + 1;
IF ctr = 500 THEN
EXIT;
END IF;
END LOOP;
END;

DECLARE
ctr NUMBER(3) := 0;
BEGIN
LOOP
UPDATE TABLE1 SET comment = ‘ACTUALIZADO’
WHERE count_col = ctr;
ctr : = ctr + 1;
EXIT WHEN ctr = 100;
END LOOP;
END;
6. 35

2. Ciclo FOR numérico


Sintaxis:

FOR <índice> IN [REVERSE] <integer> .. <integer> LOOP


<secuencia de sentencias>
END LOOP;

Ejemplos:
BEGIN
FOR i IN 1..500 LOOP
INSERT INTO temp (col1, message)
VALUES (i, ‘No me dormiré en clases’);
END LOOP;
END;

DECLARE
my_index CHAR(20) := ‘Fetuccini Alfredo’;
BEGIN
FOR my_index IN REVERSE 21..30 LOOP
/* redeclaración de my_index*/
INSERT INTO temp (col1)
VALUES (my_index);
/*Inserta los números 30 a 21*/
END LOOP;
END;
6. 36

...
FOR i IN 1..256 LOOP
x := x + i; --legal
i := i + 5; -- ilegal
END LOOP;
...

En el cliclo FOR numérico, el índice utilizado es:


• Implícitamente de tipo NUMBER.
• Definido sólo dentro del loop.
• Su valor puede ser referenciado en una expresión,
pero un nuevo valor no puede ser asignado al índice
dentro del loop.

3. Ciclo WHILE
Sintaxis:

WHILE <condición> LOOP


<secuencia de sentencias>
END LOOP;
6. 37

Ejemplo:

DECLARE
ctr NUMBER(3) := 0;
BEGIN
WHILE ctr < 500 LOOP
INSERT INTO temp (col1, message)
VALUES (ctr, ‘Bien, podría dormir sólo un poquito’);
ctr := ctr + 1;
END LOOP;
END;

4. Sentencia GO TO

Sintaxis:

<<nombre_rótulo>> x := x + 1; -- un rótulo de sentencia


GO TO nombre_rótulo; -- transfiere el control a x:= x + 1;
6. 38

Legal Ilegal
<<dinner>> GO TO your_brothers;
x := x + 1; IF a > b THEN
y := y + x; b := b – c;
IF a > b THEN <<your_brothers>>
b := b + c; x := x + 1;
GO TO dinner; END IF;
END IF;

Referencia a rótulos de sentencia:


• Los rótulos pueden rotular a cualquier sentencia.
• Además de su uso en sentencias asociadas a un GO TO, los
rótulos se pueden usar en bloques y ciclos.
• Los rótulos en un bloque permiten referenciar objetos
declarados, que podrían de otra manera no ser visibles
debido a las reglas de alcance.
Sintaxis:
<<nombre_rótulo>>
[DECLARE
-- las declaraciones van aquí]
BEGIN
-- sentencias ejecutables van aquí
[EXCEPTION
-- manejo de excepciones aquí]
END nombre_rótulo; --debe incluir el nombre del rótulo
6. 39

Ejemplo:
<<outer_block>>
DECLARE
n NUMBER;
BEGIN
n := 5;
/* Comienzo de un sub bloque */
DECLARE
x NUMBER := 10;
n CHAR(10) := ‘Quince’;
BEGIN
INSERT INTO temp (col1, col2, message)
VALUES (outer_block.n, x, n);
COMMIT;
END; /*fin del sub bloque */
END outer_block

Ejemplo:
<<sample>>
DECLARE
deptno NUMBER := 20;
BEGIN
UPDATE emp SET sal = sal * 1.1;
WHERE deptno = sample.deptno;
COMMIT;
END sample;
6. 40

Ejemplo:

<<compute_loop>>
FOR i IN 1..10 LOOP
<sentencias>
DECLARE
i NUMBER := 0;
BEGIN
INSERT INTO temp (col1, col2, message)
VALUES (i, compute_loop.i, ‘COMPLETE’);
END;
END LOOP compute_loop; -- debe incluir el nombre del loop aquí

Ejemplo:

...
<<loop_externo>> WHILE a > b LOOP
b := b + 1;
<<loop_interno>> WHILE b > c LOOP
c := c + 2;
EXIT loop_externo WHEN c > 200;
END LOOP loop_interno;
END LOOP loop_externo;
...
6. 41

6.11 Cursores
A. Conceptos

DECLARE

BEGIN

Sección de declaración y sección ejecutable al crear un cursor

Toda sentencia del DML SQL procesada por PL/SQL tiene


un cursor asociado.
6. 42

Sentencias SQL asociadas a un cursor:

• INSERT
• UPDATE
• DELETE
• SELECT INTO
• COMMIT
• ROLLBACK

B. Tipos de cursor

Tipo Descripción
Explícito Sentencias SELECT con
múltiples filas en el resultado.
Implícito Todas las sentencias INSERT
Todas las sentencias UPDATE
Todas las sentencias DELETE
Sentencias SELECT .. INTO
6. 43

.
. Obtiene una fila
.

Cursor

. Procesa una fila


.
.

Continuar hasta que esté


. vacío
.
.

Cursor

Declaración y uso de cursores explícitos


6. 44

El conjunto de filas retornadas por una consulta puede


consistir de cero, una o más filas, dependiendo del número de
filas que satisfagan la condición de búsqueda.
Cuando una consulta retorna múltiples filas, un cursor puede
ser definido explícitamente para:
• Procesar más allá de la primera fila recuperada por la
consulta.
• Mantener la pista de cual es la fila que está actualmente
siendo procesada.

C. Pasos para declarar y usar un cursor:


1. Declarar el cursor
2. Abrir el cursor
3. Sacar los datos desde el cursor
4. Cerrar el cursor

1.) Declaración de un cursor para asociar su nombre


con una sentencia SELECT

Sintaxis de la declaración del cursor:


DECLARE
CURSOR <nombre_cursor>
IS <sentencia_select_regular >;
6. 45

Ejemplo: Declaración de un CURSOR

DECLARE
x NUMBER(7,2);
total NUMBER(5);
limite_inferior_salario CONSTANT NUMBER(4) := 1200;
CURSOR c1 IS SELECT ename FROM emp
WHERE sal > limite_inferior_salario;
BEGIN. . .

2.) Abrir el cursor para procesar la sentencia SELECT y


almacenar las filas retornadas en el cursor

Sintaxis para abrir el cursor:

OPEN <nombre_cursor>;

3.) Extraer los datos desde el cursor y almacenarlos en


las variables especificadas

Sintaxis para el Fetch a los datos:

FETCH <nombre_cursor> INTO <var1, var2, . . .>;


6. 46

4.) Cerrar el cursor para liberar el recurso


Sintaxis para cerrar el cursor:

CLOSE <nombre_cursor>

D. Referencia a los atributos de los cursores explícitos:


%NOTFOUND, %FOUND, %ROWCOUNT, %ISOPEN

1.) %NOTFOUND se evalúa en TRUE, si el FETCH


previo no retorna filas.

Ejemplo %NOTFOUND:
LOOP
FETCH my_cursor INTO my_ename, my_sal;
EXIT WHEN my_cursor%NOTFOUND;
-- procesamiento de datos aquí
END LOOP;

2.) %FOUND se evalúa en TRUE, si el FETCH


previo retorna una fila

Ejemplo %FOUND:

FETCH my_cursor INTO my_ename, my_sal;


WHILE my_cursor%FOUND LOOP
-- procesamiento de datos aquí
FETCH my_cursor INTO my_ename, my_sal;
END LOOP;
6. 47

3.) %ROWCOUNT se evalúa al número total de filas


obtenidas hasta el momento.

Ejemplo %ROWCOUNT:

LOOP
FETCH my_cursor INTO my_ename, my_sal;
EXIT WHEN (my_cursor%NOTFOUND)
OR (my_cursor%ROWCOUNT > 10);
-- procesamiento de datos aquí
END LOOP;

4.) %ISOPEN se evalúa en TRUE, si el cursor asociado


está abierto.

Ejemplo %ISOPEN:

IF my_cursor%ISOPEN THEN
FETCH my_cursor INTO my_ename, my_sal;
ELSE
OPEN my_cursor;
END IF;
6. 48

Ejemplo:

DECLARE
sal_limit NUMBER(4) := 0
my_ename emp.ename%TYPE;
my_sal emp.sal%TYPE;
CURSOR my_cursor IS SELECT ename, sal FROM emp
WHERE sal > sal_limit;
BEGIN
sal_limit := 1200;
OPEN my_cursor; -- use 1200 como sal_limit
LOOP
FETCH my_cursor INTO my_ename, my_sal;
EXIT WHEN my_cursor%NOTFOUND; -- nada retornado
INSERT INTO new_table VALUES (my_ename, my_sal);
END LOOP;
CLOSE my_cursor;
COMMIT;
END;
6. 49

Ejemplo: Reapertura de un cursor


DECLARE
total_bonus NUMBER(8,2) := 0;
min_sal emp.sal%TYPE := 1000; -- Suposición inicial
bonus_amt emp.sal%TYPE;
CURSOR bonus IS
SELECT sal * .10 FROM emp WHERE sal > min_sal;
BEGIN
OPEN bonus; -- usa el valor de min_sal de 1000
LOOP
FETCH bonus INTO bonus_amt;
EXIT WHEN bonus%NOTFOUND;
total_bonus := total_bonus + bonus_amt;
IF total_bonus > 2000 THEN
/* incrementar el mínimo y tratar de nuevo */
min_sal := min_sal + 500;
total_bonus := 0 --- resetear el total
CLOSE bonus;
OPEN bonus; -- re abrir con nuevo min_sal
END IF;
END LOOP;
-- se podría querer almacenar min_sal en algún lado
END;
6. 50

E. Referencia a la fila actual del cursor


Sintaxis:

WHERE CURRENT OF <nombre_cursor>

Ejemplo:

DECLARE
CURSOR my_cur IS SELECT ename, sal FROM emp
FOR UPDATE of sal;
emp_name emp.ename%TYPE;
salary emp.sal%TYPE;
BEGIN
OPEN my_cur;
LOOP
FETCH my_cur INTO emp_name, salary;
EXIT WHEN my_cur%NOTFOUND;
IF salary > 3000 THEN
DELETE FROM emp WHERE CURRENT OF my_cur;
END IF;
...
END LOOP;
6. 51

F. Ciclo FOR para referenciar a un cursor:


Permite la especificación de una secuencia de sentencias, para
ser repetida, una vez por cada fila que sea retornada por el
cursor.
Sintaxis ciclo FOR para el cursor:
FOR <nombre_record> IN <nombre_cursor> LOOP
-- sentencias para ser repetidas, aquí
END LOOP;

Esta clase de ciclo FOR considera:


• Los ciclos FOR de cursores son similares a los ciclos FOR
numéricos.
• Los ciclos FOR especifican un conjunto de filas de una
tabla, usando el nombre del cursor. Los ciclos FOR
numéricos especifican un rango de enteros.
• Un registro de un ciclo FOR para el cursor, toma los
valores de cada fila. Un índice de un ciclo FOR numérico,
toma cada valor en el rango.
• nombre_record es implícitamente declarado como:
nombre_record nombre_cursor%ROWTYPE;

• Para referenciar a un elemento del registro, se usa la


notación nombre_record.nombre_columna.
6. 52

G. Modelo conceptual del ciclo FOR para el cursor:


• Cuando es iniciado el ciclo, se ejecuta un OPEN
nombre_cursor implícito.
• Por cada fila que satisface la consulta asociada al
cursor, se ejecuta un FETCH implícito hacia los
componentes de nombre_record.
• Cuando no existen más filas que retornar con FETCH,
se ejecuta un CLOSE nombre_cursor implícito y se
sale del ciclo.
Ejemplo:
DECLARE
sal_limit NUMBER(4) := 0;
total_sal NUMBER(9,2) := 0;
CURSOR my_cursor IS SELECT ename, sal FROM emp
WHERE sal > sal_limit;
BEGIN
sal_limit := 1200;
-- OPEN implícito aquí
FOR cursor_row IN my_cursor LOOP
-- se hace un implícito FETCH aquí
INSERT INTO new_table
VALUES(cursor_row.ename, cursor_row.sal);
total_sal := total_sal + cursor_row.sal;
END LOOP; -- se hace un close implícito aquí
-- almacena total_sal en tabla temp
INSERT INTO temp (COL1, MESSAGE)
VALUES (total_sal, ‘Salario total’);
COMMIT;
END;
6. 53

Ejemplo: Carga de datos dentro de una tabla PL/SQL,


mediante un ciclo FOR para cursor:

DECLARE
TYPE ename_tab_type IS TABLE OF emp.ename%TYPE
INDEX BY BINARY INTEGER;
TYPE sal_tab_type IS TABLE OF emp.sal%TYPE
INDEX BY BINARY INTEGER;
ename_tab ename_tab_type;
sal_tab sal_tab_type;
i BINARY_INTEGER := 0;
CURSOR empcur IS SELECT ename, sal FROM emp;
BEGIN
-- cargar nombres de empleados y salarios dentro
-- de las tablas PL/SQL.
FOR emprec IN empcur LOOP
i := i + 1;
ename_tab(i) := emprec.ename;
sal_tab(i) := emprec.sal;
END LOOP;
-- procesos de tablas PL/SQL
...
END;
6. 54

H. Declaración explícita de parámetros para los


cursores
Sintaxis:
DECLARE
CURSOR <nombre_cursor> [(nombre_parametro tipo_parametro)]
IS <sentencia select regular >;

Ejemplo:
DECLARE
CURSOR emp_cur (sal_value NUMBER) IS
SELECT ename FROM emp WHERE sal > sal_value;
BEGIN
OPEN emp_cur (1200);
...
END;

Es equivalente a:

DECLARE
sal_value NUMBER;
CURSOR emp_cur IS
SELECT ename FROM emp WHERE sal > sal_value;
BEGIN
sal_value := 1200;
OPEN emp_cur;
...
END;
6. 55

I. Referencia a los atributos de los cursores implícitos:


SQL%NOTFOUND, SQL%FOUND,
SQL%ROWCOUNT, SQL%ISOPEN.

1.) SQL%NOTFOUND se evalúa a TRUE, si la sentencia SQL


más recientemente ejecutada, no afecta a ninguna tupla.
Ejemplo de SQL%NOTFOUND:
UPDATE emp SET sal = sal*10.0 WHERE ename = ‘WARD’;
IF SQL%NOTFOUND THEN -- WARD no fue encontrado
INSERT INTO emp(empno, ename, sal) VALUES
(1234,’WARD’,99999);
END IF;

2.) SQL%FOUND se evalúa a TRUE, si la sentencia SQL


más recientemente ejecutada afecta a una o más tuplas.
3.) SQL%ROWCOUNT se evalúa al número de tuplas
afectadas por una sentencia DELETE o UPDATE, Al
número de tuplas INSERTadas, o al número de filas
recuperadas por un SELECT INTO.
Ejemplo de SQL%ROWCOUNT:
DELETE FROM baseball_team WHERE batting_avg < .100;
IF SQL%ROWCOUNT > 5 THEN
INSERT INTO temp (message)
VALUES (‘Tu equipo necesita ayuda’);
END IF;

4.) SQL%ISOPEN siempre se evalúa a FALSE.


6. 56

6.12 Manejo de excepciones


A. CONCEPTOS
Se utiliza la sección de excepciones

Sección de excepciones

BEGIN

EXCEPTION

END;
6. 57

En PL/SQL, los errores son llamados excepciones.


Cuando se levanta una excepción, el proceso salta a los
manejadores de excepciones.
Un manejador de excepciones es una secuencia de sentencias
a ser procesadas cuando una cierta excepción ocurre.
Cuando el manejador de excepción está completado, termina
el procesamiento del bloque.
B. Tipos de excepciones
• Excepciones internas pre definidas:
Corresponden aproximadamente a 20 errores comunes en
ORACLE.
Son levantadas automáticamente por PL/SQL, en
respuesta a un error ORACLE.
• Excepciones definidas por el usuario:
Deben ser declaradas.
Deben ser levantadas explícitamente.
Declaración del manejador de excepciones:
Sintaxis:
WHEN <nombre_excepción> [OR <nombre_excepción> ....] THEN
<secuencia de sentencias>
...
[WHEN OTHERS THEN --si es usado, debe ser el último
manejador
<secuencia de sentencias> ]
6. 58

Ejemplo:
DECLARE
employee_num emp.empno%TYPE;

BEGIN
SELECT empno INTO employee_num FROM emp
WHERE ename = ‘BLAKE’;
INSERT INTO temp (col1, message)
VALUES (employee_num, ‘Blake’’s employee number.’);
DELETE FROM emp WHERE ename = ‘BLAKE’;

EXCEPTION
WHEN NO_DATA_FOUND THEN
ROLLBACK;
INSERT INTO temp (message)
VALUES (‘BLAKE no encontrado’);
COMMIT;
WHEN TOO_MANY_ROWS THEN
ROLLBACK;
INSERT INTO temp (message)
VALUES (‘Más de un BLAKE encontrado’);
COMMIT;
WHEN OTHERS THEN
ROLLBACK;
END;
6. 59

Declaración de excepciones definidas por el usuario:


Las excepciones pre definidas por el usuario se definen y
levantan explícitamente.
Ejemplos:

DECLARE
x NUMBER;
my_exception EXCEPTION; -- un nuevo tipo de objeto
...

RAISE excepción (llamado a la excepción predefinida):

RAISE my_exception;

Debe considerarse:
• Una vez que la excepción es levantada manualmente,
se trata exactamente como si fuese una excepción
interna predefinida.
• El alcance de la declaración de excepciones es igual
que con las variables.
• Una excepción predefinida por el usuario es chequeada
manualmente y levantada si es apropiado.
6. 60

Ejemplo:
DECLARE
my_ename emp.ename%TYPE := ‘BLAKE’;
assigned_projects NUMBER;
too_few_projects EXCEPTION;

BEGIN
-- obtiene los números de proyectos asignados a BLAKE
...
IF assigned_projects < 3 THEN
RAISE too_few_projects;
END IF;

EXCEPTION -- comienzo de los manejadores de excepciones


WHEN too_few_projects THEN
INSERT INTO temp
VALUES (my_ename, assigned_projects,
‘MENOS DE 3 PROYECTOS!’);
COMMIT;
...
END;
6. 61

C. Propagación de Excepciones

Pasos en la propagación:

1. El bloque actual es buscado para un manejador. Si no lo


encuentra, ir al paso 2.

2. Si se encuentra un bloque que encierra, éste es buscado


para un manejador.

3. Se repiten los pasos 1 y 2 hasta que no se encuentren


más bloques que encierran, o hasta que se encuentre un
manejador.
• Si no existen más bloques que encierran, la
excepción es pasada de vuelta al ambiente que la
llama (SQL* Plus, SQL* Forms, un programa pre
compilado, etc.).
• Si se encuentra un manejador, éste es ejecutado.
Cuando esto se hace, el bloque en el cual se
encontró el manejador es terminado y el control es
pasado al bloque que encierra (si existe), o al
ambiente (si no existe el bloque).
6. 62

Ejemplo:

BEGIN
...

BEGIN
IF X = 1 THEN
RAISE A;
ELSEIF X = 2 THEN
RAISE B;
ELSE
RAISE C;

EXCEPTION
WHEN A THEN Exception A es
... manejada localmente y
la ejecución se reasume
END; en el bloque externo

_________
_________
_________

EXCEPTION
WHEN B THEN
...
END;
6. 63

Ejemplo:

BEGIN
...
BEGIN
IF X = 1 THEN
RAISE A;
ELSIF X = 2 THEN
RAISE B;
ELSE
RAISE C;

EXCEPTION
WHEN A THEN
Excepción B se propaga
...
al primer bloque externo
END;
con un manejador
apropiado

EXCEPTION
WHEN B THEN
... Excepción B es manejada
y el control es pasado de
END; vuelta al ambiente.
6. 64

Ejemplo:

BEGIN
...

BEGIN
IF X = 1 THEN
RAISE A;
ELSEIF X = 2 THEN
RAISE B;
ELSE
RAISE C;

EXCEPTION
WHEN A THEN
... La excepción C no tiene
manejador y resultará en
END;
una excepción no manejada

EXCEPTION en tiempo de ejecución.

WHEN B THEN
...

END;
6. 65

Otros usos del RAISE:

Esta sentencia re levanta la excepción actual (como sí


estuviera siendo propagada).

Sintaxis:

RAISE;

Es necesario tener en cuenta:


• RAISE sólo puede ser usado en un manejador de
excepciones.

D. Excepciones internas predefinidas

Cualquier error ORACLE levanta automáticamente una


excepción; algunos de los errores más comunes tienen
nombres.
6. 66

Ejemplos:

TOO_MANY_ROWS (ORA-01427)
--- un SELECT de única fila, retornó más de una fila

NO_DATA_FOUND (ORA-01403)
--- un SELECT de única fila, no retornó datos

INVALID_CURSOR (ORA-01001)
--- Ocurrió una operación ilegal con el cursor

VALUE_ERROR (ORA-06502)
--- Ocurrió un error aritmético, de conversión, truncamiento o de restricción.

INVALID_NUMBER (ORA-01722)
--- Falló la conversión de un string de caracteres a un número en una sentencia SQL.

ZERO_DIVIDE (ORA-01476)
--- Intento de dividir por cero

DUP_VAL_ON_INDEX (ORA-00001)
--- Intento de insertar un valor duplicado dentro de una columna, la que tiene un
índice asociado que permite valores únicos en la columna.

CURSOR_ALREADY_OPEN (ORA-06511)
--- Intento de abrir un cursor ya abierto
6. 67

E. Pragma EXCEPTION_INIT
Las excepciones sólo pueden ser manejadas por el nombre y
no a través del número de error ORACLE.

Con el pragma EXCEPTION_INIT, se puede dar un nombre


a un error particular de ORACLE.

Esto da la capacidad de atrapar un error específico, en lugar


de hacerlo vía un manejador OTHERS.

Sintaxis:

PRAGMA EXCEPTION_INIT (<nombre_excepcion_definida_por_el_usuario>,


<número_error_ORACLE >);

Ejemplo:

DECLARE
dead_lock_detected EXCEPTION;
PRAGMA EXCEPTION_INIT (dead_lock_detected, -60);
6. 68

Ejemplo:
Se levantará la excepción definida por un usuario
e_MissingNull si se encuentra el error “ORA-1400: columna
obligatoria NOT NULL falta o es NULL durante la
inserción” en tiempo de ejecución.

DECLARE
e_MissingNull EXCEPTION;
PRAGMA EXCEPTION_INIT (e_MissingNull, -1400);
BEGIN
INSERT INTO students (id) VALUES (NULL);
EXCEPTION
WHEN e_MissingNull then
INSERT INTO log_table (info) VALUES (‘ORA-1400 ocurre’);
END;

F. Función RAISE_APLICATION_ERROR

La función RAISE_APLICATION_ERROR permite al


programador, crear sus propios mensajes de error.

Los errores definidos por el usuario son pasados fuera del


bloque, de la misma forma que los errores de Oracle.
6. 69

La sintaxis de RAISE_APPLICATION_ERROR es:

RAISE_APPLICATION_ERROR(número de error,
mensaje de error, [guarda errores]);

Donde:

• número de error: parámetro entre –20.000 y –20.999


• mensaje de error: texto asociado con este error
• guarda errores: un valor booleano opcional.
Si es TRUE, el nuevo error es agregado a la lista de
errores ya levantados (si existe una).
Si es FALSE, que es el valor por defecto, el nuevo error
remplazará la actual lista de errores.

Ejemplo:
El siguiente procedimiento chequea si existe suficiente
espacio en una sala de clase antes de incorporar a un nuevo
estudiante.
6. 70

CREATE OR REPLACE PROCEDURE Register (


/* Registra al estudiante identificado por el parámetro p_StudentID, en la
clase identificada por los parámetros p_Department y p_Course. Antes de
llamar a ClassPackage.AddStudent, el cual realmente agrega el estudiante a
la clase, este procedimiento verifica que exista suficiente espacio en una sala
de clase, y que la clase existe. */
p_StudentID IN students.id%TYPE,
p_Department IN classes.department%TYPE,
p_Course IN classes.course%TYPE) AS

v_CurrentStudents NUMBER; -- Número actual de estudiantes en la clase


v_MaxStudents NUMBER; -- Número máximo de estudiantes en la clase
BEGIN
/* Determina el número actual de estudiantes registrados, y el número máximo
de estudiantes permitidos para registrarse. */
SELECT current_students, max_students
INTO v_CurrentStudents, v_maxStudents
FROM classes
WHERE course = p_Course
AND department = p_Department;

/*Asegurarse de que existe suficiente espacio para este estudiante adicional*/


IF v_CurrentStudents + 1 > v_MaxStudents THEN
RAISE_APPLICATION_ERROR (-20000, ‘No puede agregar más estudiantes a’
| |p_Department | | ‘ ‘ | | p_Course);
END IF;
/*Agrega el estudiante a la clase. */
ClassPackage.AddStudent (p_StudentID, p_Department, p_Course);
...
6. 71

EXCEPTION
WHEN NO_DATA_FOUND THEN
/* La información de la clase pasada a este procedimiento no existe. Levantar un
error para que el programa que llamó conozca esto */
RAISE_APPLICATION_ERROR(-20001, p_Department | | ‘ ‘ | |
p_Course | | ‘no existe!’);
END Register;

Resultados del llamado al procedimiento Register


6. 72

G. Referencias a funciones de reporte de errores


Las funciones SQLCODE y SQLERRM:
• Proveen información acerca de los errores actualmente
manejados.
• Son especialmente útiles en el manejador OTHERS.

SQLCODE:
• Retorna el número de error ORACLE de la excepción,
o 1 si fue una excepción definida por el usuario.

SQLERRM:
• Retorna el mensaje de error ORACLE asociado con el
valor actual de SQLCODE.
• También puede usar cualquier número de error
ORACLE, como un argumento.

Debe tenerse en cuenta:


• Si no existe una excepción activa...
SQLCODE = 0
SQLERRM = normal, succesfull completion.
• SQLCODE y SQLERRM no pueden ser usados dentro
de una sentencia SQL.
6. 73

Ejemplo:

DECLARE
sqlcode_val NUMBER;
sqlerrm_val CHAR(55);
BEGIN
...
EXCEPTION
WHEN OTHERS THEN
sqlcode_val := SQLCODE;
-- No se puede insertar directamente SQLCODE.

sqlerrm_val := SUBSTR(SQLERRM, 1,55);


-- No se puede insertar directamente SQLERRM.
-- También, SUBSTR debe ser usado para asegurarse de
-- que el string retornado cabe dentro de la variable objetivo.

INSERT INTO temp (col1, message)


VALUES (sqlcode_val, sqlerrm_val);
END;
6. 74

6.13 Subprogramas Locales: Procedure y Functions

6.13.1 Conceptos

Los subprogramas:

• Proveen extensibilidad

• Proveen modularidad

• Promueven la reusabilidad

• Promueven la mantenibilidad

• Ayudan a la abstracción
6. 75

DECLARE
...
PROCEDURE . . . IS
BEGIN

[EXCEPTION]

END;
...

BEGIN
...
Procedure_name . . . ;
...

Un subprograma local dentro de un bloque PL/SQL


6. 76

6.13.2 Procedimientos

Sección de declaración, sección ejecutable y sección


de manejo de excepciones de un procedimiento.

PROCEDURE ... IS

BEGIN

[EXCEPTION]

END;
6. 77

Sintaxis Cuerpo del procedimiento:

PROCEDURE nombre_procedimiento [ (parámetro [, parámetro, . . .] ) ] IS


[declaraciones locales]
BEGIN
Sentencias ejecutables
[EXCEPTION
manejadores de excepciones]
END [nombre_procedimiento];

donde: parámetro representa lo siguiente:


nombre_parametro modo tipo de dato [: = value]

Sintaxis Llamado a un procedimiento:

nombre_procedimiento [ ( nombre_parametro [, nombre_parametro, . . . ] ) ]

Los procedimientos PL/SQL son específicos a ORACLE7 y


ORACLE 8.

Notación posicional y por nombre


6. 78

Dadas las siguientes declaraciones:


DECLARE
acct INTEGER;
amt REAL;
PROCEDURE credit (acctno INTEGER, amount REAL) IS . . .
...
Se puede llamar al procedimiento credit de cuatro maneras
lógicamente equivalentes:
BEGIN
credit(acct, ammt); -- notación posicional
credit(amount => amt, acctno => acct); -- notación por nombre
credit(acctno => acct, amount => amt); -- notación por nombre
credit(acct, amount => amt); -- notación mezclada
...
END;

Paso de información entre procedimientos usando parámetros


Sintaxis del uso de parámetros:

PROCEDURE nombre_procedimiento (nombre_parametros


modo tipo de dato [:= value])
6. 79

donde: modo puede ser IN , OUT, IN OUT.

IN: Permite el traspaso de valores a un


procedimiento. Actúa como una
constante y no se le puede asignar un
valor en el procedimiento. IN es el
modo por defecto.

OUT: Permite retornar valores hacia el que


llama al procedimiento. Actúa como
una variable sin inicializar y no puede
ser asignada a otra variable o reasignada
a ella misma en el procedimiento.

IN OUT:Permite el traspaso de valores iniciales a


un procedimiento y retorna valores
actualizados al llamador. Actúa como
una variable inicializada.
6. 80

Ejemplo: Llamada a un procedimiento con parámetros IN

raise_salary (emp_num, amount);

Cuerpo del procedimiento con parámetros IN

PROCEDURE raise_salary (emp_id IN INTEGER, increase IN REAL) IS


current_salary REAL;
salary_missing EXCEPTION;
BEGIN
SELECT sal INTO current_salary FROM emp
WHERE empno = emp_id;
IF current_salary IS NULL THEN
RAISE salary_missing;
ELSE
UPDATE emp SET sal = sal + increase
WHERE empno = emp_id;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO emp_audit VALUES (emp_id, ‘No such number’);
WHEN salary_missing THEN
INSERT INTO emp_audit VALUES (emp_id, ‘salary is null’);
END raise_salary;
6. 81

Ejemplo: Llamado a un procedimiento con un parámetro IN


y un parámetro OUT

calc_bonus(employee_num, bonus_amt);

Cuerpo del procedimiento con los parámetros IN y OUT

PROCEDURE calc_bonus (emp_id IN INTEGER, bonus OUT REAL) IS


hire_date DATE;
comission NUMBER (7,2);
BEGIN
SELECT hiredate, comm into hire_date, commision FROM emp
WHERE empno = emp_id;
IF MONTHS_BETWEEN (SYSDATE, hire_date) > 60 THEN
bonus := comission + 500;
END IF;
END calc_bonus;
6. 82

Ejemplo: Llamada a un procedimiento con un parámetro IN


y uno IN OUT

debit_account (account_num, purchase);

Cuerpo del procedimiento con los parámetros IN e IN OUT

PROCEDURE debit_account (acct_id IN INTEGER,


amount IN OUT REAL) IS
minimum_purchase CONSTANT REAL := 10.5;
service_charge CONSTANT REAL := 0.50;
BEGIN
IF amount < minimum_purchase THEN
amount := amount + service_charge;
END IF:
...
END debit_account;
6. 83

Valores por defecto de los parámetros

Para el siguiente procedimiento:

PROCEDURE create_dept
(new_dname CHAR DEFAULT ‘TEMP’,
new_loc CHAR DEFAULT ‘TEMP’) IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
END create_dept;

Considerar los siguientes llamados a create_dept:

BEGIN
...
create_dept;
create_dept(‘MARKETING’);
create_dept(‘MARKETING’, ‘NEW YORK’);
...
END;
6. 84

Sobrecarga (OVERLOADING)
PL/SQL permite la sobrecarga de los nombres de los subprogramas.

Supongamos que se quiere inicializar las primeras n filas en


dos tablas PL/SQL que fueron declaradas como sigue:
DECLARE
TYPE DateTabTyp IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
TYPE RealTabTyp IS TABLE OF REAL
INDEX BY BINARY_INTEGER;
hiredate_tab DateTabTyp;
sal_tab RealTabTyp;
...

Se podría escribir el siguiente procedimiento para inicializar


la tabla PL/SQL llamada hiredate_tab.

PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS


BEGIN
FOR i IN 1..n LOOP
tab(i) := SYSDATE;
END LOOP;
END initialize;
6. 85

Se puede escribir el siguiente procedimiento para inicializar


la tabla PL/SQL llamada sal_tab.

PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS


BEGIN
FOR i IN 1..n LOOP
tab(i) := 0.0;
END LOOP;
END initialize;

Ejemplo:
DECLARE
TYPE DateTabTyp IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
TYPE RealTabTyp IS TABLE OF REAL
INDEX BY BINARY_INTEGER;
hiredate_tab DateTabTyp;
comm_tab RealTabTyp;
indx BINARY_INTEGER;
...
BEGIN
indx := 50;
initialize(hiredate_tab, indx); -- llama a la primera versión
initialize(comm_tab, indx); -- llama a la segunda versión
END;
6. 86

6.13.3 Funciones
Una función PL/SQL es un bloque con nombre, que se puede
llamar para que ejecute una acción especifica y retorne un
valor.

Esqueleto de una función


FUNCTION ... IS

BEGIN

[EXCEPTION]

END;
6. 87

Sintaxis Cuerpo de la función:


FUNCTION nombre_funcion [(parametro [, parametro, ...]) ]
RETURN datatype IS
[declaraciones locales]
BEGIN
Sentencias ejecutables
[EXCEPTION
manejadores de excepciones]
END [nombre_funcion];

donde: parametro representa lo siguiente:


nombre_parametro modo tipo de dato [: = value]

Sintaxis Llamado a una función como parte de una expresión:

. . . nombre_funcion [ (nombre_parametro [, nombre_parametro, ...] ) ] ...

Debe considerarse:
• Las funciones son específicas de ORACLE7 y ORACLE 8.
• En la versión 1.0 de PL/SQL, una función sólo puede tomar
parámetros IN.
• Una función debe ser llamada como parte de una expresión.
• Los llamados a funciones definidas por el usuario, pueden
aparecer en sentencias procedurales, pero no en sentencias SQL.
6. 88

Ejemplo: Llamado a una función como parte de una expresión

IF sal_ok (new_sal, level) THEN


...
END IF;

promotable := sal_ok (new_sal, level) AND (rating > 3);

Ejemplo: Cuerpo de función


FUNCTION sal_ok (salary IN REAL, level IN CHAR)
RETURN BOOLEAN IS
min_sal REAL;
max_sal REAL;
BEGIN
SELECT losal, hisal INTO min_sal, max_sal FROM salgrade
WHERE grade = level;
RETURN (salary >= min_sal) AND (salary <= max_sal);
END sal_ok;
6. 89

6.13.4 Subprogramas recursivos


Ejemplo: Calcular el factorial de un número.
FUNCTION fac (n POSITIVE) RETURN INTEGER IS -- retorna n!
BEGIN
IF n = 1 THEN -- término de condición
RETURN 1;
ELSE
RETURN n * fac(n – 1); --llamado recursivo
END IF;
END fac;

Ejemplo:
FUNCTION odd (n NATURAL) RETURN BOOLEAN;
--declaración está más adelante

FUNCTION even (n NATURAL) RETURN BOOLEAN IS


BEGIN
IF n = 0 THEN
RETURN TRUE;
ELSE
RETURN odd(n – 1); --llamado mutuamente recursivo
END IF;
END even;
6. 90

FUNCTION odd (n NATURAL) RETURN BOOLEAN IS


BEGIN
IF n = 0 THEN
RETURN FALSE;
ELSE
RETURN even(n – 1); -- llamado mutuamente recursivo
END IF;
END odd;

6.14 Subprogramas almacenados


6.14.1 Conceptos

SQL

Servidor Cliente
Lógica de Interfaz
Relational Aplicación
BD DBMS Usuario

Data

Cliente/Servidor estándar (SQL)


6. 91

Calls

Servidor Cliente
RDBMS
Presentación Interfaz
Lógica de
BD Aplicación Usuario

Data

Cliente/Servidor Transaccional (RPC)

Servidor Cliente

D Servicio L Form
BD
B O
SQL M RPC G
Servicio Form
S I
C
Servicio A Form

Servicios disponibles a Clientes


6. 92

El servidor ORACLE es un importante cliente PL/SQL. Si se


tiene la extensión procedural de bases de datos (Procedural
Database Extension), los subprogramas pueden ser
compilados en forma separada y almacenados
permanentemente en una base de datos ORACLE, listos para
ser ejecutados.

6.14.2 Ventajas de los subprogramas almacenados


Los subprogramas almacenados ofrecen:
• Alta productividad
• Mejor desempeño
• Ahorro de memoria
• Integridad de la aplicación
• Alta seguridad

6.14.3 Llamado a subprogramas almacenados


Se puede llamar a los subprogramas almacenados desde un
trigger de base de datos, desde otro subprograma almacenado,
desde una aplicación del precompilador ORACLE, desde una
aplicación OCI o desde una herramienta ORACLE, como
SQL*Plus.
6. 93

6.14.4 Funciones

Sintaxis:

Comando CREATE FUNCTION ::=

CREATE FUNCTION function

OR REPLACE schema.

( argument datatype )
IN
OUT

IN OUT

RETURN datatype IS cuerpo_subprograma_PL/SQL

AS
6. 94

Ejemplo:
CREATE FUNCTION get_bal(acc_no IN NUMBER)
RETURN NUMBER
IS
acc_bal NUMBER(11,2);
BEGIN
SELECT balance
INTO acc_bal
FROM accounts
WHERE account_id = acc_no;
RETURN(acc_bal);
END;

6.14.5 Procedimientos
Sintaxis:
Comando CREATE PROCEDURE ::=

CREATE PROCEDURE procedure

OR REPLACE schema.

( argument datatype )

IN
OUT
IN OUT

IS cuerpo_subprograma_PL/SQL

AS
6. 95

Ejemplo:
CREATE PROCEDURE sam.credit (acc_no IN NUMBER,
amount IN NUMBER)
AS BEGIN
UPDATE accounts
SET balance = balance + amount
WHERE account_id = acc_no;
END;

6.14.6 Subprogramas locales versus Subprogramas


almacenados

Si se desarrolla un subprograma útil, entonces se tendrá una


alta posibilidad de que éste sea llamado por más de un
bloque. De acuerdo a esto, el subprograma debería ser
almacenado en la base de datos.

Los únicos procedimientos y funciones que podrían


declararse locales a un bloque, deberían tender a ser cortos, y
sólo ser llamados por una sección específica del programa (el
bloque que los contiene).
6. 96

6.14.7 Subprogramas almacenados y el diccionario de


datos

Subprograma: Código fuente


Código-p (forma compilada)

La información acerca de los subprogramas es accesible a


través de varias vistas del diccionario de datos:

• user_objects
• user_source
• user_errors

Sea el siguiente procedimiento simple:

CREATE OR REPLACE PROCEDURE Simple AS


v_Counter NUMBER;
BEGIN
v_Counter := 7;
END Simple;
6. 97

Vistas user_objecs, user_source y user_errors (sin errores)

Si se cambia el código anterior, tal que exista un error (notar


que falta el punto y coma):

CREATE OR REPLACE PROCEDURE Simple AS


v_Counter NUMBER;
BEGIN
v_Counter := 7
END Simple;
6. 98

Vistas user_objects, user_source y user_errors (con errores)

:= 7

En SQL*Plus, el comando SHOW ERRORS consultará


user_errors y formateará la salida para que sea legible.

Se puede usar SHOW ERRORS después de recibir el mensaje


“Warning: Procedure created with compilation errors”.
6. 99

6.14.8 Dependencias en los subprogramas

Cuando un procedimiento o una función es compilada, todos


los objetos ORACLE a los que hace referencia son
registrados en el diccionario de datos. El procedimiento es
dependiente de estos objetos.

Dependencias de RecordFullClases

La función AlmostFull
depende directamente
de classes.
AlmostFull
classes

El procedimiento
RecordFullClasses
depende directamente de
temp_table y AlmostFull e
indirectamente de classes.

temp_table RecordFullClasses
6. 100

Invalidación como resultado de una operación DDL


6. 101

6.14.9 Paquetes

A. Formato General ( Pseudocodigo):

PACKAGE <nombre_paquete> IS
/*
** ESPECIFICACION DEL PAQUETE
** Declaraciones públicas de tipos de datos, variables,
constantes, excepciones y cursores.
** Especificaciones públicas de procedimientos y funciones
(declaraciones “forward”)
*/
END [nombre_paquete];

PACKAGE BODY <nombre_package> IS


/*
** CUERPO DEL PAQUETE
** Declaraciones privadas de tipos de datos, variables,
constantes excepciones y cursores.
** Cuerpos de procedimientos y funciones públicos y privados.
*/
[BEGIN
-- sentencias de inicialización]
END [nombre_paquete];

donde:
nombre_paquete, es un nombre único dentro del
alcance de un esquema de base de datos
6. 102

B. Comando CREATE PACKAGE

Sintaxis:

Comando CREATE PACKAGE::=

CREATE PACKAGE package

OR REPLACE schema.

IS pl/sql_package_spec

AS

Un paquete es una colección encapsulada de:


• Procedimientos
• Funciones
• Variables
• Constantes
• Cursores
• Excepciones
6. 103

Ejemplo:
CREATE PACKAGE emp_mgmt AS
FUNCTION hire (ename VARCHAR2, job VARCHAR2,
mgr NUMBER, sal NUMBER, comm NUMBER,
deptno NUMBER)
RETURN NUMBER;

FUNCTION create_dept (dname VARCHAR2, loc VARCHAR2)


RETURN NUMBER;

PROCEDURE remove_emp (empno NUMBER);

PROCEDURE remove_dept (deptno NUMBER);

PROCEDURE increase_sal(empno NUMBER, sal_incr NUMBER)

PROCEDURE increase_comm (empno NUMBER,


comm_incr NUMBER);
no_comm EXCEPTION;
no_sal EXCEPTION;
END emp_mgmt
6. 104

C. Comando CREATE PACKAGE BODY

Sintaxis:

Comando CREATE PACKAGE BODY ::=

CREATE PACKAGE BODY package

OR REPLACE schema.

IS pl/sql_package_body

AS
6. 105

Ejemplo:
CREATE PACKAGE BODY emp_mgmt AS
tot_emps NUMBER;
tot_depts NUMBER;

FUNCTION hire (ename VARCHAR2, job VARCHAR2,


mgr NUMBER, sal NUMBER, deptno NUMBER)
RETURN NUMBER IS
new_empno NUMBER(4);
BEGIN
SELECT empseq.NEXTVAL
INTO new_empno
FROM DUAL;
INSERT INTO emp
VALUES (new_empno, ename, job, mgr,
sal, comm, deptno)
tot_emps := tot_emps + 1;
RETURN (new_empno);
END;
6. 106

FUNCTION create_dept (dname VARCHAR2, loc VARCHAR2)


RETURN NUMBER IS
new_deptno NUMBER(4);
BEGIN
SELECT deptseq.NEXTVAL
INTO new_deptno
FROM dual;
INSERT INTO dept
VALUES (new_deptno, dname, loc);
tot_depts := tot_depts + 1;
RETURN (new_deptno);
END;

PROCEDURE remove_emp(empno NUMBER) IS


BEGIN
DELETE FROM emp
WHERE emp.empno = remove_emp.empno;
tot_emps := tot_emps – 1;
END;
6. 107

PROCEDURE remove_dept(deptno NUMBER) IS


BEGIN
DELETE FROM dept
WHERE dept.deptno = remove_dept.deptno;
tot_depts := tot_depts – 1;
SELECT COUNT(*)
INTO tot_emps
FROM emp;
/* En caso de que ORACLE elimine
empleados desde la tabla EMP, para asegurar
las restricciones de integridad referencial, se
resetea el valor de la variable TOT_EMPS al
número total de empleados en la tabla EMP.*/
END;
6. 108

PROCEDURE increase_sal (empno NUMBER, sal_incr NUMBER) IS


curr_sal NUMBER(7,2);
BEGIN
SELECT sal
INTO curr_sal
FROM emp
WHERE emp.empno = increase_sal.empno;
IF curr_sal IS NULL
THEN RAISE no_sal;
ELSE UPDATE emp
SET sal = sal + sal_incr
WHERE emp.empno = increase_sal.empno;
END IF;
END;

PROCEDURE increase_comm (empno NUMBER,


comm_incr NUMBER) IS
curr_comm NUMBER(7,2);
BEGIN
SELECT comm
INTO curr_comm
FROM emp
WHERE emp.empno = increase_comm.empno;
IF curr_comm IS NULL
THEN RAISE no_comm;
ELSE UPDATE emp
SET comm = comm + comm_incr
WHERE emp.empno = increase_comm.empno;
END;
END emp_mgmt;
6. 109

D. Referencias al contenido del paquete


Los contenidos públicos del paquete pueden ser
referenciados, usando notación de punto.
Ejemplo:
• nombre_paquete.nombre_tipo_dato
o
• nombre_paquete.nombre_variable
o
• nombre_paquete.nombre_procedimiento(. . . );
o
• nombre_paquete.nombre_funcion(. . .);

6.15 Triggers (Disparadores)


A. Conceptos
Es un procedimiento que el sistema ejecuta automáticamente,
como un efecto secundario de una modificación de la base de
datos.

Para diseñar un mecanismo de disparador se deben:


• Especificar las condiciones bajo las cuales se va a
ejecutar el disparador.
• Especificar las acciones que se van a tomar cuando se
ejecute el disparador.
6. 110

El SQL estándar no incluye disparadores; existen diversas


versiones de SQL, con sus propias características de
disparadores no estándar.

Los triggers son usados a menudo, para iniciar procesos de


negocio secundarios. Por ejemplo, pueden ser usados para
comenzar las siguientes operaciones:

• Verificar la integridad de los datos en inserciones o


actualizaciones.
• Implementar eliminaciones en cascada.
• Transparentemente efectuar eventos de bitácora (log)
• Asegurar el cumplimiento de complejas reglas de
negocios.
• Iniciar procesos de negocios.
• Derivar valores de columnas automáticamente.
• Forzar complejas reglas de seguridad.
• Mantención de datos replicados.
6. 111

Ejemplo:
Trigger REORDER

Sentencia de triggering
AFTER UPDATE OF parts_on_hand ON inventory

Restricción del trigger


WHEN (new.parts_on_hand < new.reorder_point)

Acción del trigger


FOR EACH ROW
DECLARE /* una variable dummy para contar*/
NUMBER x;
BEGIN
SELECT COUNT(*) INTO x /*consulta para encontrar si las partes
han sido*/
FROM pending_orders /* reordenadas – si es sí, x=1, si es no x=0 */
WHERE part_no=:new.part_no;
IF x=0
THEN /* partes que no han sido pedidas de nuevo,*/
INSERT INTO pending_orders /* se piden de nuevo */
VALUES (:new.part_no, :new.reorder_quantity,sysdate)
ENDIF; /* la parte ya ha sido pedida*/
END;
6. 112

SINTAXIS:

CREATE [OR REPLACE] TRIGGER <nombre_trigger>


{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF nombre_columna1
[, nombre_column2] . . . ] }
[OR {DELETE | INSERT | UPDATE [OF columna
[, columna] . . . ]}] . . .
ON <nombre_tabla>
[ [ REFERENCING {OLD [AS] old [NEW [AS] new]
| NEW [AS] new [OLD [AS] old] } ]
[FOR EACH ROW
[WHEN (condición) ] ]
[DECLARE]
...
BEGIN
...
[EXCEPTION
...]
END;
6. 113

donde:
nombre_trigger: es el nombre del trigger; debe ser
único dentro del esquema de la base de datos.
nombre_tabla: es el nombre de la tabla a la cual se
asocia el trigger.

B. Opciones de los triggers de base de datos


Cada trigger tiene tres opciones:
• La primera opción, que considera 2 casos, especifica
cuando es iniciado el trigger: BEFORE (antes) ó AFTER
(después) de una operación específica sobre la tabla.
• La segunda opción, que considera 3 casos, especifica la
operación que activa el trigger: INSERT, UPDATE o
DELETE. Estas operaciones pueden ser especificadas
individualmente, o en cualquier combinación
incluyéndolas todas a la vez. Por ejemplo:
INSERT
INSERT OR DELETE
DELETE OR UPDATE
INSERT OR UPDATE
DELETE OR UPDATE OR INSERT
6. 114

• La tercera opción, que considera 2 casos, especifica si el


trigger será ejecutado sólo una vez por cada operación
sobre la tabla (default), o una vez para cada fila afectada
por la operación que activa el trigger (FOR EACH ROW).
En este último caso, se tiene un trigger de fila.

Opciones de los triggers de base de datos


Before o After Insert, Update o Delete For Each Row
BEFORE INSERT
BEFORE UPDATE
BEFORE DELETE
BEFORE INSERT FOR EACH ROW
BEFORE UPDATE FOR EACH ROW
BEFORE DELETE FOR EACH ROW
AFTER INSERT
AFTER UPDATE
AFTER DELETE
AFTER INSERT FOR EACH ROW
AFTER UPDATE FOR EACH ROW
AFTER DELETE FOR EACH ROW
6. 115

C. Orden de disparo de los triggers


El algoritmo para ejecutar una sentencia DML es el siguiente:
1. Ejecutar el trigger before a nivel de sentencia, si está
presente.
2. Por cada fila afectada por la sentencia:
a) Ejecutar el trigger before a nivel de fila, si está
presente.
b) Ejecutar la sentencia en sí.
c) Ejecutar el trigger after a nivel de fila, si está
presente.
3. Ejecutar el trigger after a nivel de sentencia, si está
presente.

Opción WHEN
• Opcionalmente, una condición booleana puede ser
especificada para un trigger de fila.
• Si se especifica, la condición WHEN es evaluada para cada
fila afectada por la operación de la tabla.
• Si la condición del WHEN se evalúa a TRUE para una fila
afectada por la operación sobre la tabla, el trigger es
disparado para esa fila.
6. 116

• Sin embargo, si la condición WHEN se evalúa FALSE o


null para una fila afectada por la operación sobre la tabla,
el trigger no se dispara para esa fila.

Opciones condicionales
Debido a que un trigger de base de datos puede ser creado
para múltiples operaciones de una tabla (por ejemplo,
“INSERT OR DELETE OR UPDATE OF...”), las opciones
condicionales o predicados pueden ser usados en el cuerpo
del trigger para distinguir entre que operación ha causado el
disparo del trigger.

Estos predicados condicionales son INSERTING,


DELETING y UPDATING.

Ellos son usados para ejecutar secciones especificas del


cuerpo de un trigger de base de datos, según la operación que
disparó el trigger de base de datos.

Por ejemplo:
INSERT OR UPDATE ON item . . .
6. 117

Dentro del cuerpo del trigger, podrían ser especificadas las


siguientes condiciones:
• IF INSERTING THEN
...
END IF;
• IF UPDATING THEN
...
END IF;

Ejemplo:

CREATE TRIGGER . . .
. . .UPDATE OF item_cost, item_name ON item. . .
BEGIN
IF UPDATING (‘ITEM_COST’) THEN
...
END IF;
END;
6. 118

D. Pseudoregistros :old y :new


Un trigger a nivel de fila se dispara una vez por cada fila
procesada. Dentro del trigger, se puede accesar la fila que
está siendo actualmente procesada, a través de los
pseudoregistros: old y :new

El tipo de ambos es:


triggering_table%ROWTYPE;
donde triggering_table es la tabla para la cual se define el trigger.

:old y :new
Sentencias :old :new
del Trigger
INSERT Indefinido. Todos los campos Valores que serán insertados
son nulos. cuando la sentencia está
completa.
UPDATE Valores originales para la fila Nuevos valores que serán
antes de la actualización. actualizado cuando la
sentencia está completa.
DELETE Valores originales antes de Indefinido. Todos los
que la fila sea eliminada. campos son nulos.
6. 119

E. Los triggers y el diccionario de datos


Cuando se crea un trigger, su código fuente es almacenado en
la vista del diccionario de datos user_triggers.

Ejemplo:
SQL> SELECT trigger_type, table_name, triggering_event
2 FROM user_triggers
3 WHERE trigger_name = ‘UPDATEMAJORSTATS’;

TRIGGER_TYPE TABLE_NAME TRIGERRING_EVENT


AFTER STUDENTS INSERT OR UPDATE
STATEMENT OR DELETE

F. Borrado y deshabilitación de los triggers


SQL> DROP TRIGGER nombretrigger

SQL> ALTER TRIGGER UpdateMajorStats DISABLE


Trigger altered

SQL> ALTER TRIGGER UpdateMajorStats ENABLE;


Trigger altered
6. 120

SQL> ALTER TABLE students


2 ENABLE ALL TRIGGERS;
Table altered

SQL> ALTER TABLE students


2 DISABLE ALL TRIGGERS;
Table altered

La columna status de user_triggers contiene ‘ENABLED’ o


‘DISABLED’, que indican el estado actual del trigger.

G. Ejemplo
Este ejemplo crea un trigger de fila BEFORE, llamado
SALARY_CHECK en el esquema SCOTT. Cuando se
agregue un nuevo empleado a la tabla employee o a un
empleado existente se le cambie el salario o el trabajo, este
trigger garantiza que el salario del empleado cae dentro del
rango de salario establecido para los empleados de ese
trabajo.
6. 121

CREATE TRIGGER scott.salary_check


BEFORE
INSERT OR UPDATE OF sal, job
ON scott.emp
FOR EACH ROW
WHEN (new.job <> ‘PRESIDENT’)
DECLARE
minsal NUMBER;
maxsal NUMBER;
BEGIN
/*Obtiene el salario mínimo y el máximo para el */
/*trabajo del empleado desde la tabla SAL_GUIDE */
SELECT minsal, maxsal
INTO minsal, maxsal
FROM sal_guide
WHERE job = :new.job;
/*Si el salario del empleado es menor que el mínimo */
/*o arriba del máximo para ese trabajo, se genera un error */
IF (:new.sal < minsal OR :new.sal > maxsal)
THEN raise_application_error (-20601, ‘Salario’
|| :new.sal || ‘fuera del rango para el trabajo’
||:new.job || ‘ para el empleado’ || :new.ename);
END IF;
END;
6. 122

6.16 Objetos en el esquema


• Tables
• Views
• Sequences
• Procedures
• Functions
• Packages
• Synonyms
• Indexes
• Cluster, HashClusters
• Database Links
• Snapshots
• Constraints
• Database Triggers

SABD
B.D.
6. 123

6.17 Ambiente SQL*Plus para ejecutar bloques PL/SQL

Después de entrar al ambiente SQL*Plus, se puede usar


PL/SQL de varias maneras:

• Ingresar y almacenar bloques PL/SQL.


• Ingresar y correr bloques PL/SQL.
• Crear un script conteniendo bloques PL/SQL.
• Cargar y correr un script conteniendo bloques PL/SQL.
• Llamar a un subprograma almacenado.

A. Entrando un bloque anónimo

Cada bloque PL/SQL comienza con la palabra reservada


DECLARE o, si el bloque no tiene parte declarativa, con la
palabra reservada BEGIN. Al entrar la palabra reservada,
SQL*Plus efectúa lo siguiente:
• Limpia el buffer SQL.
• Entra al modo INPUT.
• Ignora los punto y coma (terminador de una sentencia SQL).
6. 124

Se entra el resto del bloque PL/SQL línea por línea. Se


termina con un punto final (.) en una línea, que permite
almacenar el bloque, en el buffer SQL.

Se puede salvar el bloque PL/SQL en un archivo script como sigue:


SQL> SAVE <nombrearchivo>
SQL> SAVE <nombrearchivo> REPLACE

B. Ejecución de un bloque anónimo

Una vez que está almacenado en el buffer SQL, se puede


correr el bloque PL/SQL como sigue:

SQL> RUN
O
SQL> /
6. 125

C. Creación de un script
Ejemplo:
CLEAR BREAKS;
CLEAR COLUMNS;
COLUMN ENAME HEADING NAME;
TTITLE ‘CLERICAL STAFF’;
DECLARE
avg_sal NUMBER(7,2);
BEGIN
SELECT AVG(sal) INTO avg_sal FROM emp;
IF avg_sal < 1500 THEN
UPDATE emp SET sal = sal * 1.05 WHERE job = ‘CLERK’;
END IF;
END;
/
SELECT ENAME, SAL FROM EMP WHERE JOB = ‘CLERK’;

D. Cargando y corriendo un script


SQL> GET <nombrearchivo>
SQL> START <nombrearchivo>

E. Llamando a subprogramas almacenados


SQL> EXECUTE create_dept (‘ADVERTISING’, ‘NEW YORK’);

Este llamado es equivalente al siguiente bloque anónimo PL/SQL:


SQL> BEGIN create_dept (‘ADVERTISING’, ‘NEW YORK’); END;
6. 126

6.18 PL/SQL Dinámico

6.18.1 Introducción

• En general, un lenguaje de programación puede enlazar


variables de 2 maneras: tarde o temprano.
Ligar una variable es el proceso de identificar la
localización del almacenamiento asociada con un
identificador en el programa.
En PL/SQL el ligamiento involucra también el chequeo de
la base de datos para los permisos de acceso al objeto
referenciado.

Un lenguaje que usa sólo ligamiento temprano hace el


ligamiento durante la fase de compilación mientras, que
un lenguaje que usa el ligamiento tarde pospone el
ligamiento hasta el tiempo de ejecución.

Ligamiento temprano significa que la fase de


compilación tomará mucho más tiempo (ya que el
trabajo del ligamiento tiene que ser hecho), pero la
ejecución será más rápida, puesto que el ligamiento ya ha
sido completado.
6. 127

El ligamiento tarde acorta el tiempo de compilación


pero alarga el tiempo de la ejecución.

Una de las decisiones de diseño para PL/SQL fue el uso


del “Early Binding” (Ligamiento temprano).
Esta decisión fue tomada de manera que la ejecución de un
bloque fuera tan rápida como sea posible, porque todos
los objetos de la base de datos ya han sido verificados por
el compilador.
Esto tiene sentido, puesto que los bloques PL/SQL pueden
ser almacenados dentro de la base de datos vía
procedimientos, funciones, paquetes y triggers.
Estos objetos se guardan en forma compilada, de manera
que cuando sean necesitados ellos puedan ser cargados
desde la base de datos a la memoria y luego ejecutados.

• La principal consecuencia de esta decisión es que


PL/SQL puede contener solamente sentencias DML y
de control de transacciones, no sentencias DDL.
6. 128

Para ilustrar esto, considere el hipotético bloque PL/SQL:

BEGIN
CREATE TABLE temp_table (
num_value NUMBER,
char_value CHAR (10));

INSERT INTO temp_table (num_value, char_value)


VALUES (10, ‘Hello’);
END;

Para compilar esto, el identificador temp_table necesita ser


ligado.
Este proceso chequeará para ver si esta tabla existe.
Sin embargo, la tabla no existirá hasta que el bloque esté
en ejecución.
Pero si el bloque no puede ser aún compilado, no hay
forma de que pueda ser ejecutado.
Las sentencias DML y las de control de transacciones, son
las únicas sentencias SQL que no tienen el potencial para
modificar el objeto o permisos de acceso sobre los objetos,
por lo tanto son sentencias SQL legales en PL/SQL.
6. 129

• Con el PL/SQL 2.1 y superior, esta restricción es


levantada, a través del paquete DBMS_SQL.

Este paquete permite crear una sentencia SQL


dinámicamente en tiempo de ejecución, y luego parsearla y
ejecutarla.
Puesto que la sentencia realmente no se crea hasta el
tiempo de ejecución, el compilador de PL/SQL no tiene
que ligar los identificadores en la sentencia, lo cual permite
al bloque compilar.
Por ejemplo, podríamos usar el paquete DBMS_SQL para
ejecutar la sentencia CREATE TABLE en el bloque
anterior.
Sin embargo, la sentencia INSERT fallaría al ser
compilada puesto que la tabla no existirá hasta que el
bloque sea ejecutado.
La solución a este problema es usar DBMS_SQL para
ejecutar la sentencia INSERT también.
6. 130

• El paquete DBMS_SQL implementa el SQL y el PL/SQL


dinámico, llamado desde otros bloques PL/SQL.

• Esencialmente, DBMS_SQL externaliza el proceso normal


de ejecutar SQL y PL/SQL, y lo pone bajo nuestro control.

• Debido a que el proceso completo está bajo el control del


programa, se puede utilizar DBMS_SQL, tanto para
sentencias DDL como DML.

• La versión 8.1 de PL/SQL para el servidor ORACLE 8i


proporciona capacidades de SQL dinámico, directamente
en el lenguaje PL/SQL, utilizando una nueva sintaxis y
extensiones de la sintaxis existente.
6. 131
Con la versión antigua de PL/SQL

CREATE PROCEDURE insert_into_table (


table_name VARCHAR2, deptnumber NUMBER,
deptname VARCHAR2, location VARCHAR2) IS
cur_hdl INTEGER;
stmt_str VARCHAR2(200);
rows_processed BINARY_INTEGER;
BEGIN
stmt_str := '
INSERT INTO '|| table_name || '
VALUES (:deptno, :dname, :loc)'
;
-- open cursor
cur_hdl := dbms_sql.open_cursor;
-- parse cursor
dbms_sql.parse(cur_hdl, stmt_str, dbms_sql.native);
-- supply binds
dbms_sql.bind_variable (cur_hdl, '
:deptno'
, deptnumber);
dbms_sql.bind_variable (cur_hdl, '
:dname'
, deptname);
dbms_sql.bind_variable (cur_hdl, '
:loc'
, location);
-- execute cursor
rows_processed := dbms_sql.execute(cur_hdl);
-- close cursor
dbms_sql.close_cursor(cur_hdl);
END;
6. 132

Ahora con la
versión nueva de
PL/SQL

CREATE PROCEDURE insert_into_table (

table_name VARCHAR2,
deptnumber NUMBER,
deptname VARCHAR2,
location VARCHAR2) IS

stmt_str VARCHAR2(200);

BEGIN

stmt_str := ’INSERT INTO ’ ||


table_name || ’ values
(:deptno, :dname, :loc)’;

EXECUTE IMMEDIATE stmt_str USING


deptnumber, deptname, location;
END;
6. 133

DECLARE
sql_stmt VARCHAR2(200);
plsql_block VARCHAR2(500);
emp_id NUMBER(4) := 7566;
salary NUMBER(7,2);
dept_id NUMBER(2) := 50;
dept_name VARCHAR2(14) := ’PERSONNEL’;
location VARCHAR2(13) := ’DALLAS’;
emp_rec emp%ROWTYPE;

BEGIN
EXECUTE IMMEDIATE ’CREATE TABLE
bonus (id NUMBER, amt NUMBER)’;

sql_stmt := ’INSERT INTO dept VALUES (:1, :2, :3)’;


EXECUTE IMMEDIATE sql_stmt USING
dept_id, dept_name, location;

sql_stmt := ’SELECT * FROM emp WHERE empno = :id’;


EXECUTE IMMEDIATE sql_stmt INTO
emp_rec USING emp_id;
END;
6. 134

DECLARE

TYPE EmpCurTyp IS REF CURSOR;


emp_cv EmpCurTyp;
emp_rec emp%ROWTYPE;
sql_stmt VARCHAR2(200);
my_job VARCHAR2(15) := ’CLERK’;

BEGIN

sql_stmt := ’SELECT * FROM emp WHERE job = :j’;

OPEN emp_cv FOR sql_stmt USING my_job;


LOOP
FETCH emp_cv INTO emp_rec;
EXIT WHEN emp_cv%NOTFOUND;
-- process record
END LOOP;
CLOSE emp_cv;

END;
6. 135

CREATE OR REPLACE PROCEDURE query_invoice (


month VARCHAR2, year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := '
SELECT num, cust, amt FROM
inv_'|| month ||’_’|| year || 'WHERE invnum = :id'
;
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
--process row here
--
END LOOP;
CLOSE c;
END;

You might also like