Está en la página 1de 25

PL/SQL

PL/SQL provee una manera muy cmoda de relacionar los conceptos de bases de datos y
manejarlos mediante ciertas estructuras de control, dentro del contexto de una herramienta
netamente de programacin.

Su utilizacin es dentro del administrador de bases de datos Oracle y sus principales


caractersticas son la posibilidad que brinda de utilizar sentencias SQL para manipular datos en
Oracle y sentencias de control de flujo para organizar esta manipulacin de datos.

Dentro del lenguaje, es posible declarar constantes y variables, definir procedimientos y


funciones y atrapar errores en tiempo de ejecucin. As visto, PL/SQL combina la el poder de la
manipulacin de datos, con SQL, y las facilidades del procesamiento de los mismos, tal como
en los ms modernos lenguajes de programacin.

Conjunto de caracteres de PL/SQL


Las instrucciones del lenguaje deben ser escritas utilizando un grupo de caracteres vlidos.
PL/SQL no es sensible a maysculas o minsculas. El grupo de caracteres incluye los
siguientes:
Letras maysculas y minsculas de la A a la Z
Nmeros del 0 al 9
Los smbolos ( ) + - * / < > = ! ~ ^ ; . @ % , # $ & _ | { } ? [ ]
Tabuladores, espacios y saltos de carro

Operadores aritmticos
La siguiente tabla presenta los operadores aritmticos ms comunes utilizados en PL/SQL.

Operador Significado
+ Suma
* Multiplicacin
** Exponenciacin
- Sustraccin
/ Divisin

Operadores relacionales
La siguiente tabla muestra los operadores relacionales de PL/SQL.

Operador Significado
<> Distinto de
^= Distinto de
> Mayor que
= Distinto de
< Menor que
= Igual

Smbolos especiales
Los siguientes smbolos se emplean para la programacin con PL/SQL. Esta tabla muestra una
lista parcial de los smbolos; incluye los smbolos ms comunes, que debemos conocer.

1
Smbolo Significado Ejemplo
() Separadores de lista and NAME in (Jose, Juan, Pedro)
; Fin de orden procedure_name(arg1, arg2)
. Separador select * from account.table_name
Cadena de caracteres if var1= Jose ...
:= Asignacin reg := reg+1
|| Concatenacin nombre := Juan || Perez
-- Comentario en una lnea -- Esto es un comentario
/* y */ Comentario en varias lneas /* esto es un comentario */

Tipos de Datos y Conversiones


Cada constante y variable posee un tipo de dato el cual especifica su forma de
almacenamiento, restricciones y rango de valores vlidos. Con PL/SQL se proveen diferentes
tipos de datos predefinidos. Veremos cuatro tipos de datos: varchar2, number, date y boolean.

varchar2
Es un tipo de dato alfanumrico de longitud variable. En PL/SQL puede tener hasta una
longitud de 32.767 bytes. La definicin en la seccin declare se termina con un punto y coma (;)
y todas las definiciones se hacen as.

nombre_variable varchar2(longitud);

donde la longitud debe ser un entero positivo. Tambin tenemos la posibilidad de inicializar su
valor, usando la sintaxis:

campo varchar2(10) := VALINICIAL;

number
Este tipo de dato se usa para representar los datos numricos. La declaracin es:

nombre_variable number(precision, escala);

donde precision puede ser de 1 a 38 caracteres y escala representa el nmero de las


posiciones especificadas por precisin que se reservan para los dgitos decimales. El siguiente
ejemplo describe una variable que puede tener como mximo diez dgitos enteros y dos
decimales.

campo number(12, 2);

date
Este tipo de dato se usa para almacenar fechas de longitud fija. La declaracin no tiene
calificadores y se expresa as:

campo date;

De manera predeterminada, Oracle presenta la fecha en el formato DD-MON-YY; por lo que el


9 de septiembre de 2004 sera 09-SEP-04. Cuando se programa con fechas en PL/SQL debe
utilizarse este formato. Es recomendable utilizar TO_CHAR para escribir una fecha en formato
de cadena y TO_DATE para pasar una cadena a fecha.

boolean
Este tipo de datos es un conmutador que puede almacenar los estado true y false.

campo boolean;

2
Uso de %TYPE
El atributo %TYPE define el tipo de una variable utilizando una definicin previa de otra variable
o columna de la base de datos. Ejemplo:

DECLARE
credito REAL(7,2);
debito credito%TYPE;

Tambin se podra declarar una variable siguiendo el tipo de un campo de alguna tabla, como
por ejemplo en:

debito cuenta.debe%TYPE;

La ventaja de esta ltima forma es que no es necesario conocer el tipo de dato del campo
debe de la tabla cuenta, manteniendo la independencia necesaria para proveer ms
flexibilidad y rapidez en la construccin de los programas.

Uso de %ROWTYPE
El atributo %ROWTYPE precisa el tipo de un registro (record) utilizando una definicin previa
de una tabla o vista de la base de datos. Tambin se puede asociar a una variable como del
tipo de la estructura retornada por un cursor. Ejemplo:

DECLARE
emp_rec emp%ROWTYPE;
CURSOR c1 IS SELECT deptno, dname, loc FROM dept;
dept_rec c1%ROWTYPE;

En este ejemplo la variable emp_rec tomar el formato de un registro completo de la tabla emp
y la variable dept_rec se define por una estructura similar a la retornada por el cursor c1.

Estructura de bloque
Los programas PL/SQL se escriben en bloques de cdigo que disponen de secciones
independientes para las declaraciones de variables, el cdigo ejecutable y el tratamiento de
excepciones. Los fragmentos de cdigo PL/SQL se pueden almacenar en la base de datos
como un subprograma con nombre, o bien, codificarse directamente en la ventana de
SQL*Plus como un bloque annimo sin nombre.

Las secciones de un bloque son dos, la seccin de declaracin de variables (declare) y la


seccin ejecutable (exception). La estructura general de un bloque sin nombre es:

DECLARE
...
BEGIN
...
EXCEPTION
END;

3
Ejemplo de bloque sin nombre que procesa pedidos de raquetas de tenis, recupera la cantidad
en stock, si es mayor que 0 actualiza la venta, si no, inserta un mensaje de alerta:

DECLARE
qty_on_hand NUMBER(5);
BEGIN
SELECT quantity INTO qty_on_hand FROM inventory
WHERE product = 'TENNIS RACKET'
FOR UPDATE OF quantity;
IF qty_on_hand > 0 THEN -- check quantity
UPDATE inventory SET quantity = quantity - 1
WHERE product = 'TENNIS RACKET';
INSERT INTO purchase_record
VALUES ('Tennis racket purchased', SYSDATE);
ELSE
INSERT INTO purchase_record
VALUES ('Out of tennis rackets', SYSDATE);
END IF;

COMMIT;
END;
/

Estructuras del lenguaje


Esta seccin muestra como estructurar el flujo de control dentro de un programa PL/SQL.

Control Condicional: Sentencia IF


A menudo es necesario tomar alternativas de accin dependiendo de las circunstancias. La
sentencia IF permite ejecutar una secuencia de acciones condicionalmente. Esto es, si la
secuencia es ejecutada o no depende del valor de la condicin a evaluar. Existen tres modos
para esta instruccin: IF THEN, IF THEN ELSE y IF THEN ELSIF.

IF THEN
Este es el modo ms simple y consiste en asociar una condicin con una secuencia de
sentencias encerradas entre las palabras reservadas THEN y END IF (no ENDIF). Ejemplo:

IF condicin THEN
secuencia_de_sentencias
END IF;

La secuencia de sentencias es ejecutada slo si la condicin es verdadera. Si la condicin es


falsa o nula no realiza nada. Un ejemplo real de su utilizacin es la siguiente:

IF condicin THEN
calcular_bonus (emp_id)
UPDATE sueldos SET pago = pago + bonus WHERE emp_no = emp_id;
END IF;

IF THEN ELSE
Esta segunda modalidad de la sentencia IF tiene una nueva palabra clave: ELSE, seguida por
una secuencia alternativa de acciones:

IF condicin THEN
secuencia_de_sentencias_1
ELSE
secuencia_de_sentencias_2
END IF;

4
La secuencia de sentencias en la clusula ELSE es ejecutada solamente si la condicin es
falsa o nula. Esto implica que la presencia de la clusula ELSE asegura la ejecucin de alguna
de las dos secuencias de sentencias. En el ejemplo siguiente el primer UPDATE es ejecutado
cuando la condicin es verdadera, en el caso que sea falsa o nula se ejecutar el segundo
UPDATE:

IF tipo_trans = CR THEN
UPDATE cuentas SET balance = balance + credito WHERE
ELSE
UPDATE cuentas SET balance = balance debito WHERE
END IF;

Las clusulas THEN y ELSE pueden incluir estamentos IF, tal como lo indica el siguiente
ejemplo:

IF tipo_trans = CR THEN
UPDATE cuentas SET balance = balance + credito WHERE
ELSE
IF nuevo_balance >= minimo_balance THEN
UPDATE cuentas SET balance = balance debito WHERE
ELSE
RAISE fondos_insuficientes;
END IF;
END IF;

IF THEN ELSIF
Algunas veces se requiere seleccionar una accin de una serie de alternativas mutuamente
exclusivas. El tercer modo de la sentencia IF utiliza la clave ELSIF (no ELSEIF) para introducir
condiciones adicionales, como se observa en el ejemplo siguiente:

IF condicin_1 THEN
secuencia_de_sentencias_1
ELSIF condicin_2 THEN
secuencia_de_sentencias_2
ELSE
secuencia_de_sentencias_3
END IF;

Si la primera condicin es falsa o nula, la clusula ELSIF verifica una nueva condicin. Cada
sentencia IF puede poseer un nmero indeterminado de clusulas ELSIF; la palabra clave
ELSE que se encuentra al final es opcional.

Las condiciones son evaluadas una a una desde arriba hacia abajo. Si alguna es verdadera, la
secuencia de sentencias que corresponda ser ejecutada. Si cada una de las condiciones
analizadas resultan ser falsas, la secuencia correspondiente al ELSE ser ejecutada:

BEGIN

IF sueldo > 50000 THEN
bonus : = 1500;
ELSIF sueldo > 35000 THEN
bonus : = 500;
ELSE
bonus : = 100;
END IF;
INSERT INTO sueldos VALUES (emp_id, bonus, ... );
END;

5
Si el valor de sueldo es mayor que 50.000, la primera y segunda condicin son verdaderas, sin
embargo a bonus se le asigna 1500, ya que la segunda condicin jams es verificada. En este
caso slo se verifica la primera condicin para luego pasar el control a la sentencia INSERT.

Controles de Iteracin: Las sentencias LOOP y EXIT


La sentencia LOOP permite ejecutar una secuencia de acciones mltiples veces. Todas ellas
gobernadas por una condicin que regula la ejecucin de la iteracin.

Existen tres modalidades para esta instruccin: LOOP, WHILE LOOP y FOR LOOP.

LOOP
El modo bsico (o infinito) de LOOP encierra una serie de acciones entre las palabras clave
LOOP y END LOOP, como en el siguiente ejemplo:

LOOP
secuencia_de_instrucciones
END LOOP;

Con cada iteracin del ciclo las sentencias son ejecutadas. Para terminar estos ciclos de
ejecucin se utiliza la palabra clave EXIT. Es posible ubicar innumerables EXIT dentro del loop,
obviamente ninguno fuera de l. Existen dos modalidades para utilizar esta sentencia: EXIT y
EXIT WHEN.

EXIT
La clusula EXIT obliga al loop a concluir incondicionalmente. Cuando se encuentra un EXIT
en el cdigo, el loop es completado inmediatamente y pasa el control a la prxima sentencia.

LOOP
IF ranking_credito < 3 THEN

EXIT; --Termina el loop inmediatamente
END IF;
END LOOP;

Es necesario recordar que esta sentencia debe estar dentro del loop. Para completar un bloque
PL/SQL antes de que su final natural sea alcanzado, es posible utilizar la instruccin RETURN.

EXIT WHEN
Esta sentencia permite terminar el loop de manera condicional. Cuando se encuentra un EXIT
la condicin de la clusula WHEN es evaluada. Si la condicin es verdadera el loop es
terminado y el control es pasado a la prxima sentencia. Ejemplo:

LOOP
FECTH c1 INTO
EXIT WHEN c1%NOTFOUND; -- termina el loop si la
-- condicin es verdadera

END LOOP;
CLOSE c1;

Hasta que la condicin no sea verdadera el loop no puede completarse, esto implica que
necesariamente dentro de las sentencias el valor de la condicin debe ir variando. En el
ejemplo anterior si la ejecucin de FETCH retorna una fila la condicin es falsa. Cuando
FETCH falla al retornar una fila, la condicin es verdadera por lo que el loop es completado y el
control es pasado a la sentencia CLOSE.

La sentencia EXIT WHEN reemplaza la utilizacin de un IF. A modo de ejemplo se pueden


comparar los siguientes cdigos:

6
IF count > 100 THEN | EXIT WHEN count > 100;
EXIT; |
END IF; |

Ambos cdigos son equivalentes, pero el EXIT WHEN es ms fcil de leer y de entender.

WHILE - LOOP
Esta sentencia se asocia a una condicin con una secuencia de sentencias encerradas por las
palabras clave LOOP y END LOOP, como sigue:

WHILE condicin LOOP


secuencia_de_sentencias
END LOOP;

Antes de cada iteracin del ciclo se evala la condicin. Si sta es verdadera se ejecuta la
secuencia de sentencias y el control se devuelve al inicio del loop. Si la condicin en falsa o
nula, el ciclo se rompe y el control se transfiere a la prxima instruccin, fuera del loop.

FOR - LOOP
En las instrucciones anteriores el nmero de iteraciones es desconocido, mientras no se evala
la condicin del ciclo. Con una instruccin del tipo FOR-LOOP, la iteracin se efecta un
nmero finito (y conocido) de veces. La sintaxis de esta instruccin es la siguiente:

FOR contador IN [REVERSE] valor_minimo..valor_maximo LOOP


secuencia_de_sentencias
END LOOP;

El contador no necesita ser declarado porque por defecto se crea para el bloque que involucra
el ciclo y luego se destruye.

Por defecto, la iteracin ocurre en forma creciente, es decir, desde el menor valor aportado
hasta el mayor. Sin embargo, si se desea alterar esta condicin por defecto, se debe incluir
explcitamente en la sentencia la palabra REVERSE.

Los lmites de una iteracin pueden ser literales, variables o expresiones, pero que deben
evaluarse como nmeros enteros.

Un contador de loop tiene validez slo dentro del ciclo. No es posible asignar un valor a una
variable contadora de un loop, fuera de l. Ejemplo:

FOR cont IN 1..10 LOOP



END LOOP;
sum := cont + 1 ; -- Esto no est permitido

La sentencia EXIT tambin puede ser utilizada para abortar la ejecucin del loop en forma
prematura. Por ejemplo, en el siguiente trozo de programa la secuencia normal debera
completarse despus de 10 veces de ejecutarse, pero la aparicin de la clusula EXIT podra
hacer que sta termine antes:

FOR j IN 1..10 LOOP


FETCH c1 INTO emprec;
EXIT WHEN c1%NOTFOUND;

END LOOP;

7
Manejo de cursores
Los cursores permiten manejar grupos de datos que se obtienen como resultado de una
consulta SQL que retorna una o ms filas.

PL/SQL utiliza dos tipos de cursores: implcitos y explcitos. Siempre declara un cursor implcito
para cualquier sentencia de manipulacin de datos, incluyendo aquellas que retornan slo una
fila.

Sin embargo, para las consultas que retornan ms de una fila, usted debe declarar un cursor
explcito para ser usado en una instruccin FOR.

No se pueden usar sentencias de control para cursores implcitos, como en el caso de los
cursores explcitos, por lo que no lo veremos.

Los cursores explcitos son aquellos que devuelven cero, una o ms filas, dependiendo de los
criterios con que hayan sido construidos. Un cursor puede ser declarado en la primera seccin
de un programa PL/SQL (declare).

Existen tres comandos para controlar un cursor: OPEN, FETCH y CLOSE. En un principio, el
cursor se inicializa con la instruccin OPEN. Enseguida, se utiliza la instruccin FETCH para
recuperar la primera fila o conjunto de datos. Se puede ejecutar FETCH repetidas veces hasta
que todas las filas hayan sido recuperadas. Cuando la ltima fila ya ha sido procesada, el
cursor se puede liberar con la sentencia CLOSE.

Es posible procesar varias consultas en paralelo, declarando y abriendo mltiples cursores.

Declaracin de Cursores
Los cursores deben ser declarados antes de ser utilizados en otras sentencias. Cuando se
declara un cursor, a ste se le da un nombre y se asocia con una consulta especfica usando la
sintaxis siguiente:

DECLARE
CURSOR nombre_cursor [ (parmetro1 [, parmetro2]) ]
[RETURN tipo_de_retorno] IS sentencia_select ;

Donde tipo_de_retorno debe representar a un registro o una fila en una tabla de la base y los
parmetros siguen la siguiente sintaxis:

nombre_del_parametro [IN] tipo_de_dato [ { := | DEFAULT} expresin ]

Por ejemplo, los cursores c1 y c2 se pueden declarar como sigue:

DECLARE
CURSOR c1 IS SELECT empno, ename, job, sal
FROM emp WHERE sal>1000 ;
CURSOR c2 RETURN dept%ROWTYPE IS SELECT *
FROM dept WHERE deptno = 10 ;

El nombre del cursor (c1 y c2 en el ejemplo) corresponde a un identificador no declarado, no a


un nombre de variable de PL/SQL. No se pueden asignar valores a un nombre de cursor ni
utilizarlos en una expresin.

Un cursor puede recibir parmetros, los cuales deben ser declarados con la clusula IN para
formalizar su incorporacin. Los parmetros pueden ser inicializados con algn valor, pero
estos pueden ser cambiados en cualquier oportunidad.

El alcance de los parmetros es local al cursor, lo que significa que ellos slo pueden ser
referenciados dentro de la consulta especificada en la declaracin del mismo. Estos valores
son utilizados por la query cuando el cursor se abre.

8
Apertura de un Cursor
Al abrir un cursor se ejecuta inmediatamente la consulta e identifica el conjunto resultado, el
que contiene todas las filas que concuerdan con el criterio de seleccin de ste. Para los
cursores que se abren con la clusula For Update, la sentencia de apertura del cursor
adems bloquea esas filas retornadas. Un ejemplo es el siguiente:

DECLARE
CURSOR c1 IS SELECT ename, job FROM emp WHERE sal > 3000;

BEGIN
OPEN c1;

END;

Paso de Parmetros
Se utiliza tambin la sentencia OPEN para pasar los parmetros al cursor, en caso de que ste
los requiera. Por ejemplo:

DECLARE
emp_name emp.name%TYPE;
salary emp.sal%TYPE;
CURSOR c1 (name VARCHAR2, salary NUMBER) IS SELECT

Cualquiera de estas sentencias abre el cursor:

OPEN c1(emp_name, 3000);


OPEN c1(John, 1500);
OPEN c1(emp_name, salary);

Obsrvese que en este ejemplo, cuando se utiliza la variable salary en la declaracin se


refiere al nombre del segundo parmetro del cursor. Sin embargo, cuando es usada en una
sentencia OPEN se refiere a la variable del programa.

Recuperacin de Filas
La sentencia FETCH permite recuperar los conjuntos de datos de uno en uno. Despus de
cada recuperacin y carga de un set de datos el cursor avanza a la fila siguiente. Ejemplo:

FETCH c1 INTO my_empno, my_ename, my_deptno;

Para cada columna retornada en un cursor y especificada en la declaracin del mismo debe
existir una variable compatible en tipo en la lista INTO.

Tpicamente se utiliza la sentencia FETCH dentro de un conjunto de instrucciones como el


siguiente:

LOOP
FETCH c1 INTO mi_registro;
EXIT WHEN c1%NOTFOUND;
--- se procesa el registro
END LOOP;

Eventualmente, la sentencia FETCH fallar, es decir, no retornar ningn conjunto de datos.


Cuando esto sucede, no se lanza una excepcin, por tanto, se debe detectar esta condicin
utilizando los atributos del cursor %FOUND y/o %NOTFOUND.

Algunos atributos de los cursores se detallan a continuacin:

9
Uso de %FOUND
Despus de abrir un cursor, pero antes de recuperar la primera fila el valor del atributo
%FOUND es nulo. A continuacin, tomar el valor TRUE cada vez que obtenga una fila del set
de resultados (en cada FETCH que se haga) y slo alcanzar el valor FALSE cuando ya no
existan ms filas para mostrar en el set de resultados. Ejemplo:

LOOP
FETCH c1 INTO
IF c1%FOUND THEN -- fetch exitoso

ELSE -- fetch fall; se sale del loop
EXIT;
END IF;
END LOOP;

Uso de %NOTFOUND
Es el opuesto lgico de %FOUND. Cada vez que una sentencia FETCH retorne una fila vlida,
este atributo devolver FALSO. Slo alcanzar el valor TRUE cuando no haya ms filas en un
cursor y se ejecute la sentencia FETCH (sin xito por lo tanto). Ejemplo:

LOOP
FETCH c1 INTO
EXIT WHEN c1%NOTFOUND;

END LOOP;

Uso de %ISOPEN
Este atributo toma el valor verdadero (TRUE) cuando un cursor se encuentra abierto. De otra
manera, retorna FALSO.

Uso de %ROWCOUNT
Cuando un cursor es abierto, este atributo es puesto a 0 (cero). En adelante, cada vez que se
recuperen filas exitosamente con un FETCH, este valor se ir incrementando en uno. Por tanto,
no es el nmero total, sino las que llevamos recuperadas hasta el momento.

Cuando se utiliza con cursores implcitos, este atributo devuelve el total de filas afectadas por
una instruccin del tipo INSERT, UPDATE o DELETE.

Cierre de un Cursor
La sentencia que deshabilita un cursor, CLOSE, se utiliza de la siguiente manera:

CLOSE c1;

Una vez que un cursor ya ha sido cerrado, es posible volverlo a abrir sin tener que declararlo
otra vez. Cualquier otra operacin que se desee efectuar sobre un cursor no operativo
(cerrado) provocar una excepcin del tipo invalid_cursor.

Excepciones
En PL/SQL una advertencia o condicin de error es llamada una excepcin. Estas pueden ser
definidas en forma interna (en tiempo de ejecucin de un programa) o explcitamente por el
usuario. Ejemplos de excepciones definidas en forma interna son la divisin por cero y la falta
de memoria en tiempo de ejecucin. Estas mismas condiciones excepcionales tienen sus
nombres propios y pueden ser referenciadas con ellos: zero_divide y storage_error.

Tambin se pueden definir excepciones a medida y nombrarlas como veremos.

10
Cuando ocurre un error se alcanza la excepcin, esto quiere decir que se ejecuta la porcin del
programa donde sta se encuentra implementada, transfirindose el control a ese bloque de
sentencias. Las excepciones definidas por el usuario deben ser alcanzadas explcitamente
utilizando la sentencia raise.

Con las excepciones ser pueden manejar los errores cmodamente sin necesidad de mantener
mltiples chequeos por cada sentencia escrita. Tambin provee claridad en el cdigo desde el
momento en que permite mantener las rutinas correspondientes al tratamiento de los errores
en forma separada de la lgica de la aplicacin.

Excepciones predefinidas
Las excepciones predefinidas no necesitan ser declaradas. Simplemente se utilizan cuando
estas son lanzadas por algn error determinado.

La siguiente es la lista de las excepciones predeterminadas por PL/SQL y una breve


descripcin de cundo son accionadas:

Nombre Excepcin Lanzada cuando SQLCODE

El programa intent asignar valores a los


ACCESS_INTO_NULL -6530
atributos de un objeto no inicializado
El programa intent asignar valores a una tabla
COLLECTION_IS_NULL -6531
anidada an no inicializada
El programa intent abrir un cursor que ya se
encontraba abierto. Recuerde que un cursor de
CURSOR_ALREADY_OPEN -6511
ciclo FOR automticamente lo abre y ello no se
debe especificar con la sentencia OPEN
El programa intent almacenar valores
duplicados en una columna que se mantiene
DUP_VAL_ON_INDEX -1
con restriccin de integridad de un ndice nico
(unique index)
El programa intent efectuar una operacin no
INVALID_CURSOR -1001
vlida sobre un cursor
En una sentencia SQL, la conversin de una
cadena de caracteres hacia un nmero falla
INVALID_NUMBER -1722
cuando esa cadena no representa un nmero
vlido
El programa intent conectarse a Oracle con un
LOGIN_DENIED -1017
nombre de usuario o password invlido
Una sentencia SELECT INTO no devolvi
NO_DATA_FOUND valores o el programa referenci un elemento +100
no inicializado en una tabla indexada
El programa efectu una llamada a Oracle sin
NOT_LOGGED_ON -1012
estar conectado
PROGRAM_ERROR PL/SQL tiene un problema interno -6501
Los elementos de una asignacin (el valor a
asignar y la variable que lo contendr) tienen
ROWTYPE_MISMATCH tipos incompatibles. Tambin se presenta este -6504
error cuando un parmetro pasado a un
subprograma no es del tipo esperado
El parmetro SELF (el primero que es pasado a
SELF_IS_NULL -30625
un mtodo MEMBER) es nulo
STORAGE_ERROR La memoria se termin o est corrupta -6500
El programa est tratando de referenciar un
elemento de un arreglo indexado que se
SUBSCRIPT_BEYOND_COUNT -6533
encuentra en una posicin ms grande que el
nmero real de elementos de la coleccin
El programa est referenciando un elemento de
SUBSCRIPT_OUTSIDE_LIMIT un arreglo utilizando un nmero fuera del rango -6532
permitido (por ejemplo, el elemento -1)

11
La conversin de una cadena de caracteres
SYS_INVALID_ROWID hacia un tipo rowid fall porque la cadena no -1410
representa un nmero
Se excedi el tiempo mximo de espera por un
TIMEOUT_ON_RESOURCE -51
recurso en Oracle
Una sentencia SELECT INTO devuelve ms de
TOO_MANY_ROWS -1422
una fila
Ocurri un error aritmtico, de conversin o
truncamiento. Por ejemplo, sucede cuando se
VALUE_ERROR -6502
intenta calzar un valor muy grande dentro de
una variable ms pequea
El programa intent efectuar una divisin por
ZERO_DIVIDE -1476
cero

Excepciones definidas por el usuario


PL/SQL permite al usuario definir sus propias excepciones, las que debern ser declaradas y
lanzadas explcitamente utilizando otros comandos del lenguaje.

Declaracin

Las excepciones slo pueden ser declaradas en el segmento Declare de un bloque,


subprograma o paquete. Se declara una excepcin escribiendo su nombre seguida de la
palabra clave EXCEPTION. Las declaraciones son similares a las de variables, pero recuerde
que una excepcin es una condicin de error, no un tem de datos. Aun as, las mismas reglas
de alcance aplican tanto sobre variables como sobre las excepciones. Ejemplo:

DECLARE
error_01 EXCEPTION;

Reglas de Alcance

Una excepcin no puede ser declarada dos veces en un mismo bloque. Tal como las variables,
una excepcin declarada en un bloque es local a ese bloque y global a todos los sub-bloques
que comprende.

La sentencia RAISE
La sentencia RAISE permite lanzar una excepcin en forma explcita. Es factible utilizar esta
sentencia en cualquier lugar que se encuentre dentro del alcance de la excepcin. Ejemplo:

DECLARE
out_of_stock EXCEPTION; -- declaracin de la excepcin
total NUMBER(4);
BEGIN

IF total < 1 THEN
RAISE out_of_stock; -- llamada a la excepcin
END IF;
EXCEPTION
WHEN out_of_stock THEN
-- manejar el error aqu
WHEN OTHERS THEN

END;

Finalmente, cabe destacar la existencia de la excepcin OTHERS, que simboliza cualquier


condicin de excepcin que no ha sido declarada. Se utiliza comnmente al final del bloque de
excepciones para absorber cualquier tipo de error que no ha sido previsto por el programador.

12
Subprogramas
Los subprogramas son bloques de instrucciones de PL/SQL que pueden ser invocados por
otros y recibir parmetros. En PL/SQL existen dos tipos de subprogramas: Los Procedimientos
y las Funciones. Por regla general, se utiliza un procedimiento para ejecutar una accin
especfica y una funcin para calcular un valor.

Los subprogramas tambin constan de una seccin de declaraciones, un cuerpo que se


ejecuta y una seccin opcional de manejo de excepciones. Ejemplo:

PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS


old_balance REAL;
new_balance REAL;
overdrown EXCEPTION;
BEGIN
SELECT bal INTO old_balance FROM accts WHERE acct_no = acct_id;
new_balance := old_balance amount;
IF new_balance < 0 THEN
RAISE overdrown;
ELSE
UPDATE accts SET bal = new_balance WHERE acct_no = acct_id;
END IF;
EXCEPTION
WHEN overdrown THEN

END debit_account;

En el ejemplo, cuando el subprograma es invocado, recibe los parmetros acct_id y amount.


Con el primero de ellos selecciona el valor del campo bal y lo almacena en old_balance.
Luego almacena una diferencia en otra variable, new_balance, la que de ser negativa lanzar
una condicin de excepcin definida por el usuario (overdrown).

Procedimientos
Un procedimiento es un subprograma que ejecuta una accin especfica. La sintaxis para
construirlos es la siguiente:

PROCEDURE nombre [ (parmetro [, parmetro, ] ) ] IS


[declaraciones_locales]
BEGIN
sentencias_ejecutables
[EXCEPTION
condiciones_de_excepcin]
END [nombre] ;

Adems, cada parmetro se escribe con la siguiente notacin:

nombre_parmetro [IN | OUT [NOCOPY] | IN OUT [NOCOPY] tipo_de_dato


[ { := | DEFAULT } expresin ]

En el paso de parmetros no se puede precisar el largo de alguno de ellos explcitamente,


como en:
PROCEDURE xxx (param01 CHAR(5)) IS

Esta sentencia es invlida, ya que debera decir slo param01 CHAR, sin especificar el
tamao del carcter.

13
Sin embargo, si es absolutamente necesario restringir el largo de una cadena como la del
ejemplo, se puede corregir la situacin codificando la llamada al procedimiento xxx de la
siguiente manera:

DECLARE
temp CHAR(5);
SUBTYPE Char5 IS temp%TYPE;
PROCEDURE xxx (param01 Char5) IS

Un procedimiento posee dos partes: una especificacin y un cuerpo. La especificacin es


simple, comienza con la palabra PROCEDURE y termina (en la misma lnea) con el nombre del
procedimiento o la lista de parmetros (que es opcional).

El cuerpo del procedimiento comienza con la palabra reservada IS y termina con END,
seguido opcionalmente por el nombre del procedimiento.

Funciones
Una funcin es un subprograma que calcula un valor. La sintaxis para construir funciones es la
siguiente:

FUNCTION nombre [ (parmetro [, parmetro, ] ) ]


RETURN tipo_de_dato IS
BEGIN
sentencias_ejecutables
[EXCEPTION
condiciones_de_excepcin]
END [nombre] ;

Y la sintaxis de los parmetros es idntica al caso de los procedimientos:

nombre_parmetro [IN | OUT [NOCOPY] | IN OUT [NOCOPY] tipo_de_dato


[ { := | DEFAULT } expresin ]

La funcin tambin posee una especificacin y un cuerpo. El segmento de especificacin


comienza con la palabra FUNCTION y termina con la clusula RETURN, la cual especifica el
tipo de dato retornado por la funcin.

El cuerpo comienza con la palabra IS y termina con la palabra END, es decir, incluye las
secciones de declaraciones, sentencias ejecutables y una parte opcional de manejo de
excepciones. Ejemplo:

FUNCTION revisa_salario (salario REAL, cargo CHAR(10))


RETURN BOOLEAN IS
salario_minimo REAL;
salario_maximo REAL;
BEGIN
SELECT lowsal, highsal INTO salario_minimo, salario_maximo
FROM salarios WHERE job = cargo ;
RETURN (salario >= salario_minimo) AND
(salario <= salario_maximo)
END revisa_salario ;

Esta misma funcin de ejemplo puede ser llamada desde una sentencia PL/SQL que reciba un
valor booleano, como por ejemplo, en:

14
DECLARE
renta_actual REAL;
codcargo CHAR(10);
BEGIN

IF revisa_salario (renta_actual, codcargo) THEN

La funcin revisa_salario acta como una variable de tipo booleano, cuyo valor depende de los
parmetros recibidos.

La sentencia RETURN
Esta sentencia termina inmediatamente la ejecucin de un programa, retornando el control al
bloque de programa que lo llam. No se debe confundir con la clusula return de las funciones,
que especifica el tipo de dato devuelto por ella.

Un subprograma puede contener varias sentencias Return. Si se ejecuta cualquiera de ellas, el


subprograma completo se termina.

La sintaxis para los procedimientos es simple, slo se necesita la palabra RETURN. Sin
embargo, en el caso de las funciones, esta sentencia debe contener un valor, que es aquel que
se va a devolver al programa que la llam. La expresin que sigue a la sentencia puede ser tan
compleja como se desee pero siempre debe respetar el tipo de datos que est definido en la
cabecera (especificacin) de la funcin.

Una funcin debe contener como mnimo una sentencia RETURN, de otra manera, al no
encontrarla, PL/SQL generar la excepcin PROGRAM_ERROR.

Creacin de paquetes
Un paquete es un esquema u objeto que agrupa tipos de PL/SQL relacionados, tems y
subprogramas. Los paquetes se constituyen de dos partes: la especificacin y el cuerpo.

La especificacin es la interfaz con las aplicaciones. En ella es posible declarar los tipos,
variables, constantes, excepciones, cursores y subprogramas disponibles para su uso
posterior.

El cuerpo define completamente a cursores y subprogramas e implementa lo que se declar


inicialmente en la especificacin.

Es posible depurar y modificar cuantas veces se desee el cuerpo de un paquete sin necesidad
de alterar por ello la especificacin del mismo.

Ejemplo de creacin de paquetes:

CREATE OR REPLACE PACKAGE emp_actions AS -- Especificacin


TYPE EmpRecTyp IS RECORD (emp_id INTEGER, salary REAL);
CURSOR desc_salary RETURN EmpRecTyp;
PROCEDURE hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER);
PROCEDURE fire_employee(emp_id NUMBER);
END emp_actions;

CREATE OR REPLACE PACKAGE BODY emp_actions AS -- Cuerpo


CURSOR desc_salary RETURN EmpRecTyp IS
SELECT empno, sal FROM emp ORDER BY sal DESC;

15
PROCEDURE hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER) IS
BEGIN
INSERT INTO emp VALUES (empno_seq.NEXTVAL, ename, job, mgr,
SYSDATE, sal, comm, deptno);
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM emp WHERE empno = emp_id;
END fire_employee;
END emp_actions;

16
Ejemplos para probar con SQL*PLUS de PL/SQL
Las tablas con las que vamos a trabajar son:
drop table accounts
/
create table accounts(
account_id number(4) not null,
bal number(11,2))
/
create unique index accounts_index on accounts (account_id)
/
drop table action
/
create table action(
account_id number(4) not null,
oper_type char(1) not null,
new_value number(11,2),
status char(45),
time_tag date not null)
/
drop table bins
/
create table bins(
bin_num number(2) not null,
part_num number(4),
amt_in_bin number(4))
/
drop table data_table
/
create table data_table(
exper_num number(2),
n1 number(5),
n2 number(5),
n3 number(5))
/
drop table emp
/
create table emp(
empno number(4) not null,
ename varchar2(10),
job varchar2(9),
mgr number(4),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno number(2))
/
drop table inventory
/
create table inventory(
prod_id number(5) not null,
product char(15),
quantity number(5))
/
drop table journal
/
create table journal(
account_id number(4) not null,
action char(45) not null,

17
amount number(11,2),
date_tag date not null)
/
drop table num1_tab
/
create table num1_tab(
sequence number(3) not null,
num number(4))
/
drop table num2_tab
/
create table num2_tab(
sequence number(3) not null,
num number(4))
/
drop table purchase_record
/
create table purchase_record(
mesg char(45),
purch_date date)
/
drop table ratio
/
create table ratio(
sample_id number(3) not null,
ratio number)
/
drop table result_table
/
create table result_table(
sample_id number(3) not null,
x number,
y number)
/
drop table sum_tab
/
create table sum_tab(
sequence number(3) not null,
sum number(5))
/
drop table temp
/
create table temp(
num_col1 number(9,4),
num_col2 number(9,4),
char_col char(55))
/

Los datos que se introducen son:


delete from accounts
/
insert into accounts values (1,1000.00)
/
insert into accounts values (2,2000.00)
/
insert into accounts values (3,1500.00)
/
insert into accounts values (4,6500.00)
/
insert into accounts values (5,500.00)

18
/
delete from action
/
insert into action values
(3,'u',599,null,sysdate)
/
insert into action values
(6,'i',20099,null, sysdate)
/
insert into action values
(5,'d',null,null, sysdate)
/
insert into action values
(7,'u',1599,null, sysdate)
/
insert into action values
(1,'i',399,null,sysdate)
/
insert into action values
(9,'d',null,null,sysdate)
/
insert into action values
(10,'x',null,null,sysdate)
/
delete from bins
/
insert into bins values (1, 5469, 650)
/
insert into bins values (2, 7243, 450)
/
insert into bins values (3, 5469, 120)
/
insert into bins values (4, 5469, 300)
/
insert into bins values (5, 6085, 415)
/
insert into bins values (6, 5469, 280)
/
insert into bins values (7, 8159, 619)
/
delete from data_table
/
insert into data_table values
(1, 10, 167, 17)
/
insert into data_table values
(1, 16, 223, 35)
/
insert into data_table values
(2, 34, 547, 2)
/
insert into data_table values
(3, 23, 318, 11)
/
insert into data_table values
(1, 17, 266, 15)
/
insert into data_table values
(1, 20, 117, 9)
/
delete from emp

19
/
insert into emp values
(7369,'SMITH','CLERK',7902,TO_DATE('12-17-00','MM-DD-YY'),
800,NULL,20)
/
insert into emp values
(7499,'ALLEN','SALESMAN',7698,TO_DATE('02-20-01','MM-DD-YY'),
1600,300,30)
/
insert into emp values
(7521,'WARD','SALESMAN',7698,TO_DATE('02-22-01','MM-DD-YY'),
1250,500,30)
/
insert into emp values
(7566,'JONES','MANAGER',7839,TO_DATE('04-02-01','MM-DD-YY'),
2975,NULL,20)
/
insert into emp values
(7654,'MARTIN','SALESMAN',7698,TO_DATE('09-28-01','MM-DD-YY'),
1250,1400,30)
/
insert into emp values
(7698,'BLAKE','MANAGER',7839,TO_DATE('05-1-01','MM-DD-YY'),
2850,NULL,30)
/
insert into emp values
(7782,'CLARK','MANAGER',7839,TO_DATE('06-9-01','MM-DD-YY'),
2450,NULL,10)
/
insert into emp values
(7788,'SCOTT','ANALYST',7566,SYSDATE-5,3000,NULL,20)
/
insert into emp values
(7839,'KING','PRESIDENT',NULL,TO_DATE('11-17-01','MM-DD-YY'),
5000,NULL,10)
/
insert into emp values
(7844,'TURNER','SALESMAN',7698,TO_DATE('09-8-01','MM-DD-YY'),
1500,0,30)
/
insert into emp values
(7876,'ADAMS','CLERK',7788,SYSDATE-1,1100,NULL,20)
/
insert into emp values
(7900,'JAMES','CLERK',7698,TO_DATE('12-3-01','MM-DD-YY'),
950,NULL,30)
/
insert into emp values
(7902,'FORD','ANALYST',7566,TO_DATE('12-3-01','MM-DD-YY'),
3000,NULL,20)
/
insert into emp values
(7934,'MILLER','CLERK',7782,TO_DATE('01-23-02','MM-DD-YY'),
1300,NULL,10)
/
delete from inventory
/
insert into inventory values
(1234, 'TENNIS RACKET', 3)
/
insert into inventory values

20
(8159, 'GOLF CLUB', 4)
/
insert into inventory values
(2741, 'SOCCER BALL', 2)
/
delete from journal
/
delete from num1_tab
/
insert into num1_tab values (1, 5)
/
insert into num1_tab values (2, 7)
/
insert into num1_tab values (3, 4)
/
insert into num1_tab values (4, 9)
/
delete from num2_tab
/
insert into num2_tab values (1, 15)
/
insert into num2_tab values (2, 19)
/
insert into num2_tab values (3, 27)
/
delete from purchase_record
/
delete from ratio
/
delete from result_table
/
insert into result_table values (130, 70, 87)
/
insert into result_table values (131, 77, 194)
/
insert into result_table values (132, 73, 0)
/
insert into result_table values (133, 81, 98)
/
delete from sum_tab
/
delete from temp
/
commit
/

A continuacin, un conjunto de ejemplos de uso de PL/SQL para probar:

1.- En la tabla emp incrementar el salario el 10% a los empleados que tengan una
comisin superior al 5% del salario.

BEGIN
UPDATE EMP
SET SAL = SAL+SAL*(10/100)
WHERE COMM > (SAL*5/100);
END;
/

21
2.- Aadir la columna total2 y en ella escribir la suma del salario y la comisin de los
empleados con comisin distinta de 0.

ALTER TABLE EMP ADD(TOTAL2 NUMBER(7,2));

DECLARE
CURSOR CURSOR2 IS SELECT COMM,SAL FROM EMP
WHERE COMM<>0
FOR UPDATE;
BEGIN
FOR REG IN CURSOR2 LOOP
UPDATE EMP
SET TOTAL2 = SAL+COMM
WHERE CURRENT OF CURSOR2;
END LOOP;
END;
/

ALTER TABLE EMP ADD(TOTAL2 NUMBER(7,2));

DECLARE
comm emp.comm%type;
sal emp.sal%type;
CURSOR CURSOR2 IS SELECT COMM,SAL FROM EMP
WHERE COMM IS NOT NULL AND COMM<>0;
BEGIN
open cursor2;
loop
fetch cursor2 into comm, sal;
exit when cursor2%NOTFOUND;
UPDATE EMP
SET TOTAL2 = SAL+COMM
WHERE comm = emp.comm AND sal = emp.sal;
end loop;
END;
/

Alternativa:

3.- Insertar un empleado en la tabla EMP. Su nmero ser superior a los existentes y la
fecha de incorporacin a la empresa ser la actual.

DECLARE
NUM_EMPLEADO EMP.EMPNO%TYPE;
FECHA EMP.HIREDATE%TYPE;
BEGIN
SELECT MAX(EMPNO) INTO NUM_EMPLEADO FROM EMP;
SELECT SYSDATE INTO FECHA FROM DUAL;
NUM_EMPLEADO:=NUM_EMPLEADO +1;
INSERT INTO EMP
VALUES (NUM_EMPLEADO,'PEDRO','MATEMATIC',7839, 22
FECHA,3000,NULL,20,NULL);
END;
/
4.- Realizar un procedimiento para cambiar la fecha por el nmero de ao.

DECLARE
FECHA VARCHAR2(4);
CURSOR CURSOR1 IS SELECT TO_CHAR(HIREDATE,'YYYY') FROM EMP FOR
UPDATE;
BEGIN
OPEN CURSOR1;
LOOP
FETCH CURSOR1 INTO FECHA;
EXIT WHEN CURSOR1%NOTFOUND;
UPDATE EMP
SET HIREDATE='FECHA' WHERE CURRENT OF CURSOR1;
END LOOP;
CLOSE CURSOR1;
END;
/

5.- Aadir un nuevo empleado en la tabla emp. El nmero de empleado ser el del ltimo
+ 10. La fecha la actual, el departamento el 40.

DECLARE
EMPLEADO EMP.EMPNO%TYPE;
FECHAA EMP.HIREDATE%TYPE;

PROCEDURE EJERCICIO_5(FECHA IN EMP.HIREDATE%TYPE,


NUMERO_EMP IN EMP.EMPNO%TYPE,
DEPARTAMENTO IN EMP.DEPTNO%TYPE) IS
BEGIN
INSERT INTO EMP VALUES
(NUMERO_EMP,'NOMBRE','JOB',NULL,FECHA,999,NULL,DEPARTAMENTO);
END EJERCICIO_5;

FUNCTION TRANSFORMACION1 RETURN NUMBER IS


RETORNO EMP.EMPNO%TYPE;
MAYOR_NUMERO EMP.EMPNO%TYPE;
BEGIN
SELECT MAX(EMPNO) INTO MAYOR_NUMERO FROM EMP;
RETORNO:=MAYOR_NUMERO+10;
RETURN(RETORNO);
END TRANSFORMACION1;

FUNCTION TRANSFORMACION2 RETURN DATE IS


RETORNO EMP.HIREDATE%TYPE;
BEGIN
RETORNO:=SYSDATE;
RETURN(RETORNO);
END TRANSFORMACION2;

BEGIN
EMPLEADO := TRANSFORMACION1;
FECHAA := TRANSFORMACION2;
EJERCICIO_5(FECHAA,EMPLEADO,40);
END;
/

23
6.- Buscar todos los empleados que tienen un salario + comisin superior a 2000 y
asignarles como nuevo salario esta suma. Slo para los que tienen comisin.

DECLARE
PROCEDURE ACTUALIZAR IS
CURSOR C1 IS SELECT ENAME,SAL,COMM FROM EMP
WHERE SAL+COMM>2000 FOR UPDATE;
REGISTRO C1%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO REGISTRO;
EXIT WHEN C1%NOTFOUND;
UPDATE EMP
SET SAL=SAL+COMM WHERE CURRENT OF C1;
END LOOP;
CLOSE C1;
END ACTUALIZAR;
BEGIN
ACTUALIZAR;
END;
/

7.- Crear un procedimiento almacenado que haga una venta, decrementando en 1 la


cantidad de productos disponibles, pero si la cantidad es 0, se introduce un mensaje en
una tabla de registro que indica las ventas que se hacen y que registra si hay falta de
productos.

CREATE OR REPLACE PROCEDURE VENDE(articulo char) IS


cantidad NUMBER(5);
BEGIN
dbms_output.put_line(articulo);
SELECT quantity INTO cantidad FROM inventory
WHERE product = articulo
FOR UPDATE OF quantity;
IF cantidad > 0 THEN
UPDATE inventory SET quantity = quantity - 1
WHERE product = 'TENNIS RACKET';

INSERT INTO purchase_record


VALUES (articulo || ' purchased', SYSDATE);
ELSE
INSERT INTO purchase_record
VALUES ('Out of ' || articulo, SYSDATE);
END IF;

COMMIT;
END;
/

Para ejecutar execute vende(TENNIS RACKET);

24
Disparadores de base de datos

25

También podría gustarte