You are on page 1of 58

I.E.S.

TRASSIERRA Crdoba
http://www.iestrassierra.com Departamento de Informtica

APUNTES DE PL/SQL
Etapa: Ciclo: Nivel: Mdulo: Profesor: Formacin Profesional Especfica. Desarrollo de Aplicaciones Web. Superior. Bases de Datos. Juan Carlos Prez Jorge

INDICE:
Tema 1.- PL/SQL ................................................................................................ 1 Tema 2.- Disparadores (Triggers) ................................................................. 27 Tema 3.- Procedimientos, Funciones y Paquetes .......................................... 38

I.E.S. TRASSIERRA - Crdoba

PL/SQL

TEMA 1.- PL/SQL.1.1.- Introduccin. 1.2.- El bloque PL/SQL. 1.3.- Tipos de datos, conversiones, mbito y visibilidad. 1.4.- Zona de declaraciones: DECLARE. 1.4.1.- Declaracin de variables y constantes. 1.4.2.- Declaracin de registros. 1.4.3.- Declaracin de cursores. 1.4.4.- Declaracin de excepciones. 1.5.- Zona de proceso: BEGIN. 1.5.1.- Sentencias propias de PL/SQL. 1.5.2.- Sentencias DML. 1.5.3.- Sentencias transaccionales. 1.6.- Zona de excepciones: EXCEPTION. 1.6.1.- Control de errores. 1.6.2.- Excepciones predefinidas. 1.6.3.- Excepciones definidas por el usuario 1.6.4.- Ejecucin de excepciones: RAISE 1.6.5.- SQLCODE.1.6.6.- SQLERRM.1.6.7.- Algunos ejemplos de tratamiento de excepciones.1.7.- Ejercicios. 1.7.1.- Ejercicios resueltos. 1.7.2.- Ejercicios propuestos.

1.1.- INTRODUCCIN.PL/SQL es un lenguaje de programacin procedural estructurado en bloques que ampla el lenguaje estndar SQL, uniendo la potencia de ste, con la capacidad de los lenguajes de programacin tradicionales. De esta forma, PL/SQL no slo nos permite manipular los datos de la base de datos ORACLE, sino que dispone de tcnicas procedurales como los bucles o el control condicional. Una de las mayores limitaciones del SQL es la imposibilidad de tratar de forma independiente las filas devueltas por una consulta. Esto se consigue con PL/SQL mediante el uso de cursores que ORACLE abre para ejecutar una sentencia SELECT. Tambin nos permite controlar los errores que se pudieran producir (excepciones), como por ejemplo que una consulta no devuelva filas. Existen errores propios de Oracle (excepciones predefinidas) y errores que puede provocar y definir el usuario. El motor PL/SQL se encuentra en el Ncleo de Oracle (RDBMS)

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Por extensin al SQL, PL/SQL puede acceder a la base de Datos ORACLE porque soporta: Sentencias LMD de SQL (SELECT, INSERT, UPDATE, DELETE) Sentencias transaccionales (COMMIT, ROLLBACK, SAVEPOINT) Todas las funciones SQL (numricas, de caracteres, de fechas, de grupos...) Todos los tipos de predicados (sentencia WHERE) Las propias sentencias de PL/SQL.

Adems de la capacidad procedural, el soporte a SQL, y la integracin con Oracle ya comentadas, PL/SQL presenta las siguientes ventajas: Mejora del rendimiento. Sin PL/SQL, Oracle tendra que procesar las instrucciones una a una. Cada llamada producira un overhead1 considerable, sobre todo si consideramos que estas consultas viajan a travs de la red. Por el contrario, con PL/SQL, un bloque completo de sentencias puede ser enviado de una vez, lo que reduce la comunicacin con la base de datos. Los procedimientos almacenados son compilados una vez y almacenados en la base en formato ejecutable, consiguindose mayor rapidez y eficiencia en las llamadas. Adems, ya que los procedimientos almacenados se ejecutan en el propio servidor, el trfico por la red se reduce a la simple llamada y el envo de los parmetros necesarios para su ejecucin. El cdigo ejecutable se almacena en cach y se comparte con todos los usuarios, lo que reduce los requerimientos de memoria y disminuye el overhead. Portabilidad. Las aplicaciones escritas con PL/SQL son portables a cualquier sistema operativo y plataforma en la cual se encuentre corriendo Oracle. En otras palabras, PL/SQL corre dondequiera que se encuentre corriendo Oracle. Esto significa que se pueden codificar libreras que podrn ser reutilizadas en otros entornos. Seguridad. Los procedimientos almacenados habilitan la divisin lgica entre cliente y servidor, de forma que se previene que se manipulen los datos desde el cliente. Adems permite a los usuarios ejecutar solo aquellos procedimientos para los cuales tengan privilegios.
1

Informacin adjuntada a un aviso de la red para garantizar un envo correcto al destino

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.2.- EL BLOQUE PL/SQL.PL/SQL es un lenguaje estructurado en bloques. Un bloque es un conjunto de sentencias procedurales y SQL. Un bloque PL/SQL consta de tres zonas: Declaraciones: donde se definen las variables, cursores y excepciones de usuario que se necesiten. Proceso: donde se escriben todas las sentencias ejecutables. Excepciones: zona donde se define el control de errores.

DECLARE Declaracin de variables y ctes ; Declaracin de cursores ; Declaracin de excepciones ; BEGIN Sentencias ejecutables ; EXCEPTION Control de excepciones ; END ;

DECLARE .......... BEGIN .... DECLARE BEGIN .... EXCEPTION .... END ; ..... EXCEPTION ............ END ;

Los bloques PL/SQL se pueden anidar tanto en la zona BEGIN como en la EXCEPTION, pero no en la zona DECLARE, que es nica para cada bloque. Cada bloque debe acabar con el carcter '/' como nico de la ltima lnea.

1.3.- TIPOS DE DATOS, CONVERSIONES, MBITO Y VISIBILIDAD.1.3.1.- Tipos de datos.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. Un tipo escalar no tiene componentes internas. Un tipo compuesto tiene otras componentes internas que pueden ser manipuladas individualmente. Un tipo referencia almacena valores, llamados punteros, que designan a otros elementos de programa. Un tipo lob (large object) especifica la ubicacin de un tipo especial de datos que se almacenan de manera diferente. En la siguiente figura se muestran los diferentes tipos de datos predefinidos.

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Tipos de datos de PL/SQL Number.- Numrico. PL/SQL tiene predefinidos los siguientes subtipos: DEC DECIMAL NUMERIC DOUBLE PRECISION FLOAT REAL INTEGER INT SMALLINT

Nmeros con punto fijo con precisin mxima de 38 dgitos decimales Nmeros con punto flotante con precisin mxima de 38 dgitos decimales Nmeros con punto flotante con precisin mxima de 18 dgitos decimales Nmeros enteros con una precisin mxima de 38 dgitos

Binary_integer.- binario con desbordamiento a number. Usado para almacenar enteros con signo. PL/SQL tiene predefinidos los siguientes subtipos de binary_integer: NATURAL NATURALN POSITIVE POSITIVEN SIGNTYPE No negativo. No negativo, no admite nulos. Positivo. Positivo, no admite nulos. -1, 0 y 1, usado en lgica trivaluada.

Pls_integer.- binario sin desbordamiento. Es ms actualizado que su equivalente binary_integer, el cual se mantiene por razones de compatibilidad. Bolanos.- solo pueden tomar 3 valores TRUE, FALSE y NULL. Usados para lgica trivaluada.
Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.3.2.- Conversiones.Adems de las conversiones de tipo de datos realizadas explcitamente por las funciones de conversin, cuando se hace necesario, PL/SQL puede convertir un tipo de dato a otro en forma implcita. Esto significa que la interpretacin que se dar a algn dato ser la que mejor se adecue dependiendo del contexto en que se encuentre, por ejemplo cuando variables de tipo char se operan matemticamente para obtener un resultado numrico. Si PL/SQL no puede decidir a qu tipos de dato de destino puede convertir una variable se generar un error de compilacin.
Hasta Desde BIN_INT CHAR DATE LONG NUMBER PLS_INT RAW ROWID VARCHAR2 BIN_INT CHAR DATE LONG NUMBER X X X X X X X X X X X X X X X X X X X X PLS_INT RAW X X ROWID VARCHAR2 X X X X X X X X

X X

X X

X X

Tabla de conversiones implcitas

1.3.3.- mbito y visibilidad.Dentro de un programa, las referencias a un identificador son resueltas de acuerdo a su mbito y visibilidad. El mbito de un identificador (variable o constante) es aquella regin de la unidad de programa (bloque, subprograma o paquete) en la que el identificador mantiene su valor sin perderlo. La visibilidad se refiere a las zonas en que se puede referenciar (usar). Los identificadores declarados en un bloque de PL/SQL se consideran locales al bloque y globales a todos sus sub-bloques anidados. Por eso un mismo identificador no puede declararse dos veces en un mismo bloque pero s en varios bloques diferentes, cuantas veces se desee. La siguiente figura muestra el alcance y visibilidad de la variable x, la cual est declarada en dos bloques cerrados diferentes.

Puede observarse que la variable ms externa tiene un mbito ms amplio pero cuando es referenciada en el bloque en que se ha declarado otra variable con el mismo nombre, es esta ltima la que puede ser manipulada y no la primera.

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.4.- ZONA DE DECLARACIONES.Es la parte del bloque PL/SQL utilizada cuando es necesario definir variables y/o constantes, cursores o excepciones. Es una zona opcional de forma que si no hacen falta declaraciones se omite la palabra reservada DECLARE. Sintaxis: DECLARE declaracin_variables; declaracin_cursores; declaracin_excepciones;

La palabra reservada DECLARE, determina el comienzo de un bloque PL/SQL. Las definiciones realizadas en esta zona son declaraciones locales que solo se reconocern en el bloque PL/SQL actual y en los bloques anidados que pudiese contener.

1.4.1.- Declaracin de variables y constantes.Las variables se utilizan para guardar valores devueltos por una consulta o almacenar clculos intermedios. Las constantes son campos que se definen y no alteran su valor durante el proceso. Sintaxis: Identificador [CONSTANT] <tipo_de_dato / identificador% TYPE / identificador% ROWTYPE> [NOT NULL] [:= <expresin_plsql / valor_constante>]; dnde: identificador [CONSTANT] tipo_de_dato Nombre de la variable o constante. Palabra reservada para la definicin de constantes. Tipo de dato de la variable.

identificador%TYPE Declara la variable o constante con el mismo tipo de dato que una variable definida anteriormente o que una columna de una tabla. Identificador es el nombre de la variable PL/SQL definida anteriormente, o el nombre de la tabla y la columna de la base de datos. Identiticador%ROWTYPE Este atributo declara una fila variable con campos con los mismos nombres y tipos que las columnas de una tabla o de una fila recuperada de un cursor. Al declarar una fila %ROWTYPE, no se admite ni la constante, ni la asignacin de valores. NOT NULL Obliga a que siempre tenga valor. expresin_plsql / valor_constante Asigna a la variable o constante el valor inicial como resultado de una operacin (expresin pl/sql) o con un valor constante. Ejemplos:
varn_dept total respuesta var2 registro1 Dep. Informtica tdepto.numde%TYPE ; number(10,2):=0 ; char(1) ; varn_dept%TYPE ; dept%ROWTYPE ;

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.4.2.- Declaracin de registros.Los registros son grupos de variables que pueden recibir la informacin de un cursor. Se declaran segn la siguiente sintaxis: TYPE nombre_registro campo1 campo2 .... var nombre_registro; Ejemplo:
TYPE reg IS RECORD ( empleado temple.nomem%TYPE salario temple.salar% TYPE r_empleado reg; -- Declaracin del tipo de dato reg ); -- Declaracin de una variable de tipo reg

IS RECORD ( tabla.columna%TYPE tabla.columna%TYPE ) ;

1.4.3.- Declaracin de cursores.La declaracin de un cursor le proporciona un nombre y le asocia una consulta (SELECT). El cursor es un rea de trabajo que utiliza ORACLE para consultas que devuelven ms de una fila, permitiendo la lectura y manipulacin de cada una de ellas. Un cursor tiene tres componentes: La tabla de resultado obtenida al ejecutar la SELECT. Un orden establecido entre sus filas. Un puntero que seala la posicin sobre la tabla resultado.

Tipos de cursores: Estticos Se declaran en la DECLARE con su clusula SELECT asociada, y se abren, ejecutan y cierran en la zona BEGN. Pueden ser simples o parametrizados. Dinmicos La SELECT asociada al cursor no se especifica en la zona DECLARE, sino en BEGIN, con lo que el resultado de su ejecucin es dinmico. Segn se declare o no el registro que recibir los datos de la SELECT, los cursores dinmicos pueden ser prefijados o no prefijados.

Esttico simple Un cursor esttico simple se declara en la DECLARE con su clusula SELECT y se abre, ejecuta y cierra en la zona BEGIN. Sintaxis: Ejemplo:
DECLARE CURSOR SELECT Dep. Informtica departa IS numde, nomde FROM tdepto ;

CURSOR

nombre_cursor IS sentencia SELECT ;

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Esttico parametrizado Los cursores estticos parametrizados permiten obtener con el mismo cursor diferentes resultados en funcin del valor que se le pase al parmetro. Su sintaxis es: CURSOR nombre_cursor (nombre_parmetro tipo_parmetro) IS sentencia_SELECT_utilizando_los_parmtetros ; Los parmetros son variables nicamente de entrada, nunca para recuperacin de datos. Estas variables son locales para el cursor y solo se referencian en la SELECT. Si se definen parmetros, stos deben especificarse en la SELECT y se utilizan igual que si utilizsemos un valor constante. Al abrir el cursor se sustituyen los parmetros por los valores correspondientes. Ejemplo:
ACCEPT valor PROMPT Departamento: DECLARE CURSOR empleados (dept_pl NUMBER) IS SELECT numem, nomem FROM temple WHERE numde = dept_pl ; .......... BEGIN OPEN empleados (&valor) ; -- Apertura del cursor y paso del parmetro

Si en cualquier cursor quisiramos modificar las filas que nos devuelve, deberamos aadir a la Select asociada al cursor la clusula: FOR UPDATE OF nombre_columna ; Ejemplo:
DECLARE CURSOR empleados IS SELECT numem, nomem FROM temple FOR UPDATE OF nomem ;

Dinmico no prefijado Los cursores dinmicos son aquellos que la SELECT no aparece en la zona DECLARE. En los no prefijados no se concreta la proyeccin de la SELECT . Sintaxis: TYPE tipo_cursor IS REF CURSOR ; ... nombre_cursor tipo_cursor ;

Ejemplo:
DECLARE TYPE CurEmp IS REF CURSOR .... C_emple1 CurEmp ; -- Declaracin del tipo de cursor -- Declaracin de un cursor del tipo anterior

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Dinmico prefijado Los cursores dinmicos prefijados declaran el registro que va a recibir los datos del cursor con RETURN <tipo_registro>. Sintaxis: TYPE tipo_cursor IS REF CURSOR RETURN tabla%ROWTYPE ; ... nombre_cursor tipo_cursor ;

Ejemplo:
DECLARE TYPE .... Datos.emp Datos IS REF CURSOR RETURN temple%ROWTYPE ; Datos ;

1.4.4.- Declaracin de excepciones. Si vamos a utilizar excepciones definidas por el usuario, es necesario declararlas en la zona DECLARE. Las excepciones de usuario se declaran simplemente escribiendo la palabra EXCEPTION detrs del nombre asignado a la excepcin que queramos definir: Sintaxis: nombre_excepcin EXCEPTION ;

El tratamiento de las excepciones, tanto las internas de Oracle como las de usuario, se realiza en la zona EXCEPTION, y lo veremos en el apartado 1.6.

1.5.- ZONA DE PROCESO: BEGIN.En esta zona del bloque PL/SQL se escriben todas las sentencias ejecutables. El comienzo del bloque PL/SQL se especifica con la palabra BEGIN. En el bloque se permiten: Sentencias propias de PL/SQL. Sentencias DML de SQL (SELECT, INSERT, UPDATE, DELETE) Sentencias transaccionales (COMMIT, ROLLBACK, SAVEPOINT)

1.5.1.- Sentencias propias de PL/SQL.Las sentencias propias del lenguaje PL/SQL se agrupan en: Asignaciones Manejo de cursores EXIT Control condicional Bucles GOTO NULL

Dep. Informtica

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.1.1.- Asignacin.- La asignacin de valores a una variable se hace con el operador ":=". Algunos ejemplos son:
salario := 2251 ; comision := substr(cod_postal,2 ,3) ; aumento := salario * 0.1 ; total_sueldo := salario + aumento + comision ;

1.5.1.2.- Manejo de Cursores.- Los cursores que vayan a utilizarse debern haber sido definidos en la zona DECLARE. Para poder realizar una lectura de todas las filas recuperadas en un cursor, es necesario realizar un bucle. Existen bucles ya predefinidos para recorrer cursores, aunque tambin se pueden utilizar bucles simples y hacer el recorrido de forma ms controlada. Para el manejo de los cursores, podemos utilizar los siguientes atributos predefinidos: %NOTFOUND Devuelve TRUE si la ltima lectura falla porque no hay mas filas disponibles o FALSE si recupera una fila. Se utiliza para detectar el final y romper el bucle de lectura de un cursor. Es lo contrario de %NOTFOUND Devuelve el nmero de fila, procesada por el cursor. Devuelve TRUE si el cursor esta abierto y FALSE si esta cerrado.

%FOUND %ROWCOUNT %ISOPEN

Para manejar un cursor primero hay que abrirlo (OPEN), luego leerlo (FETCH) y por ltimo cerrarlo (CLOSE). Abrir el cursor: OPEN. La sentencia OPEN, evala la SELECT asociada al cursor y lo prepara para permitir su lectura. La sentencia OPEN, abre el cursor y ejecuta la consulta asociada a dicho cursor. Tipo de cursor cursor esttico simple cursor esttico parametrizado cursor dinmico Sintaxis OPEN nombre_cursor ; OPEN nombre_cursor (parm1 [,parm2] .. ) ; OPEN nombre_cursor FOR sentencia_select ;

Los parmetros se pueden pasar posicionalmente: el primer parmetro sustituye al primero que espera el cursor y as sucesivamente, o por asociacin de nombres:
DECLARE CURSOR departamentos (dept_pl NUMBER, loc_pl CHAR) IS SELECT dept_no, dname, loc FROM dept WHERE dept_no = dept_pl and loc = loc_pl ; BEGIN OPEN departamentos (10, 'MADRID') ; OPEN departamentos (v_dept, v_loc) ; OPEN departamentos (loc_pl => 'MADRID', dept_pl => 10) ; OPEN departamentos (loc_pl => v_loc, dept_pl => v_dept) ;

Dep. Informtica

10

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Leer el Cursor: FETCH. La sentencia FETCH, recupera la siguiente fila del cursor hasta detectar el final. Los datos recuperados deben almacenarse en variables o registros. Sintaxis para recuperacin en variables: FETCH nombre_cursor INTO var1 [,var2] ; Todas las variables que aparecen en la clusula INTO deben haber sido definidas previamente en la zona de declaracin de variables DECLARE. Ser necesario tener tantas variables como columnas estemos recuperando en la SELECT asociada al cursor. La recuperacin es posicional, es decir la primera columna seleccionada se almacenar en la primera variable, la segunda en la segunda, y as sucesivamente, por lo tanto, los tipos de las variables debern ser compatibles con los valores de las columnas que van a almacenar. Sintaxis para recuperacin en registro: FETCH nombre_cursor INTO nombre_registro ; Es la sintaxis ms usada debido a la facilidad para declarar el registro en la zona DECLARE con la opcin de atributo %ROWTYPE<nombre_tabla>/<nombre_cursor>. En la zona BEGIN, y mientras el cursor est abierto, las columnas del cursor pueden utilizarse haciendo referencia al nombre del registro, del que son variables miembro. En el supuesto del prrafo anterior, y caso de que la select asociada al cursor tenga columnas con expresiones distintas a nombres de columna (SUM(salar), por ejemplo), debern usarse alias de columna, por cuyo nombre sern referenciadas. Ejemplo:
DECLARE CURSOR curdep IS SELECT numde, nomde, presu FROM tdepto ; reg_dept curdep%ROWTYPE ; BEGIN FETCH curdep INTO reg_dept ; IF reg_dep.numde < 150 THEN ...

Cerrar el Cursor: CLOSE. La sentencia CLOSE cierra el cursor. Una vez cerrado no se puede volver a leer (FETCH), pero si se puede volver a abrir. Cualquier operacin que se intenta realizar con un cursor que esta cerrado, provoca la excepcin predefinida INVALID_CURSOR. Su sintaxis es: CLOSE Ejemplo:
CLOSE curdep;

nombre_cursor ;

1.5.1.3.- EXIT.EXIT nos permite salir de un bucle de forma radical o evaluando una condicin. Su sintaxis es: EXIT [nombre_bucle] [WHEN condicin] ; Si se omite el nombre del bucle sale del bucle actual. Si se especifica WHEN, solo sale si se cumple la condicin. Si se omiten ambos, sale del bucle actual incondicionalmente.
Dep. Informtica

11

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.1.4.- Control condicional. Permite ejecutar una o varias sentencias dependiendo de una condicin. Su sintaxis es: IF [ELSIF [ELSE END IF ; condicin THEN sentencias ; condicin THEN sentencias ;] sentencias ;]

La condicin puede ser cualquier expresin PL/SQL de comparacin. Al procesar la sentencia, se evalan una a una todas las condiciones por orden empezando por la que le sigue a la palabra IF. Si alguna de las condiciones se cumple, se ejecutan las sentencias especificadas y se pasa a la siguiente instruccin tras END IF . Si no se cumple ninguna condicin, pero existe un ELSE, se ejecutan las sentencias siguientes al ELSE, si no es as, no se ejecuta ninguna sentencia. Slo una secuencia de sentencias se ejecuta al procesar una instruccin IF. Ejemplos:
IF tipo = 1 ELSIF tipo = 2 ELSE END IF ; THEN sal := sal * 0.01 ; THEN sal := sal * 0.02 ; sal := sal * 0.03 ;

1.5.1.5.- Bucles. Existen 5 tipos de bucles: Bucles bsicos Bucles condicionales (WHILE) Bucles numricos (FOR) Bucles sobre cursores Bucles para una SELECT Bucles Bsicos. Son bucles infinitos que responden a la siguiente sintaxis: LOOP sentencias ; END LOOP ; Para romper el bucle (salir de l) deberemos utilizar EXIT, GOTO o RAISE. Ejemplo:
LOOP FETCH cursor_cl INTO registro ; EXIT WHEN cursor_cl%NOTFOUND ; END LOOP ;

Bucles Condicionales (WHILE). La condicin se evala antes de cada entrada al bucle. Si la condicin se cumple se ejecutan las sentencias, y si no es as, se pasa el control a la sentencia siguiente al final del bucle. Su sintaxis es: WHILE condicin LOOP sentencias; END LOOP;
Dep. Informtica

12

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Ejemplo:
WHILE a>0 LOOP a := a - 1 ; END LOOP ;

Bucles Numricos (FOR). Son bucles que se ejecutan una vez para cada elemento definido dentro del rango numrico. Su sintaxis es: FOR ndice IN [REVERSE] exp_n1 .. exp_n2 LOOP Sentencias ; END LOOP ; ndice REVERSE es la variable numrica de control del bucle, comienza con el valor de exp_n1 y se va incrementando de 1 en 1 hasta el valor de exp_n2. realiza la cuenta al revs, desde exp_n2 hasta exp_n1.

exp_n1, exp_n2 pueden ser valores constantes o cualquier expresin numrica. Ejemplo:
FOR x IN 1 .. 10 LOOP valor:= valor + x ; END LOOP ;

Bucles sobre Cursores. Son bucles que se ejecutan para cada fila que recupera el cursor. Cuando se inicia un bucle sobre un cursor automticamente se realizan los siguientes pasos: - Se declara implcitamente el registro especificado como nombre_cursor%ROWTYPE. - Se abre el cursor. - Se realiza la lectura y se ejecutan las sentencias del bucle hasta que no hay mas filas. - Se cierra el cursor. Sintaxis: FOR nombre_registro IN nombre_cursor LOOP Sentencias ; END LOOP ; La variable de control del bucle se incrementa automticamente y no es necesario inicializarla, pues lo est implcitamente como variable local de tipo integer. El mbito del contador es el bucle y no puede accederse a su valor fuera de l. Dentro del bucle, el contador puede referenciarse como una constante pero no se le puede asignar un valor. Tambin le podemos pasar parmetros al cursor, tal y como se indic en el apartado de manejo de cursores. En este caso los parmetros se indican entre parntesis tras el nombre del cursor: FOR nombre_registro IN nombre_cursor(lista_de_parametros) ....... Si se sale del bucle prematuramente (p.e. con EXIT), o se detecta un error (excepcin), el cursor se cierra.
Dep. Informtica

13

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Ejemplo:
DECLARE CURSOR curdep IS SELECT numde, nomde FROM tdepto; reg_dept curdep%ROWTYPE ;

-- (*)

BEGIN FOR reg_dept IN curdep LOOP ......; END LOOP; (*) Esta sentencia sobra, pues es declarada implcitamente por este tipo de bucle.

Bucles para Sentencias SELECT. Es el mismo concepto que el del cursor, excepto que en vez de declarar un cursor con su SELECT, se escribe directamente la sentencia SELECT y Oracle utiliza un cursor interno para hacer la declaracin. Su sintaxis es: FOR nombre_registro IN sentencia_SELECT LOOP sentencias ; END LOOP ; Si se sale del bucle prematuramente, o se detecta un error, el cursor se cierra. Hay que resaltar que las columnas del cursor no pueden ser usadas con <registro.columna> fuera de estos dos ltimos bucles, ya que cierran automticamente el cursor que tratan. Ejemplo:
FOR registro IN SELECT numde FROM tdepto LOOP sentencias ; END LOOP ;

1.5.1.6.- GOTO. Esta sentencia transfiere el control a la sentencia o bloque PL/SQL siguiente a la etiqueta indicada. Su sintaxis es: GOTO etiqueta; La etiqueta se especifica entre los smbolos << y >>, por ejemplo:
GOTO A ..... <<A>>

La sentencia GOTO puede ir a otra parte del bloque o aun sub-bloque, pero nunca podr ir a la zona de excepciones. La sentencia siguiente a una etiqueta debe ser ejecutable. En caso de no existir, por ejemplo por ser el final del programa, se puede utilizar la sentencia NULL.

1.5.1.7.- NULL. Significa inaccin. El nico objeto que tiene es pasar el control a la siguiente sentencia. Su sintaxis es: NULL Ejemplo:
IF tipo = 1 THEN vsal := vsal * 0.01 ELSE NULL; END IF; Dep. Informtica

14

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.2. Sentencias DML Oracle abre un cursor implcito (no definido por el usuario) por cada sentencia SQL que tenga que procesar. PL/SQL nos permite referimos al cursor implcito mas reciente como "SQL%". Estos cursores los maneja Oracle, por lo que no se pueden abrir, leer o cerrar, pero si podemos utilizar algunos atributos que nos dan informacin sobre la ejecucin de la sentencia SQL (INSERT, UPDATE, DELETE, SELECT) para la que se haya abierto. Estos atributos son: %NOTFOUND Devuelve TRUE si un INSERT, UPDATE o DELETE no ha procesado ninguna fila o si la SELECT no recupera nada. En este ltimo caso se provoca la excepcin predefinida NO_DATA_FOUND %FOUND Devuelve TRUE si un INSERT, UPDATE o DELETE ha procesado alguna fila o una SELECT recupera datos

%ROWCOUNT Devuelve el nmero de filas procesadas por un INSERT, UPDATE o DELETE o las recuperadas por una SELECT .

1.5.2.1.- SELECT. La sentencia SELECT recupera valores de la Base de Datos que sern almacenados en variables PL/SQL. La sintaxis completa de la SELECT ya se estudi en la parte de SQL, la nica variante en programacin es la opcin INTO que permite almacenar los valores recuperados en variables. Para poder recuperar valores en variables, la consulta slo debe devolver una fila (no funciona con predicados de grupo). Su sintaxis es: SELECT FROM {* / col1 [,coI2] } INTO {reg / var1 [,var2] } tabla resto_de_la_select ;

Las excepciones predefinidas ms comunes son: A.- Si la consulta no devuelve ninguna fila: Se produce la excepcin predefinida NO_DATA_FOUND El error Oracle (SQLCODE) ORA-01403 El mensaje de error (SQLERRM) "no data found" SQL%NOTFOUND es TRUE SQL%FOUND es FALSE SQL%ROWCOUNT es 0 B.- Si la consulta devuelve ms de una fila: Se produce la excepcin predefinida TOO_MANY _ROWS El error Oracle (SQLCODE) ORA-O1422 El mensaje de error (SQLEERM) 'single-row query returns more than one row" SQL%NOTFOUND es FALSE SQL%FOUND es TRUE SQL%ROWCOUNT es 1 (solo puede devolver 0 o 1 fila)

Dep. Informtica

15

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.2.2.- INSERT. INSERT crea filas en la tabla o vista especificada. Su sintaxis es idntica a la de SQL. Se puede realizar el INSERT con valores de las variables PL/SQL o con los datos de una SELECT. En este caso hay que tener en cuenta que sta ltima no lleva clusula INTO. En ambos casos el nmero de columnas a insertar deber ser igual al nmero de valores y/o variables o a las columnas especificadas en la SELECT. En caso de omitir las columnas de la tabla en la que vamos a insertar, se debern especificar valores para todas las columnas de la tabla y en el mismo orden en el que esta haya sido creada. Su sintaxis es la misma de SQL: INSERT INTO tabla [ (col1 [,col2] .....) ] { VALUES (exp1 [,exp2]..... ) / Sentencia_Select }; VALUES Sentencia_Select permite insertar una tupla indicando expresiones constantes o variables. permite insertar varias tuplas a la vez.

Si no se inserta ninguna fila los atributos devuelven los siguientes valores: SQL%NOTFOUND es TRUE SQL%FOUND es FALSE SQL%ROWCOUNT es 0 Si se dan de alta una o mas filas los atributos devuelven los siguientes valores: SQL%NOTFOUND es FALSE SQL%FOUND es TRUE SQL%ROWCOUNT es nmero de filas insertadas Ejemplo:
DECLARE x number(2) := 80; y char(14) := 'INFORMATICA'; c char(13) := 'MADRID'; BEGIN INSERT INTO dept (loc, dep_no, dname) VALUES (c, x, y); x := 90; y := 'PERSONAL' c := 'MALAGA' INSERT INTO dept (loc, dep_no, dname) VALUES (c, x, y); INSERT INTO dept (loc, dep_no, dname) VALUES (92, 'ADMINISTRACION', 'SEVILLA') ; END;

Dep. Informtica

16

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.2.3.- UPDATE La sentencia UPDA TE permite modificar los datos almacenados en las tablas o vistas. Sintaxis: UPDATE SET tabla columna = { sentencia select / expresin_plsql / constante / variable_plsql } [ WHERE { condicin / CURRENT OF nombre_cursor} ] ;

La clusula WHERE CURRENT OF debe ser utilizada despus de haber realizado la lectura del cursor que debe haber sido definido con la opcin FOR UPDATE OF. Esta clusula no es admitida en cursores cuya select utilice varias tablas (obtenidos con select con yuncin). Si no se actualiza ninguna fila los atributos devuelven los siguientes valores: SQL%NOTFOUND es TRUE SQL %FOUND es FALSE SQL %ROWCOUNT es 0 Si se actualizan una o ms filas los atributos devuelven los siguientes valores: SQL %NOTFOUND es FALSE SQL%FOUND es TRUE SQL%ROWCOUNT el nmero de filas actualizadas Ejemplo:
UPDATE temple SET salar = salar * 0.1 WHERE numde = 110 ;

1.5.2.4.- DELETE. La sentencia DELETE permite borrar filas de la tabla o vista especificada. Sintaxis: DELETE [ FROM] [ WHERE tabla {condicin / CURRENT OF nombre_cursor} ] ;

Al igual que con UPDATE, la clusula WHERE CURRENT OF debe ser utilizada despus de haber ledo el cursor que debe haber sido definido con la opcin FOR UPDATE OF. Si no se borra ninguna fila los atributos devuelven los siguientes valores: SQL%NOTFOUND es TRUE SQL%FOUND es FALSE SQL%ROWCOUNT es 0

Dep. Informtica

17

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Si se borran una o ms filas los atributos devuelven los siguientes valores: SQL%NOTFOUND es FALSE SQL%FOUND es TRUE SQL%ROWCOUNT es nmero de filas borradas Ejemplo:
DELETE WHERE FROM temple numde = 110;

1.5.3. Sentencias transaccionales Como sabemos, una transaccin o Unidad Lgica de Trabajo (ULT) es una secuencia de operaciones de actualizacin que forman un todo, de forma que o se ejecutan todas o no se ejecuta ninguna, debiendo dejar la base de datos en estado coherente. Una transaccin comienza en la primera sentencia SQL tras: una sentencia COMMIT, una sentencia ROLLBACK o una conexin a la base de datos. Una transaccin termina con: una sentencia COMMIT, una sentencia ROLLBACK o una desconexin, intencionada o no, a la base de datos. El SGBD realiza un COMMIT implcito antes de ejecutar cualquier sentencia de LDD (create, alter, ..) o al realizar una desconexin que no haya sido precedida de un error. A continuacin veremos las sentencias SQL que permiten gestionar explcitamente las transacciones:

1.5.3.1.- SAVEPOINT. Los puntos de salvaguarda son marcas que va poniendo el usuario durante la transaccin. Estas marcas permiten deshacer los cambios por partes en vez de deshacer toda la transaccin. Sintaxis: SAVEPOINT <punto_de_salvaguarda> ;

Los nombres de los puntos de salvaguarda pueden reutilizarse durante la transaccin. Al reutilizarlo el anterior punto se pierde. Al ejecutar un ROLLBACK sin parmetros o un COMMIT, se eliminan todos los puntos de salvaguarda. ROLLBACK TO <punto_de_salvaguarda> hace que solo se borren los puntos posteriores al indicado. As, por ejemplo:
INSERT INTO ... SAVEPOINT A; DELETE ... ROLLBACK TO SAVEPOINT A;

deshace solo el borrado, permaneciendo pendiente la insercin realizada con INSERT .

Dep. Informtica

18

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.5.3.2.- COMMIT. Seala el final de una transaccin y el principio de otra indicndole al sistema que se deben validar los cambios que se produjeron desde el principio de la transaccin que se da por concluida, hacindolos visibles para los dems usuarios. Su sintaxis es: COMMIT [WORK] ; La palabra reservada WORK es opcional, y no tiene ninguna trascendencia. Al hacer un COMMIT, se liberan todos los SAVEPOINT indicados hasta el momento. Si se hace el COMMIT cuando tenemos abierto un cursor declarado con la opcin FOR UPDATE OF, el siguiente FETCH provoca un error, por lo que se debe cerrar el cursor. 1.5.3.3.- ROLLBACK. Seala el final de una transaccin y el principio de otra indicndole al sistema que se deben restaurar el estado de la base de datos tal y como estaba al comenzar la transaccin, es decir, deshace todos los cambios pendientes de validacin de la transaccin actual. Su sintaxis es: ROLLBACK [WORK] [TO SAVEPOINT punto_salvaguarda] ; WORK es opcional, y no tiene ninguna trascendencia. TO SAVEPOINT punto_salvaguarda deshace slo los cambios efectuados desde el punto de salvaguarda indicado. 1.5.3.4.- SET TRANSACTION READ ONLY. Permite establecer una transaccin de solo lectura Sintaxis: SET TRANSACTION READ ONLY ;

Una vez especificado este comando, las siguientes consultas que se realicen, solo vern los cambios efectuados antes de que comenzase la transaccin. SQL*Plus dispone de un comando para controlar las validaciones de forma automtica. Su sintaxis es: SET AUTOCOMMIT ON/OFF Si se especifica ON los cambios se validarn automticamente despus de cada operacin de actualizacin de datos. La opcin por defecto es OFF y permite que sea el usuario el que controle la validacin de los cambios con los comandos anteriores. Las sentencias del LMD y la desconexin de Oracle llevan implcita una validacin (commit). Si se produce la cada del sistema o la terminacin anormal de una aplicacin, se llevar a cabo una restauracin automtica, mediante la consulta al fichero diario o log. Oracle almacena temporalmente, en los llamados segmentos de rollback, la informacin necesaria para deshacer los cambios si se ejecuta un ROLLBACK y dejar la informacin en estado consistente.
Dep. Informtica

19

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Puede comprobarse que el propietario de la tabla modificada, tiene constancia al instante de las modificaciones que se produzcan. Bastar hacer una consulta y observar el nuevo valor. El DBA Studio es una herramienta del Administrador de la Base de Datos, por lo que al abrir una tabla nos da la visin que de la misma tiene aqul. Si accedemos a una tabla con el DBA Studio, no apreciaremos los cambios hasta que se cierre la sesin del usuario (nos conectemos como otro usuario o salgamos de SQL, por ejemplo), o se ejecute el comando COMMIT.

1.6.- ZONA DE EXCEPCIONES: EXCEPTION.La zona de excepciones es la ltima parte del bloque PL/SQL y en ella se realiza la gestin y el control de errores. 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 ya que permite mantener las rutinas correspondientes al tratamiento de los errores en forma separada de la lgica del negocio. Sintaxis: EXCEPTION control_de_errores ;

Slo se activa un error de todos los definidos en dicha zona. La zona EXCEPTION termina cuando se detecta la palabra reservada END (fin de la zona y fin del bloque PL/SQL). La ejecucin de las sentencias asignadas a un error se produce: Automticamente Oracle detecta un error, para el proceso y pasa el control a la zona EXCEPTION, buscando si existe tratamiento al error detectado. Manualmente En el proceso llega un punto en el que nos interesa realizar lo mismo que si se hubiese detectado un error y ejecutamos la excepcin.

La zona EXCEPTION es opcional y se puede omitir. En este caso, si se detecta algn error, el bloque PL/SQL termina incorrectamente, terminando el proceso y devolviendo el control al punto de partida. Existen dos variables para poder recuperar los errores Oracle que se pueden producir: SQLCODE y SQLERRM.

1.6.1.- Control de errores. Cuando en el proceso se detecta un error, el proceso se para, pasando el control a la zona EXCEPTION. Sintaxis: WHEN nombre_excepcin THEN sentencias ; es el nombre de la que se quiere controlar (predefinida o de usuario). es el conjunto de sentencias que se ejecutan si se produce el error.

nombre_excepcin sentencias
Dep. Informtica

20

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.6.2.- Excepciones predefinidas. En Oracle existen las siguientes excepciones predefinidas:


Nombre de excepcin CURSOR_ALREADY_OPEN DUP_VAL_ON_INDEX INVALID_CURSOR INVALID_NUMBER LOGIN_DENIED NO_DATA_FOUND NOT_LOGGED_ON PROGRAM_ERROR STORAGE_ERROR TIMEOUT_ON_RESOURCE TOO_MANY_ROWS VALUE_ERROR ZERO_DIVIDE OTHERS Tipo de error indicado Cursor abierto previamente Valor duplicado en ndice Cursor no vlido Nmero no vlido Denegada la conexin a Oracle La consulta no recupera ninguna fila No conectado a Oracle Problema interno Fuera de memoria o error de memoria Exceso de recursos consumidos La consulta devuelve ms de una fila Valor incorrecto Divisin por cero Cualquier otro error no especificado SQLCODE -6511 -1 -1001 -1722 -1017 +100 -1012 -6501 -6500 -51 -1422 -6502 -1476

1.6.3.- Excepciones definidas por el usuario Existen dos tipos de excepciones a definir por los usuarios y son: Basadas en errores ORACLE (EXCEPTION INIT).Asigna un nombre a un error ORACLE existente. Sintaxis: PRAGMA EXCEPTION_INIT (nombre_excepcin, -nmero_error) nombre_excepcin Deber estar definida como excepcin de usuario. nmero_error Es el numero del error Oracle que se desea controlar (el que nos devuelve el SQLCODE).

La definicin se realiza en la zona del DECLARE y el control de la excepcin en la zona EXCEPTION. Solo debe haber una excepcin por cada error Oracle. Otras excepciones. Se trata de un control de errores para el usuario. Este tipo de excepciones se deben declarar segn vimos en el apartado de DECLARE. Una vez declarada la excepcin, la podemos provocar cuando sea necesario utilizando la funcin RAISE que se detalla a continuacin. El tratamiento dentro de la zona de excepciones es exactamente igual que el de una excepcin predefinida. Sintaxis: DECLARE A EXCEPTION; BEGIN RAISE A; EXCEPTION WHEN A THEN .... ; END;
Dep. Informtica

21

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.6.4. - Ejecucin de excepciones: RAISE Para la ejecucin del bloque PL/SQL y pasa el control a la zona de excepciones. Sintaxis: RAISE nombre_excepcin

El nombre_excepcin es el nombre de la excepcin que se desea ejecutar, pueden ser excepciones predefinidas o excepciones de usuario. Ejemplo:
DECLARE error EXCEPTION; BEGIN RAISE error; RAISE TOO_MANY _ROWS; EXCEPTION WHEN error THEN ...; WHEN TOO_MANY _ROWS THEN ... ; END;

1.6.5.- SQLCODE.La funcin SQLCODE nos devuelve el nmero del error que se ha producido. Slo tiene valor cuando ocurre un error Oracle. Esta funcin solo se habilita en la zona de Excepciones, ya que es el nico sitio donde se pueden controlan los errores. No se puede utilizar directamente, pero si se puede guardar su valor en una variable.

1.6.6.- SQLERRM.La funcin SQLERRM devuelve el mensaje del error del valor actual del SQLCODE. Al igual que SQLCODE, no se puede utilizar directamente, sino que debemos declarar una variable alfanumrica lo suficientemente grande para contener el mensaje de error.

1.6.7.- Algunos ejemplos de tratamiento de excepciones.1.- Excepciones predefinidas.


EXCEPTION WHEN zero_divide THEN Rollback; WHEN value_error THEN INSERT INTO errores VALUES Commit; WHEN others THEN NULL; END;

Dep. Informtica

22

I.E.S. TRASSIERRA - Crdoba

PL/SQL

2.- Excepcin de usuario.


DECLARE emp_sal temple.salar%TYPE; emp_no temple.numem%TYPE; salario_muy_alto EXCEPTION; BEGIN SELECT numem, salar INTO emp_no, emp_sal FROM temple WHERE nomem = 'DURAN, LIVIA'; IF emp_sal * 1.05 > 4000 THEN RAISE salario_muy_alto ELSE UPDATE temple SET..... END IF; EXCEPTION WHEN NO_DATA_FOUND THEN ROLLBACK; WHEN salario_muy_alto THEN INSERT INTO emp_alto_sal VALUES(emp_no); COMMIT; END;

3.- Cualquier error se graba en el fichero errores.


DECLARE err_num NUMBER; err_msg VARCHAR2(100); BEGIN EXCEPTION WHEN OTHERS THEN err_num := SQLCODE; err_msg := SUBSTR(SQLERRM, 1, 100); INSERT INTO errores VALUES(err_num, err_msg); END;

4.- Ante la inexistencia de cualquier error se manda un mensaje al buffer de salida que ser visualizado al final del programa:
SET SERVEROUTPUT ON SIZE 5000 -- Parametro de entorno que activa el buffer de salida y le -- asigna un tamao de 5000 bytes.

DECLARE cur1 IS .......... no_existe EXCEPTION ; BEGIN OPEN cur1; FETCH cur1 INTO registro; IF cur1%NOTFOUND THEN RAISE no_existe; END IF; ........ EXCEPTION WHEN no_existe THEN DBMS_OUTPUT.PUT_LINE('Empleado inexistente'); END;

-- Envio al buffer del literal

Dep. Informtica

23

I.E.S. TRASSIERRA - Crdoba

PL/SQL

1.7.- EJERCICIOS.1.7.1.- EJERCICIOS RESUELTOS.1.- Programa que haciendo uso de un bucle simple, inserte sucesivamente tuplas con el valor de un contador entre 1 y 14 en la tabla TEMPORAL, previamente creada y con una sola columna: NUMERO number(2).
DROP TABLE temporal ; CREATE TABLE temporal (numero number(2)); DECLARE i number(2):=1; BEGIN LOOP INSERT INTO temporal VALUES (i); i:=i+1; EXIT WHEN i>14; END LOOP; END; / SELECT * FROM temporal ;

2.- Idem al anterior, pero usando un bucle WHILE.


DROP TABLE temporal ; CREATE TABLE temporal (numero number(2)); DECLARE i number(2):=1; BEGIN WHILE i < 15 LOOP INSERT INTO temporal VALUES (i); i:=i+1; END LOOP; END; / SELECT * FROM temporal ;

3.- Idem al anterior, pero usando un bucle FOR.


DROP TABLE temporal ; CREATE TABLE temporal (numero number(2)); DECLARE i number(2):=1; BEGIN FOR I IN 1 .. 14 LOOP INSERT INTO temporal VALUES (i); END LOOP; END; / SELECT * FROM temporal ;

4.- Programa que crea la tabla TEST, con dos nicas columnas numricas de longitud 3: NR1 y NR2. Y posteriormente asigna a NR1 un valor decreciente del 19 hasta el cero, y a NR2 el valor de un contador creciente de 1 a 20. Y ello con el uso de un bucle de cursor.

Dep. Informtica

24

I.E.S. TRASSIERRA - Crdoba

PL/SQL

DROP TABLE test; CREATE TABLE test (nr1 NUMBER(3), nr2 NUMBER(3)) ; BEGIN -- inserta valores en la tabla TEST FOR i IN 1..20 LOOP INSERT INTO test values( NULL, i) ; END LOOP; COMMIT; END; / DECLARE x NUMBER(4) := 0; CURSOR t_cur IS SELECT * FROM test ORDER BY nr2 desc FOR UPDATE OF nr1; BEGIN FOR t_rec IN t_cur LOOP UPDATE test SET nr1 = x WHERE CURRENT OF t_cur; x := x + 1; END LOOP; COMMIT; END; / SELECT * FROM test ; SET ECHO OFF;

1.7.2.- EJERCICIOS PROPUESTOS.1.- Codificar el programa PL/SQL que permita aumentar en un 10% el salario de un empleado cuyo nmero se introduce por teclado. 2.- Modificar el anterior programa para controlar la inexistencia del empleado tecleado, no haciendo nada, solo evitando que el programa finalice con error. 3.- Modificar el anterior programa de forma que si el empleado no existe se visualice un mensaje de error. 4.- Codificar el programa PL/SQL que solicite por pantalla un nmero de departamento y calcule la suma total de los salarios y comisiones de ese departamento. Despus inserte la tupla correspondiente en la tabla TOTALES, previamente creada con la siguiente estructura: deptno total number(3) number(10,2)

Realizar el ejercicio utilizando un bucle simple y tratando el cursor. 5.- Modificar el programa anterior para que en la tabla TOTALES no se inserten los departamentos inexistentes. 6.- Realizar el mismo ejercicio con un bucle sobre el cursor. 7.- Mejorar el programa anterior para que, tanto en caso de inexistencia del departamento como en caso de que ya haya sido introducido (que exista ya en totales), muestre sendos mensajes de error.

Dep. Informtica

25

I.E.S. TRASSIERRA - Crdoba

PL/SQL

8.- A partir de las tablas tdepto y temple, realizar los aumentos de sueldo (columna salar) siguientes: tdepto.nomde % subida FINANZAS 10 PERSONAL 20 RESTO 5 Adems, crear la tabla T-DEP, que contendr, por cada departamento, el nmero de empleados con comisin y sin comisin, y con la siguiente estructura: deptno number(3) sin_comm number(3) con_comm number(3) Controlar el error de Oracle 'dup_val_on_index' insertando un registro en la tabla errores con el formato siguiente: codigo char(10) mens char(100) 9.- Idem al ejercicio anterior, pero si se produce duplicidad de ndices, se acumularn los valores de empleados con comisin y sin comisin al departamento correspondiente de la tabla T_DEP, y cualquier otro error se registrar en la tabla errores.

Dep. Informtica

26

I.E.S. TRASSIERRA - Crdoba

PL/SQL

TEMA 2.- DISPARADORES (TRIGGERS)


2.1.- Disparadores. 2.1.1.- Concepto. 2.1.2.- Sintaxis. 2.1.3.- Nombres correlativos. 2.2.- Componentes de un disparador. 2.2.1.- Instruccin. 2.2.2.- Restriccin. 2.2.3.- Accin. 2.3.- Tipos de disparadores. 2.4.- Modificar y borrar disparadores. 2.5.- Manejo de errores. 2.6.-Ejercicios. 2.6.1.- Ejercicios resueltos. 2.6.2.- Ejercicios propuestos.

2.1.- DISPARADORES .2.1.1.- Concepto.ORACLE permite definir procedimientos almacenados en la base de datos, que estn asociados a una tabla y que son ejecutados implcitamente ("disparados") cuando una instruccin INSERT, UPDATE o DELETE se ejecuta sobre la tabla asociada. Estos procedimientos reciben el nombre de disparadores de la base de datos. Un disparador puede incluir instrucciones SQL e instrucciones PL/SQL y puede invocar a otros procedimientos. Los procedimientos y los disparadores se diferencian en la forma en que son invocados. Mientras un procedimiento lo ejecuta explcitamente un usuario o una aplicacin, un disparador es ejecutado (disparado) implcitamente por Oracle cuando se ejecuta una instruccin INSERT , UPDATE o DELETE de un disparador. La siguiente figura muestra una aplicacin de la base de datos con algunas instrucciones SQL que implcitamente disparan varios disparadores almacenados en la base de datos. Base de datos Aplica
UPDATE ..SET .. INSERT INTO .. DELETE FROM ..
Tabla 1 UPDATE TIGGER BEGIN .... INSERT TIGGER BEGIN .... DELETE TIGGER BEGIN ....

----------------------------------------------

Dep. Informtica

27

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Los disparadores se almacenan en la base de datos, separados de las tablas asociadas a ellos. Slo se pueden definir sobre tablas, y no sobre vistas, sin embargo, los disparadores definidos sobre la tabla o tablas base de una vista son disparados si se ejecuta sobre la vista una instruccin INSERT, UPDATE o DELETE. Para crear un disparador se debe usar ORACLE con la opcin de procedimiento que se activa al ejecutar los scripts DBMSSTDX.SQL y CATPROC.SQL, que en la versin 10g Express Edition se encuentran en C:\oraclexe\app\oracle\product\10.2.0\server\RDBMS\ADMIN, y son cargados automticamente al iniciar una sesin. Si no estuvieran cargados, el administrador debe ejecutarlos. Tambin es necesario tener uno de los siguientes privilegios CREATE TRIGGER o CREATE ANY TRIGGER, que permiten a un usuario crear disparadores en una tabla de su esquema, o bien de cualquier (ANY) esquema. Si el disparador usa instrucciones SQL o invoca a procedimientos o funciones, el usuario que define el disparador debe tener los privilegios necesarios para utilizarlos.

2.1.2.- Sintaxis.Un disparador puede crearse con el DBA Studio o el comando CREATE TRIGGER desde alguna herramienta interactiva (como SQL*PLUS, SQL*DBA). Su sintaxis es: CREATE [OR REPLACE] TRIGGER esquema.disparador {BEFORE / AFTER} { INSERT / DELETE / UPDATE [OF lista_columnas] [ OR INSERT / DELETE / UPDATE [OF lista_columnas] [ OR INSERT / DELETE / UPDATE [OF lista_columnas]]]} ON esquema.tabla [ REFERENCING OLD [AS] antiguo [ NEW [AS] nuevo ] ] [ FOR EACH ROW [WHEN (condicin)] ] bloque_pl/sql / OR REPLACE: Reemplaza el disparador si ya existe. Se usa para cambiar la definicin de un disparador que ya exista sin tener que borrarlo antes. Es el nombre del esquema que contiene al disparador. Si se omite, se crea el disparador en el esquema del usuario. Es el nombre del disparador. Ha de ser nico con respecto a otros disparadores en el esquema, aunque no tiene porque ser nico con respecto a otros objetos del esquema (tablas, vistas y procedimientos). Indica que ORACLE dispara el disparador antes de ejecutar la instruccin disparador (INSERT, UPDATE o DELETE).

esquema:

disparador:

BEFORE:

Dep. Informtica

28

I.E.S. TRASSIERRA - Crdoba

PL/SQL

AFTER:

Indica que el disparador se dispara despus de ejecutar la instruccin disparador (INSERT, UPDATE o DELETE). Indica que se dispara cuando con DELETE se borre una fila de la tabla. Indica que se dispara cuando con INSERT se aada una fila en la tabla. Indica que se dispara el disparador siempre que con UPDATE se modifique un valor en una de las columnas especificadas en la clusula OF. Si se omite la clusula OF, el disparador se dispara siempre que una instruccin UPDATE modifique un valor en alguna columna de la tabla. Especifica el esquema y nombre de tabla sobre la que se crea el disparador. No se puede crear un disparador sobre una tabla en el esquema SYS.

DELETE: INSERT: UPDATE OF:

ON:

REFERENCING: Especifica correlacin de nombres. Se puede usar correlacin de nombres en los bloques PL/SQL y en la clusula WHEN de un disparador para referenciar los valores viejos y nuevos de la fila actual. Los nombres correlativos por defecto son OLD y NEW. FOR EACH ROW: Designa que el disparador ser un disparador fila. ORACLE dispara un disparador fila una vez por cada fila afectada por el disparador. Si se omite esta clusula, el disparador ser un disparador instruccin. ORACLE dispara un disparador instruccin slo una vez cuando la instruccin disparador se ejecuta. WHEN : Indica la restriccin disparador. Esta restriccin contiene una condicin SQL que debe satisfacerse para disparar el disparador. Esta condicin debe contener nombres correlativos y no puede contener una consulta. Solo puede especificarse una restriccin para un disparador fila. La condicin se evala para cada fila que se vea afectada por la instruccin disparador. Es el bloque PL/SQL que ORACLE ejecuta cuando dispara el disparador. El bloque PL/SQL de un disparador no puede contener las siguientes instrucciones SQL: COMMIT , ROLLBACK, y SAVEPOINT.

bloque_Pl/sql:

ADVERTENCIAS: 1.- El comando CREATE TRIGGER debe terminar con una barra oblicua (/). 2.- Crear un disparador cuya sintaxis sea correcta no dando errores al ser compilado, no significa que sea semnticamente correcto. De hecho, el error ms frecuente en el uso de disparadores aparece al ejecutarlos y tratar de acceder o referenciar directa o indirectamente con algn tipo de operacin a la misma tabla que activ el disparador, cosa que est totalmente prohibida. Las siguientes vistas del diccionario de datos muestran informacin sobre los disparadores: - USER_TRIGGERS - ALL_TRIGGERS - DBA_TRIGGERS (vase el ejercicio resuelto 1):

Dep. Informtica

29

I.E.S. TRASSIERRA - Crdoba

PL/SQL

2.1.3.- Nombres correlativos .En los disparadores de fila, existen dos nombres correlativos, OLD y NEW, para cada columna de la tabla que se est modificando. Para referenciar a los valores nuevos de la columna se utiliza el calificador NEW antes del nombre de la columna, y para los valores antiguos se usa el calificador OLD. Dependiendo del tipo de instruccin disparador puede suceder que algunos nombres correlativos no tengan significado: Un disparador disparado por una instruccin INSERT tiene acceso solamente a los valores nuevos de la columna. Los valores viejos son NULL. Un disparador disparado por una instruccin UPDATE tiene acceso a los valores viejos y nuevos de la columna para los disparadores fila BEFORE y AFTER. Un disparador disparado por una instruccin DELETE tiene acceso solamente a los valores viejos de la columna. Los valores nuevos son NULL.

Los nombres correlativos tambin se pueden usar en el predicado de una clusula WHEN. Cuando los calificadores NEW y OLD se usan en el cuerpo de un disparador (bloque PL/SQL) deben ir precedidos por dos puntos (:), que no son permitidos cuando se utilizan en la clusula WHEN o en la opcin REFERENCING OLD...

2.2.- COMPONENTES DE UN DISPARADOR.Un disparador consta de tres componentes: Instruccin disparador, Restriccin disparador, y Accin disparador.

2.2.1.- Instruccin disparador.Una instruccin disparador es la instruccin SQL que causa que un disparador sea ejecutado. Puede ser una instruccin INSERT, UPDATE o DELETE para una tabla especfica. Por ejemplo, la instruccin disparador: UPDATE OF exis ON articulos significa que cuando la columna exis de una fila en la tabla articulos se modifique, se ejecuta el disparador (se dispara). Cuando la instruccin disparador es una instruccin UPDATE se puede incluir una lista de columnas para identificar qu columnas deben ser modificadas para que se dispare el disparador; como las instrucciones INSERT y DELETE afectan a filas completas de la tabla, no es necesario especificar una lista de columnas para estas opciones. Una instruccin disparador puede contener mltiples instrucciones DML: ... INSERT OR UPDATE OR DELETE ON asegurados ...
Dep. Informtica

30

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Lo que significa que cuando una instruccin INSERT, UPDATE o DELETE se ejecuta sobre la tabla asegurados, se dispara el disparador. Cuando se utilizan mltiples tipos de instrucciones DML en un disparador, se pueden utilizar predicados condicionales (INSERTING, DELETING, y UPDATING) para detectar qu instruccin ha disparado el disparador. Por lo tanto, se pueden crear disparadores que ejecuten distintos fragmentos de cdigo segn la instruccin disparador que ha disparado el disparador. En el siguiente ejemplo se supone que la tabla tdepto tiene una nueva columna: total_sal, con el total de salarios (no comisiones) de los empleados de ese departamento.
CREATE OR REPLACE TRIGGER total_salario AFTER DELETE OR INSERT OR UPDATE OF numde, salar ON temple FOR EACH ROW BEGIN /* se asume que numde y salar son NO NULOS */ IF DELETING OR (UPDATING AND :old.numde != :new.numde) THEN UPDATE tdepto SET total_sal = total_sal - :old.salar WHERE numde = :old.numde ; END IF ; IF INSERTING OR (UPDATING AND :old.numde != :new.numde) THEN UPDATE tdepto SET total_sal = total_sal + :new.salar WHERE numde = :new.numde ; END IF ; IF (UPDATING AND :old.numde = :new.numde) THEN UPDATE tdepto SET total_sal = total_sal - :old.salar + :new.salar WHERE numde = :new.numde ; END IF ; END; / Ntese que si se modifica el departamento de un empleado se cumplen las dos primeras condiciones.

2.2.2.- Restriccin disparador.Una restriccin disparador especifica un predicado o condicin que debe ser verdadera para que el disparador se dispare. La accin disparador no se ejecuta en caso contrario. La restriccin disparador es una opcin disponible para los disparadores fila. Se especifica utilizando la clusula WHEN.

2.2.3.- Accin disparador.Una accin disparador es el procedimiento (bloque PL/SQL) que contiene las instrucciones SQL y el cdigo PL/SQL que se ejecuta cuando una instruccin disparador es ejecutada y la restriccin disparador (si existe) se evala como verdadera. Una accin disparador puede contener instrucciones SQL y PL/SQL, puede definir elementos del lenguaje PL/SQL (variables, constantes, cursores, excepciones, etc.), y puede llamar a otros procedimientos.
Dep. Informtica

31

I.E.S. TRASSIERRA - Crdoba

PL/SQL

2.3.- TIPOS DE DISPARADORES.Usando las opciones BEFORE/AFTER y FOR EACH ROW, se pueden crear cuatro tipos bsicos de disparadores. El tipo de un disparador determina: - cundo dispara ORACLE el disparador en relacin a la instruccin disparador, y - cuntas veces dispara ORACLE el disparador. La siguiente tabla describe cada tipo de disparador, sus propiedades y las opciones utilizadas para crearlos. Opcin
BEFORE FOR EACH STATEMENT ( Instruccin) FOR EACH ROW (Fila)

La accin disparador se ejecuta antes de La accin disparador se ejecuta antes modificar cada fila afectada por la de ejecutar la instruccin disparador. instruccin disparador. La accin disparador se ejecuta despus de ejecutar la actualizacin (instruccin disparador). La accin disparador se ejecuta despus de modificar cada fila afectada por la instruccin disparador.

AFTER

Los disparadores de tipo BEFORE se usan normalmente en las siguientes situaciones: Cuando la accin disparador debe determinar si se ejecuta o no la instruccin disparador. Para obtener valores de columnas especficos antes de ejecutar una instruccin disparador INSERT o UPDATE.

Los disparadores de tipo AFTER se usan normalmente en las siguientes situaciones: Cuando se quiere que la instruccin disparador termine su ejecucin antes de ejecutar la accin disparador. Si hay definido un disparador BEFORE, se puede definir un disparador AFTER que realice acciones diferentes sobre la misma instruccin disparador.

El siguiente ejemplo muestra distintas posibilidades de disparador. Aparece una nueva instruccin que permite crear un paquete (objeto que permite agrupar procedimientos, funciones y datos como una unidad y que estudiaremos en el prximo tema) de nombre stat y que solo contiene la declaracin pblica de la variable numrica stat.rowent.
DROP TABLE stat_tab ; CREATE TABLE Stat_tab (utype CHAR(8), rowent INTEGER, uhour INTEGER); CREATE OR REPLACE PACKAGE stat IS rowent INTEGER; END; / CREATE TRIGGER bt BEFORE UPDATE OR DELETE OR INSERT ON sal BEGIN stat.rowent :=0; END; / Dep. Informtica

32

I.E.S. TRASSIERRA - Crdoba

PL/SQL

CREATE TRIGGER rt BEFORE UPDATE OR DELETE OR INSERT ON sal FOR EACH ROW BEGIN stat.rowent := stat.rowent + 1 ; END ; / CREATE TRIGGER at AFTER UPDATE OR DELETE OR INSERT ON sal DECLARE typ CHAR(8); hour NUMBER ; BEGlN IF updating THEN typ :='update' ; END IF ; IF deleting THEN typ :='delete' ; END IF ; IF inserting THEN typ :='insert' ; END IF ; Hour := TRUNC( (SYSDATE- TRUNC( SYSDATE ) )*24 ) ; UPDATE stat_tab SET rowent = rowent + stat.rowent WHERE utype = typ AND uhour = hour; IF SQL%ROWCOUNT = 0 THEN INSERT INTO stat_tab VALUES ( typ, stat.rowent, hour ); END IF ; EXCEPTION WHEN dup_val_on_index THEN UPDATE stat_tab SET rowent = rowent + stat.rowent WHERE utype = typ AND uhour = hour ; END ; /

2.4.- MODIFICACIN Y BORRADO DE DISPARADORES.Un disparador no se puede modificar explcitamente, debe reemplazarse con una nueva definicin de disparador. Para reemplazar un disparador en CREATE TRIGGER se incluye la opcin OR REPLACE que permite crear una nueva versin de un disparador que ya existe en la base de datos, sin que se vean afectados los privilegios que tena el disparador antiguo. En caso de que en vez de usar la opcin OR REPLACE, borremos el disparador y posteriormente lo creemos de nuevo con las modificaciones necesarias, todos los privilegios del disparador antiguo se habrn borrado y debern volverse a otorgar al disparador nuevo. Un disparador se puede encontrar habilitado o inhabilitado. Solo en el caso de estar habilitado, ORACLE lo dispara siempre que se ejecuta una instruccin disparador y la restriccin disparador (si existe) es evaluada TRUE. Cuando se crea un disparador, ORACLE automticamente lo habilita. Es til inhabilitar temporalmente un disparador cuando: Se referencia un objeto que no est disponible. Se va a cargar una gran cantidad de datos y se quiere realizar rpidamente sin que se disparen los disparadores. Se est recargando datos.

Dep. Informtica

33

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Para habilitar o inhabilitar disparadores usa el comando ALTER TRIGGER. Su sintaxis es: ALTER TRIGGER disparador { ENABLE / DISABLE} ; Otra forma es con el comando ALTER TABLE con las clusulas DISABLE y ENABLE y la opcin ALL TRIGGERS de este comando. Ejemplo:
ALTER TABLE temple DISABLE ALL TRIGGERS

Para habilitar o inhabilitar disparadores utilizando el comando ALTER TABLE, se debe ser el propietario de la tabla, tener el privilegio de objetos ALTER para la tabla, o tener el privilegio del sistema ALTER ANY TABLE. Para habilitar o inhabilitar disparadores utilizando el comando ALTER TRIGGER, se debe ser el propietario del disparador o tener el privilegio del sistema ALTER ANY TRIGGERS. Para borrar un disparador, ste debe estar en nuestro esquema o disponer del privilegio del sistema DROP ANY TRIGGER. Se usa el comando DROP TRIGGER, cuya sintaxis es: DROP TRIGGER <disparador > ;

2.5.- MANEJO DE ERRORES.Si durante la ejecucin de un disparador se produce un error predefinido en el sistema o definido por el usuario, entonces se anulan todas las actualizaciones realizadas por la accin disparador as como el evento que la activ. La sentencia RAISE_APPLICATION_ERROR (num_error, 'mensaje') provoca la ocurrencia del error de nmero interno num_error y envia al usuario el mensaje 'mensaje'. (num_error debe ser un nmero negativo comprendido entre 20000 y 20999). Un ejemplo de su uso puede verse en el ejercicio resuelto 2.

2.6.-EJERCICIOS.2.6.1.- Ejercicios resueltos.1.- Ejemplo de uso de las tablas del diccionario de datos. La siguiente sentencia crea un disparador llamado DEP_SET_NULL que controla que antes de borrar una fila de la tabla DEPT o modificar la clave primaria (deptno) de DEPT, se pongan a NULL todos los valores relacionados de la tabla EMP:
CREATE TRIGGER dep_set_null AFTER DELETE OR UPDATE OF deptno ON dept FOR EACH ROW BEGIN IF DELETING OR UPDATING AND :old.deptno != :new.deptno THEN UPDATE emp SET emp.deptno = NULL WHERE emp.deptno = :old.deptno ; END IF ; END ; / Dep. Informtica

34

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Las siguientes consultas muestran informacin sobre el disparador DEP_SET_NULL:


SELECT FROM WHERE Rdo: trigger_type, triggering_event, table_name user_triggers trigger_name='DEP_SET_NULL' ; TRIGGERING_EVENT TABLE_NAME UPDATE OR DELETE DEPT .

TRIGGER_TYPE AFTER EACH ROW SELECT FROM WHERE

trigger_body user_triggers trigger_name = 'DEP_SET_NULL' ; .

Rdo:

TRIGGER_BODY BEGIN

IF UPDATING AND :old.deptno != :new.deptno THEN UPDATE emp SET emp.deptno = NULL WHERE emp.deptno = :old.deptno ; END IF ; END ;

El cuerpo del disparador se almacena en la tabla del diccionario en la columna trigger_body de tipo long. Como la longitud de visualizacin de los campos long est predefinida en 80 caracteres, para ver mejor estos resultados deberamos aumentarla, bien en el men Opciones y Entorno del editor de SQL*Plus, o mediante el comando SET LONG 4000, por ejemplo. 2.- Crear el disparador instruccin BEFORE llamado EMP_DATE_CAMBIOS que controlar que las modificaciones en los registros de empleados de la tabla EMP slo se realicen en horas y das laborables.
CREATE TRIGGER scott.emp_date_cambios BEFORE DELETE OR INSERT OR UPDATE ON scott.emp BEGIN /*Si hoy es sabado o domingo se devuelve un error*/ IF ( TO_CHAR( sysdate, 'DY' ) = 'SB' OR TO_CHAR( sysdate, 'DY' ) = 'DOM' ) THEN RAISE_APPLICATION_ERROR( -20501, 'No pueden actualizarse datos de empleados en fin de semana') ; END IF; /* Si la actualizacin no se hace entre las 8.00 y las 18.00 h, se devuelve un error */ IF ( TO_CHAR( sysdate, 'HH24' )< 8 OR TO_CHAR( sysdate, 'HH24' ) >= 18 ) THEN RAISE_APPLICATION_ERROR( -20502, 'Solo se puede actualizar datos de empleados durante las horas de trabajo') ; END IF;

END ; /

ORACLE dispara el disparador siempre que una instruccin INSERT, UPDATE o DELETE afecte a la tabla EMP del esquema SCOTT. El disparador realiza las siguientes operaciones: 1.- Si la modificacin se intenta realizar un sbado o un domingo, el disparador no permite la modificacin y da un mensaje de error. 2.- Si la hora en que se intenta modificar no est entre las 8:00 AM y las 6:00 PM, el disparador da un mensaje de error. El/la alumn@ debera adaptar este disparador a las tablas ejemplo, mejorndolo para que tambin evite las actualizaciones en los das de fiesta. La validacin se hara consultando una tabla que se actualizara anualmente y que contendra las fechas de las fiestas (das no laborables), y que deber crear el alumnado.
Dep. Informtica

35

I.E.S. TRASSIERRA - Crdoba

PL/SQL

3.- Este ejemplo crea un disparador fila BEFORE llamado VALIDA_SALARIO en el esquema SCOTT. El disparador garantizar que siempre que un empleado nuevo se aada a la tabla de empleados EMP, o se modifique el salario o el tipo de trabajo de un empleado, el salario del empleado se mantiene dentro del rango establecido en SAL_GUIDE para su categora. La tabla SAL_GUIDE deberemos crearla:
DROP TABLE SAL_GUIDE; CREATE TABLE SAL_GUIDE (JOB VARCHAR2(9) primary key, MINSAL NUMBER(7,2), MAXSAL NUMBER(7,2) ) ; INSERT INTO SAL_GUIDE VALUES ('CLERK', 800, 1300); INSERT INTO SAL_GUIDE VALUES ('ANALYST', 3000, 3500); INSERT INTO SAL_GUIDE VALUES ('SALESMAN', 1250, 1600); INSERT INTO SAL_GUIDE VALUES ('MANAGER', 2450, 2975); INSERT INTO SAL_GUIDE VALUES ('PRESIDENT', 5000, 5500); CREATE OR REPLACE TRIGGER scott.valida_salario BEFORE INSERT OR UPDATE OF sal, job ON scott.emp FOR EACH ROW DECLARE v_minsal sal_guide.minsal%TYPE; v_maxsal sal_guide.maxsal%TYPE; BEGIN /* Calcula en menor y mayor salario de esa categora de la tabla SAL_GUIDE*/ SELECT minsal, maxsal INTO v_minsal, v_maxsal FROM sal_guide WHERE job = :new.job; /* Si el salario del empleado cae fuera del rango, se provoca un error*/ IF (:new.sal< v_minsal OR :new.sal > v_maxsal) THEN RAISE_APPLICATION_ERROR( -20601, 'Salario ' ||:new.sal || ' fuera de rango de la categoria ' || :new.job || ' para el empleado ' || :new.ename ) ; END IF; END; /

ORACLE dispara el disparador siempre que se ejecute una de las siguientes instrucciones: Una instruccin INSERT que aada una fila a la tabla EMP. Una instruccin UPDATE que modifique los valores de las columnas SAL o JOB de la tabla EMP.

El disparador realiza las siguientes operaciones: 1.- Consulta la tabla de salarios para obtener el salario mximo y mnimo. 2.- Compara el salario del empleado con el salario mximo y mnimo. 3.- Si el salario del empleado no est dentro del rango, el disparador d un mensaje de error y aborta la actualizacin.

2.6.2.- Ejercicios propuestos.1.- Crear el disparador DIR_SET_NULL, que controle que antes de borrar un empleado que sea director ponga a nulo el campo direc del departamento o departamentos de los que era director, y que si se modifica la clave primaria de un director, actualice automticamente el campo direc de los departamentos en los que l es director.

Dep. Informtica

36

I.E.S. TRASSIERRA - Crdoba

PL/SQL

2.- Crear el disparador DEP_DEL_CASCADE que permita que antes de borrar un departamento se borren todos los empleados del mismo.

3.- Sobre la tabla TDEPTO, codificar el disparador de nombre TDEPTO_PRESU_SAL que impedir que el presupuesto de un departamento sea inferior a la suma de los salarios y comisiones de los empleados de ese departamento. Si una actualizacin intenta violar la anterior restriccin, el disparador devolver el cdigo de error nmero -20112 y el mensaje 'El presupuesto no puede ser inferior a la suma de los sueldos de sus empleados'.

4.- Modificar el disparador VALIDA_SALARIO del ejercicio resuelto n 3 para que vele por que se cumpla que los salarios de todos los empleados salvo el presidente: - Se encuentren comprendidos entre el valor de minsal y maxsal de su categora (JOB). - Que no disminuyan. - Que no se vean incrementados en ms del 10 % de una vez. El tratamiento de los errores se har con RAISE_APPLICATION_ERROR.

5.- Crear el disparador before CHEQ_SALGUIDE que velar por que se cumpla: - Que no se borrar ninguna tupla de SAL_GUIDE si hay algn empleado con su salario comprendido entre sus lmites. En cuyo caso se lanzar el error 20325 y un mensaje indicativo. Que una actualizacin de los valores de minsal o maxsal de SAL_GUIDE no provocar que queden empleados de esa categora con sus salarios fuera de rango, en cuyo caso se restablecern los valores antiguos.

6.- Crear el disparador DIRPRO que evite que un director lo sea en propiedad en ms de un departamento. Caso de que se intente violar la anterior restriccin, el disparador devolver el cdigo de error -20800, y el mensaje 'El jefe ya lo es en propiedad de otro departamento.'

Dep. Informtica

37

I.E.S. TRASSIERRA - Crdoba

PL/SQL

TEMA 3.- PROCEDIMIENTOS, FUNCIONES Y PAQUETES.3.1.- Introduccin. 3.2.- Procedimientos. 3.2.1.- Creacin y borrado. 3.2.2.- Tipos de argumentos: in, out, in out. 3.2.3.- Polimorfismo. 3.3.- Funciones. 3.3.1.- Creacin y borrado. 3.3.2.- Recursividad. 3.3.3.- Funciones PL/SQL en sentencias SQL. 3.4.- Paquetes. 3.4.1.- Creacin y borrado. 3.4.2.- La depuracin 3.5.- Ventajas de los procedimientos, funciones y paquetes. 3.6.- Manejo de excepciones. 3.7.- Ejercicios. 3.7.1.- Ejercicios resueltos. 3.7.2.- Ejercicios propuestos.

3.1.- INTRODUCCIN.Adems de los disparadores ya vistos, Oracle permite desarrollar subprogramas, es decir, agrupar una serie de definiciones y sentencias para ejecutarlas bajo un nico nombre, estos subprogramas son: procedimientos, funciones y paquetes. Los procedimientos son trozos de cdigo que realizan un trabajo sobre unos argumentos de entrada que son opcionales y depositan unos valores sobre unos argumentos de salida, tambin opcionales. Las funciones son similares a los procedimientos con la diferencia de que devuelven siempre un valor. Los paquetes son agrupaciones de procedimientos, funciones y definiciones de datos; es lo que en otros entornos de programacin se conoce como librera. Tanto los procedimientos como las funciones disponen de la siguiente estructura: cabecera del procedimiento o funcin declaracin de variables, cursores, sobprocedimientos, etc. BEGIN zona ejecutable EXCEPTION manipuladores de excepciones END [nombre_procedimiento] ;
Dep. Informtica

38

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Desarrollar un subprograma para almacenar un bloque PL/SQL en la base de datos es muy til cuando es necesario ejecutarlo repetidamente. Un subprograma puede invocarse desde multitud de entornos: SQL *Plus, Oracle Forms, otro subprograma, y desde cualquier otra herramienta o aplicacin Oracle. Para crear un procedimiento o funcin hay que seguir los pasos siguientes: 1.- Escribir el texto de la sentencia CREATE PROCEDURE / FUNCTION y guardarlo en un fichero de texto con la extensin .SQL. Mientras se compone esta sentencia se debe ir pensando en el manejo de errores de ejecucin. Es importante que los ficheros de texto acaben con una lnea que contenga el carcter '/' como nico carcter de la columna uno. Por ejemplo (la tabla alumnos debe estar creada):
CREATE PROCEDURE altalum (v_num number, v_nom varchar2) IS BEGIN INSERT INTO alumnos VALUES (v_num, v_nom); END ; /

2.- Compilar el fichero de texto ejecutndolo con el comando START <nombre_fichero>. La ejecucin convierte el cdigo fuente en cdigo compilado y lo almacena en la base de datos. La ejecucin se realiza desde SQL*Plus u otro interfaz que permita sentencias LDD. El cdigo fuente se almacena siempre en la B.D. El cdigo compilado solo se almacena si no se han producido errores. En este caso pueden verse los errores de compilacin con el comando SHOW ERRORS. 3.- Invocar el procedimiento o funcin desde un entorno Oracle con el comando EXECUTE. Por ejemplo:
EXECUTE altalum (1, 'JOS ANGEL FERNNDEZ') ;

La mayora de las aplicaciones Oracle, entre ellas SQLPls, permiten el paso de parmetros posicionalmente o por asociacin de nombres (de forma similar a la apertura de cursores parametrizados), incluso de forma mixta. Supongamos que tenemos las variables v_a de tipo number y v_b de tipo char:
SQL>variable v_a number; SQL>variable v_b varchar2(50); SQL>execute :v_a:=2; SQL>execute :v_b:= 'ANA PALACIOS';

Las siguientes son formas vlidas de invocacin:


EXECUTE altalum(:v_a, :v_b); EXECUTE altalum(:v_a+1 , 'MARIA VALVERDE'); EXECUTE altalum(3, 'JOSE SANTAELLA');

Dep. Informtica

39

I.E.S. TRASSIERRA - Crdoba

PL/SQL

3.2.- PROCEDIMIENTOS.Un procedimiento es un subprograma que realiza una accin especfica. Pueden recibir parmetros pero que no devuelven ningn valor.

3.2.1.- Creacin y borrado.Se crea un nuevo procedimiento con la sentencia CREATE PROCEDURE, que declara una lista de argumentos y define la accin a ejecutar por el bloque PL/SQL estndar. Sintaxis: CREATE [OR REPLACE] PROCEDURE nombre_proc ( argumento [tipo_arg] tipo_dato [,argumento [tipo] tipo_dato ] .... ) AS [IS] bloque PL/SQL dnde: nombre_proc argumento tipo_arg tipo_dato Es el nombre del procedimiento. Es el nombre del argumento. Indica el tipo de argumento: IN (por defecto), OUT o IN OUT. El tipo de dato del argumento sin precisin.

bloque PL/SQL El cuerpo procedural que define la accin a realizar. La opcin REPLACE se especifica cuando se desea reemplazar un procedimiento del mismo nombre sin necesidad de borrarlo previamente. AS e IS son equivalentes. El bloque PL/SQL debe comenzar con la palabra BEGIN o con una declaracin de variable local. Nunca debe comenzar con DECLARE. Esta diferencia con los bloques PL/SQL annimos ocurre porque AS (o IS) llevan implcito el DECLARE. El procedimiento debe finalizar con la palabra END seguida opcionalmente por el nombre del procedimiento y un punto y coma (;). Para borrar un procedimiento usaremos la sentencia: DROP PROCEDURE nombre_proc ;

3.2.2. Tipos de Argumentos.El nmero de argumentos y sus tipos de datos deben corresponderse con los de los parmetros que se le pasan al procedimiento, pero los nombres puede ser diferentes. Esto permite que el mismo procedimiento pueda invocarse desde diferentes lugares. Para transferir valores desde y hacia el entorno de llamada a travs de argumentos debemos escoger entre uno de los tres tipos de argumento: In, Out e In Out.

Dep. Informtica

40

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Tipo IN

Descripcin Se usa para pasar parmetros por valor. El parmetro acta como constante por lo que no se le puede asignar ni cambiar su valor dentro del procedimiento. Este parmetro puede referenciarse en la llamada mediante una constante, variable inicializada o expresin. Permite devolver valores desde el procedimiento al entorno de llamada. El argumento correspondiente acta como una variable no inicializada, por lo que dentro del subprograma solo puede asignrsele valores, no pudiendo aparecer en la parte derecha de una expresin. En la invocacin del subprograma, este parmetro solo puede sustituirse por una variable. Se usa para pasar parmetros por referencia y recogerlos del procedimiento o funcin. El argumento correspondiente acta como una variable inicializada, por lo que se le puede cambiar su valor y usarse en la parte derecha de las expresiones. En la llamada, este parmetro solo puede sustituirse por una variable.

OUT

IN OUT

Los parmetros pueden declararse para que tomen valores por defecto, veamos como:
CREATE PROCEDURE prueba(v1 varchar2 default 'Sin Nombre', v2 number default 5) is ..

Las llamadas vlidas que podemos hacer a este procedimiento son las siguientes:
EXECUTE prueba; EXECUTE prueba ('Paco'); EXECUTE prueba ('Marta', 9); /* equivalente a execute prueba('Sin Nombre', 5);*/ /* equivalente a execute prueba('Paco', 5); */

Si quisiramos darle un valor a v2 y tomar el de v1 por defecto, deberamos usar la notacin nominal:
EXECUTE prueba(v2=>8);

Es til y aconsejable declarar argumentos y variables utilizando los atributos %TYPE o %ROWTYPE. Veamos algunos ejemplos del uso de los tipos de argumentos:

Argumentos IN Almacenar informacin acerca de un nuevo empleado:


CREATE OR REPLACE PROCEDURE alta_emp (v_emp_numero IN temple.numem%TYPE , v_emp_departa IN temple.numde%TYPE , v_emp_telefono IN temple.extel%TYPE , v_emp_fechanac IN temple.fecna%TYPE , v_emp_fechaing IN temple.fecin%TYPE , v_emp_salario IN temple.salar%TYPE , v_emp_comision IN temple.comis%TYPE , v_emp_hijos IN temple.numhi%TYPE , v_emp_nombre IN temple.nomem%TYPE ) IS

Dep. Informtica

41

I.E.S. TRASSIERRA - Crdoba

PL/SQL

BEGIN INSERT INTO temple VALUES (v_emp_numero, v_emp_departa, v_emp_telefono, v_emp_fechanac, v_emp_fechaing, v_emp_salario, v_emp_comision, v_emp_hijos, v_emp_nombre) ; COMMIT WORK ; END alta_emp ; /

Recordemos que la orden show errors es muy til para depurar el cdigo. Si todo va bien nos aparecer el mensaje de Procedimiento creado. Ahora para ejecutarlo debemos escribir la sentencia execute, por ejemplo:
EXECUTE alta_emp(555, 111, 780, '24/06/1979', sysdate, 777, null, 3, 'ARGUDO, MANUEL')

lo que provocar la ejecucin del procedimiento y por ende el alta del empleado tecleado. Se pueden eliminar valores innecesarios de entrada a los procedimientos derivando dichos valores internamente dentro del procedimiento o relacionndolos con un valor por defecto de columna al definir la tabla. Por ejemplo para: Generar la clave primaria de una tabla utilizando un generador de se secuencia. Registrar el usuario partiendo de la variable USER. Registrar la fecha actual basndose en la variable SYSDATE. Almacenar valores por defecto, cuando sea apropiado. Utilizar reglas de negocio para calcular valores de entrada de forma automtica utilizando una frmula.

El siguiente ejemplo elimina los valores de entrada innecesarios en una rutina de altas de empleados, dejando solo como variables de entrada la informacin esencial:
CREATE OR REPLACE PROCEDURE alta_emp (v_emp_numero v_emp_departa v_emp_telefono v_emp_fechanac v_emp_hijos v_emp_nombre v_emp_fechaing temple.fecin%TYPE ; v_emp_salario temple.salar%TYPE ; v_emp_comision temple.comis%TYPE ; BEGIN v_emp_fechaing := SYSDATE ; IF (v_emp_departa IN (110, 111, 112)) THEN v_emp_comision := 0 ; ELSE v_emp_comision := NULL; END IF; IN temple.numem%TYPE , IN temple.numde%TYPE , IN temple.extel%TYPE , IN temple.fecna%TYPE , temple.numhi%TYPE , temple.nomem%TYPE ) IS

SELECT min(salar) INTO v_emp_salario FROM temple WHERE numde = v_emp_departa ; INSERT INTO temple VALUES (v_emp_numero, v_emp_departa, v_emp_telefono, v_emp_fechanac, v_emp_fechaing, v_emp_salario, v_emp_comision, v_emp_hijos, v_emp_nombre) ; COMMIT WORK ; END alta_emp ; /

Dep. Informtica

42

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Argumentos OUT Recuperar valores desde un procedimiento a el entorno de llamada a travs de argumentos OUT. En el siguiente ejemplo, recuperar informacin sobre un empleado.
CREATE OR REPLACE PROCEDURE consulta_emp (v_emp_numero IN temple.numem%TYPE , v_emp_nombre OUT temple.nomem%TYPE , v_emp_salario OUT temple.salar%TYPE , v_emp_comision OUT temple.comis%TYPE) IS BEGIN SELECT nomem, salar, comis INTO v_emp_nombre, v_emp_salario, v_emp_comision FROM temple WHERE numem = v_emp_numero; END consulta_emp; /

Para invocar a este procedimiento hay que pasarle todos los parmetros, ya sean IN, OUT o IN OUT. Aunque lo habitual es invocar a un procedimiento desde un bloque PL, el siguiente ejemplo muestra la forma de invocarlo con argumentos IN y OUT desde el prompt de SQL:
SQL> var x varchar2(50); SQL> var y number; SQL> var z number; SQL> execute consulta_emp(390, :x, :y, :z);

Incluso podramos ver el contenido de las variables OUT modificadas por el procedimiento.
SQL> PRINT :x; .......

Argumentos IN OUT El siguiente ejemplo transforma una secuencia de nueve dgitos en un numero de telfono. Recupera valores desde el entorno de trabajo hacia el procedimiento, y devuelve los diferentes valores posibles desde el procedimiento al entorno de llamada utilizando argumentos IN OUT.
CREATE OR REPLACE PROCEDURE con_guion (v_telf_no IN OUT VARCHAR2 ) IS BEGIN v_telf_no := SUBSTR (v_telf_no, 1, 3) || '-' || SUBSTR(v_telf_no, 4); END con_guion; /

El valor de un argumento tipo IN OUT debe darlo el procedimiento, bien mediante una sentencia de asignacin, o bien por mediante una sentencia SELECT .. INTO.

3.2.3.- Polimorfismo.El polimorfismo es una caracterstica que permite definir ms de un objeto con el mismo nombre. En PL/SQL podemos definir distintas funciones y procedimientos que compartan un nombre comn, pero han de diferenciarse en la cantidad o tipo de los parmetros que reciben o devuelven.

Dep. Informtica

43

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Ejemplo:
PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS BEGIN FOR i IN 1..n LOOP tab(i) := SYSDATE; END LOOP; END initialize; PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS BEGIN FOR i IN 1..n LOOP tab(i) := 0.0; END LOOP; END initialize;

Estos procedimientos slo difieren en el tipo de dato del primer parmetro. Para efectuar una llamada a cualquiera de ellos, se puede implementar lo siguiente:
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 versin initialize(comm_tab, indx); -- llama a la segunda versin ... END;

3.3.- FUNCIONES.Las funciones son subprogramas que reciben parmetros y devuelven un nico valor. Desde el punto de vista estrictamente sintctico, un procedimiento conteniendo un argumento OUT puede ser reescrito como una funcin, y uno que contenga argumentos OUT mltiples puede reescribirse como una funcin que devuelva un argumento tipo registro. El como de largo ser la invocacin de la rutina, es lo que determina que su implementacin se realice mediante un procedimiento o una funcin. Lo normal ser que sus argumentos sean de tipo IN. Los de tipo OUT o IN OUT, aunque son vlidos dentro de las funciones, se utilizan raras veces.

3.3.1.- Creacin y borrado.La sentencia CREATE FUNCTION es idntica a CREATE PROCEDURE, excepto por la clusula extra RETURN. Con ella se genera una nueva funcin que declara una lista de argumentos, declara el argumento RETURN y define la accin a realizar por el bloque PL/SQL estndar. Su sintaxis es:

Dep. Informtica

44

I.E.S. TRASSIERRA - Crdoba

PL/SQL

CREATE [OR REPLACE] FUNCTION nombre_func (argumento [tipo] tipo_dato [,argumento [tipo] tipo_dato ] ..) RETURN tipo_dato AS [IS] bloque PL/SQL dnde los elementos significan lo mismo que en la sentencia CREATE PROCEDURE, salvo: RETURN tipo_dato que indica el tipo de dato devuelto por la funcin. Este tipo de dato no puede incluir escala o precisin. En una funcin puede haber varias sentencias RETURN, con la nica condicin de que al menos haya una. Ejemplo: recuperar el salario de un empleado.
CREATE OR REPLACE FUNCTION tomar_salario (v_emp_numero IN temple.numem%TYPE) RETURN number IS v_emp_salario temple.salar%TYPE := 0; BEGIN SELECT FROM WHERE RETURN END tomar_salario; / salar INTO v_emp_salario temple numem = v_emp_numero; (v_emp_salario);

Para usar la funcin podremos hacer:


SELECT nomem, tomar_salario(numem) FROM temple;

Al igual que los procedimientos, las funciones pueden invocarse pasndoles los parmetros con notacin posicional, nominal o mixta, y se les pueden asignar valores por defecto. Para borrar una funcin usaremos la sentencia: DROP FUNCTION nombre_func ;

3.3.2.- Recursividad.Las funciones pueden invocarse recursivamente, tanto en autollamada como en interllamada. Veamos la tpica funcin de factorial (autoinvocacin):
CREATE FUNCTION factorial (n number) RETURN number IS BEGIN IF n = 0 THEN RETURN 1; ELSE RETURN n* factorial (n-1); END IF; END ; /

Como las funciones devuelven un valor, si queremos llamar a una funcin desde el prompt de SQL debemos declarar una variable para almacenar dicho valor y posteriormente visualizarlo:
Dep. Informtica

45

I.E.S. TRASSIERRA - Crdoba

PL/SQL

SQL> variable v number; SQL> EXECUTE :v:=factorial(4); SQL> print v;

La recursividad no es una herramienta considerada fundamental en la programacin PL/SQL. Cualquier problema que requiera su utilizacin tambin puede ser resuelto utilizando iteracin. Una versin iterativa de un programa es usualmente ms fcil de disear y de entender. Sin embargo, la versin recursiva es ms simple, pequea y ms fcil de depurar. A modo de ejemplo, observe las siguientes dos versiones de cmo calcular el nmero n-simo de la serie de Fibonacci:
-- Versin recursiva FUNCTION fib (n POSITIVE) RETURN INTEGER IS BEGIN IF (n = 1) OR (n = 2) THEN RETURN 1; ELSE RETURN fib(n 1) + fib(n 2); END IF; END fib; -- Versin iterativa FUNCTION fib (n POSITIVE) RETURN INTEGER IS pos1 INTEGER := 1; pos2 INTEGER := 0; cum INTEGER; BEGIN IF (n = 1) OR (n = 2) THEN RETURN 1; ELSE cum := pos1 + pos2; FOR i IN 3..n LOOP pos2 := pos1; pos1 := cum; cum := pos1 + pos2; END LOOP; RETURN cum; END IF; END fib;

La versin recursiva es ms elegante que la iterativa, sin embargo esta ltima es ms eficiente, corre ms rpido y utiliza menos memoria.

3.3.3.- Funciones PL/SQL en sentencias SQL.Dentro de una expresin SQL se pueden referenciar funciones definidas por el usuario. Donde pueda haber una funcin SQL, puede situarse tambin una funcin PL/SQL. Para invocar una funcin desde una sentencia SQL se debe ser el propietario de la funcin o tener el privilegio EXECUTE. Las situaciones en que puede invocarse una funcin de usuario son las siguientes: La lista de expresiones del comando SELECT. Condiciones de las clusulas WHERE y HAVING. La clusula VALUES de la sentencia INSERT. La clusula SET de la sentencia UPDATE.
Dep. Informtica

46

I.E.S. TRASSIERRA - Crdoba

PL/SQL

NO se puede invocar una funcin PL/SQL desde la clusula CHECK de una vista. Para poder invocar desde una expresin SQL, una funcin PL/SQL definida por el usuario debe cumplir ciertos requerimientos bsicos. Debe ser una funcin almacenada, no una funcin dentro de un Bloque PL/SQL annimo o un subprograma. Debe ser una funcin de fila nica y NO una funcin de columna (grupo). Esto ocurre porque no se puede tomar el valor de todos los datos de una columna como argumento. Todos los argumentos deben ser del tipo IN. No se admiten los tipos OUT o IN OUT. Los tipos de datos de los argumentos deben ser tipos de datos internos del Servidor Oracle, tales como CHAR, DATE o NUMBER, y no tipo PL/SQL como BOOLEAN, RECORD o TABLE. El valor resultante de la funcin tambin debe ser un tipo de dato Oracle.

Para ejecutar una sentencia SQL que llama a una funcin almacenada, el servidor Oracle debe conocer si la funcin provoca efectos colaterales. stos pueden ser cambios en tablas de la base de datos o en variables pblicas de paquetes (que se declaran en la zona de especificacin del paquete). Los efectos colaterales pueden aplazar la ejecucin de una consulta, producir resultados dependientes del orden (por lo tanto indeterminados), o requerir que el estado del paquete se deba mantener a travs de las sesiones de usuario (lo cual no est permitido). Por ello, las siguientes restricciones se aplican a funciones almacenadas invocadas desde expresiones SQL: Las funciones no pueden modificar datos en las tablas. No pueden ejecutar INSERT, UPDATE o DELETE. Slo las funciones locales que se referencian en la lista de la SELECT, en la clusula , VALUES de un INSERT o en la clusula SET de un UPDATE pueden actualizar una variable definida en un paquete. No pueden actualizar variables dentro de un paquete las funciones referenciadas en las clusulas CONNECT BY, START WITH, ORDER BY o GROUP BY. Las funciones remotas no pueden leer, ni escribir el estado de un paquete. Cada vez que se accede aun enlace de base de datos, se establece una nueva sesin en la base de datos remota. Las funciones que leen o escriben en el estado de un paquete no pueden utilizar paralelismo.

3.4.- PAQUETES.Los paquetes son un conjunto de procedimientos, funciones y datos. Un paquete es muy similar a una librera con la ventaja de que podemos ocultar informacin, ya que se compone de una parte de declaraciones visibles desde el exterior y una serie de implementaciones a las que no puede accederse desde fuera sino es a travs del interfaz declarado en la parte pblica. Las ventajas que ofrecen los paquetes son: modularidad, facilidad en el diseo de aplicaciones, ocultamiento de la informacin, aaden funcionalidad al sistema, y mejoran el rendimiento ya que la primera llamada a la librera carga sta en memoria, de forma que las sucesivas invocaciones se evitan acceder a disco.
Dep. Informtica

47

I.E.S. TRASSIERRA - Crdoba

PL/SQL

3.4.1.- Creacin y borrado.Los paquetes se crean con el comando CREATE PACKAGE, cuya sintaxis es: - Parte de declaraciones: CREATE [OR REPLACE] PACKAGE nombre_paq AS [IS] bloque_pl_sql_paq_decl - Cuerpo del paquete (implementacin): CREATE [OR REPLACE] PACKAGE body nombre_paq AS [IS] bloque_pl_sql_paq_body Un paquete puede contener procedimientos, funciones, variables, constantes, cursores y excepciones. Adems, siempre que no se cambie la parte de declaraciones, se puede cambiar la implementacin de una rutina o el cuerpo de un cursor sin necesidad de volver a compilar las aplicaciones que los usaban, ya que el interfaz no se ha cambiado. Un paquete presenta normalmente el siguiente formato: create package nombre is /* especificacin (parte visible) */ /* declaraciones de tipos y objetos pblicos */ /* especificacin de procedimientos y funciones */ end; / create package body nombre is /* cuerpo (parte oculta)*/ /*declaraciones de tipos y objetos privados */ /* implementacin de procedimientos y funciones */ [ begin /* sentencias de inicializacin */ ] end; / En la cabecera del paquete se especifican todos aquellos objetos a los que se puede acceder, y en el cuerpo del paquete se implementan dichos objetos. En el cuerpo podemos declarar e implementar cuantos objetos estimemos oportunos teniendo en cuenta que a ellos solo se va a poder acceder desde el cuerpo del paquete. El cuerpo del paquete puede incluir opcionalmente una zona de inicializacin (BEGIN...) que Solo se ejecuta la primera vez que se hace referencia al paquete dentro de una misma sesin. Ya sabemos que los procedimientos y las funciones se declaran con la palabra reservada CREATE y se invocan con EXECUTE. Esto es as cuando los creamos desde el prompt de SQL, pero dentro de un paquete no hay que poner ni CREATE OR REPLACE, ni EXECUTE. Una vez que tenemos un paquete creado y compilado podemos acceder a los objetos declarados en la parte visible desde el prompt de SQL o desde otros procedimientos y funciones; para ello tan solo hay que anteponerle al nombre del procedimiento o funcin el nombre del paquete que lo contiene separado por un punto.
/* para el caso de que la llamada se haga desde el prompt de SQL*/ SQL > execute nombre_paquete.nombre_procedimiento(argumentos);

Dep. Informtica

48

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Cuando queramos borrar un paquete lo haremos con la sentencia DROP PACKAGE nombre_paquete ; A continuacin se expone un ejemplo de un paquete completo:
create or replace package acciones_empleados is type registro_empleado is record (empleado number, salario number); cursor salario_descendente (empleado number) return registro_empleado; procedure alta_empleado (nombre varchar2 , trabajo varchar2 , salario number, dpto number) ; procedure baja_empleado (id_empleado number); end; / create or replace package body acciones_empleados is cursor salario_descendente (empleado number) return registro_empleado is select no_empleado, salario from empleados order by salario desc; procedure alta_empleado ( nombre varchar2 , trabajo varchar2, salario number, dpto number) is begin insert into empleados values (sec_empleado.nextval, nombre, trabajo, salario, dpto); end; procedure baja_empleado (id_empleado number) is begin delete from empleados where empleado = id_empleado ; end; end; /

3.4.2.- La depuracin en los paquetes.Al objeto de identificar los errores en tiempo de compilacin, en vez de esperar a la ejecucin, puede especificarse el nivel de depuracin de una funcin empaquetada cuando se crea el paquete. El nivel de depuracin determina que operaciones puede realizar la funcin en la base de datos. Para especificar en nivel de depuracin se utiliza el paquete predefinido RESTRICT_REFERENCES. Sintaxis: PRAGMA RESTRICT_REFERENCES ( nombre_func, WNDS [,WNPS] [RNDS] [,RNPS] ) ; Donde: WNDS WNPS RNDS RNPS Indica que la funcin no puede modificar tablas de la base de datos. (Writes No Database State). En este paquete es obligatoria. Hace que la funcin no pueda cambiar los valores de variables de un paquete pblico. (Writes No Package Status). No permite que la funcin pueda consultar ninguna tabla de la base de datos. (Reads No Database State). Indica que la funcin no puede referenciar los valores de las variables de un paquete pblico. (Reads No Database Status).

Dep. Informtica

49

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Este paquete sirve para alertar a los desarrolladores de aplicaciones con cdigo procedural que incumplen las restricciones de las funciones PL/SQL dentro de sentencias SQL. Indica al compilador PL/SQL qu tipo de acciones pueden realizar las funciones. Las sentencias SQL que incumplan estas reglas producen un error de compilacin.

Ejemplo: Crear una funcin llamada COMP, dentro del paquete FINANZAS. La funcin ser invocada desde sentencias SOL en bases de datos remotas. Por lo tanto ser necesario indicar los niveles de depuracin RNPS y WNPS porque las funciones remotas no pueden nunca escribir o leer el estado del paquete cuando se le invoca desde una sentencia SQL.
CREATE PACKAGE finanzas AS interes REAL; FUNCTION comp (anos IN NUMBER cant IN NUMBER porc IN NUMBER) RETURN NUMBER; PRAGMA RESTRICT _REFERENCES(comp, WNDS, RNPS, WNPS); END finanzas; / CREATE PACKAGE BODY finanzas AS FUNCTION comp (anos IN NUMBER cant IN NUMBER porc IN NUMBER) RETURN NUMBER IS BEGIN comp RETURN cant * POWER((porc/100) +1, anos) END comp; END finanzas; /

El nivel de depuracin de una funcin se debe especificar en la zona de especificacin del paquete. Si el paquete contiene varias funciones con el mismo nombre, el nivel de depuracin se aplica a la ltima. La invocacin a una funcin empaquetada se hace de la misma forma que si fuera una funcin SQL. Por ejemplo, para llamar a la funcin COMP (que se encuentra en el paquete FINANZAS residente enm la BD de Barcelona) desde una sentencia SELECT, dentro de un bloque PL/SQL, haramos:
DECLARE interes NUMBER; BEGIN SELECT finanzas.comp@bcn(anos,cant,porc) INTO interes FROM conta WHERE no_cuenta = cuenta_id END;

Dep. Informtica

50

I.E.S. TRASSIERRA - Crdoba

PL/SQL

3.5.- VENTAJAS DE PROCEDIMIENTOS, FUNCIONES Y PAQUETES.El uso de procedimientos, funciones de usuario y paquetes presenta una serie de ventajas: Modula el desarrollo de las aplicaciones. Aumenta la seguridad y la integridad en la Base de Datos. - Mayor independencia de los datos, al ser analizados dentro del servidor Oracle en vez de desde dentro de una aplicacin. - Control de acceso indirecto a objetos de la B.D. para usuarios no privilegiados. Asegura que las acciones que estn relacionadas se ejecutan conjuntamente. Aumenta del rendimiento - Permite realizar clculos complejos, lentos o no disponibles en SQL - Preanlisis de sentencias que ejecutan varios usuarios, utilizando el SQL compartido. - Las sentencias PL/SQL se analizan en tiempo de compilacin y no en ejecucin. - Reduccin del nmero de llamadas a la Base de Datos y el trfico por la red gracias a la agrupacin de comandos. - Posibilidad de manipular nuevos tipos de datos (por ejemplo latitud o longitud), codificando cadenas de caracteres y utilizando funciones para operar con las cadenas. Mejora en la utilizacin de la memoria - Una nica copia del cdigo almacenado en la B.D. en lugar de mltiples copias del mismo cdigo en aplicaciones diferentes. - Utiliza SQL compartido para evitar el exceso de cursores. Fcil mantenimiento - Permite modificar procedimientos on-line sin interferir al resto de los usuarios (que ejecutan una versin anterior). - La modificacin de una rutina afecta a todas las aplicaciones que trabajen con ella. Esto implica eliminar la duplicidad de las comprobaciones.

3.6.- MANEJO DE EXCEPCIONES.Los procedimientos y funciones deben prever cualquier tipo de excepcin ocurrida mientras se ejecutan, bien propagando el error al entorno de llamada o bien desvindolo a una zona del subprograma. Como sabemos hay dos tipos de exepciones: las de Oracle y las definidas por el usuario. La siguiente tabla muestra como conseguir la comunicacin interactiva del error o realizar una operacin en la base de datos.

Tipo de excepcin
Oracle

Efecto a conseguir
Comunicar el error interactivamente

Mtodo de manejo

Definida por usuario

Omitir en el procedimiento el manejo de errores Realizar una operacin en la base de datos Incluir el manejo de excepciones dentro adaptando el mensaje de error. del procedimiento. Comunicar el error interactivamente Llamar al procedimiento RAISE APPLICA TION ERROR. Realizar una operacin en la base de datos Incluir el manejo de excepciones dentro adaptando el mensaje de error. del procedimiento

Dep. Informtica

51

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Las excepciones de Oracle pueden ser controladas por el usuario con la sentencia EXCEPTION_INIT ya vista, que permite comunicar el cdigo y el mensaje de error para una excepcin de Oracle de forma interactiva omitiendo el manejo de excepciones. Si se omite el manejo de errores y el procedimiento termina con un fallo, devolver al entorno de llamada el error "UNHANDLED EXCEPTION". Ejemplo: Propagar una excepcin del Servidor Oracle Mientras se est almacenando informacin para un empleado nuevo, comprobar que su jefe existe como empleado:
CREATE OR REPLACE PROCEDURE alta_emp (v_emp_no IN emp.empno%TYPE, v_emp_name IN emp.ename%TYPE, v_emp_job IN emp.job%TYPE, v_mgr_no IN emp.mgr%TYPE, v_emp_sal emp.sal%TYPE) IS v_emp_hiredate emp.hiredate%TYPE ; v_emp_comm emp.comm%TYPE ; v_emp_deptno emp.deptno%TYPE ; BEGIN v_emp_hiredate:=sysdate; v_emp_comm:=0; SELECT deptno INTO v_emp_deptno FROM emp WHERE empno = v_mgr_no; INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (v_emp_no, v_emp_name, v_emp_job, v_mgr_no, v_emp_hiredate, v_emp_sal, v_emp_comm, v_emp_deptno); END alta_emp; /

La siguiente sentencia provocara la respuesta:


SQL> EXECUTE alta_emp (1, 'PEPE', 'CLERK', 9999, 1000); BEGIN alta_emp (1, 'PEPE', 'CLERK', 9999, 1000); END; * ERROR en lnea 1: ORA-01403: no se han encontrado datos ORA-06512: en "SCOTT.ALTA_EMP", lnea 14 ORA-06512: en lnea 1

Ejemplo: Propagar una excepcin definida por el usuario. Recordemos que el procedimiento RAISE_APPLICATION_ERROR su usa para mostrar una excepcin definida por el usuario de forma interactiva devolviendo un cdigo de error no estndar y un mensaje. Mientras se intenta borrar a un empleado, comprobar que el empleado especificado existe:
CREATE OR REPLACE PROCEDURE baja_emp (v_emp_numero IN temple.numem%TYPE) IS BEGIN DELETE FROM temple WHERE numem = v_emp_numero; IF SQL%NOTFOUND THEN RAISE_APPLICATION_ERROR (-20300, 'Empleado inexistente'); END IF; END baja_emp; / Dep. Informtica

52

I.E.S. TRASSIERRA - Crdoba

PL/SQL

La siguiente sentencia provocara la respuesta:


SQL> EXECUTE baja_emp(9999); BEGIN baja_emp(9999); END; * ERROR en lnea 1: ORA-20300: Empleado inexistente ORA-06512: en "SCOTT.BAJA_EMP", lnea 6 ORA-06512: en lnea 1

Si se omite el manejo de excepciones RAISE_APPLICATION_ERROR para una excepcin definida por el usuario y el procedimiento finaliza con un fallo, devolver un mensaje indicando que la excepcin definida por el usuario ha fallado. Si se incluye el manejo de excepciones, el proceso se direcciona a la zona de excepciones. El desvo de excepciones definidas por el usuario acta de forma similar a las excepciones Oracle, pero incluyendo su correspondiente manejador. Hay dos etapas adicionales: la declaracin de la excepcin e invocarla de manera explcita con la sentencia RAISE. Ejemplo: Desviar el flujo del programa a una excepcin Oracle predefinida. Mientras se almacena informacin acerca de un nuevo empleado, comprobar que su jefe existe como empleado.
CREATE OR REPLACE PROCEDURE alta_emp (v_emp_no IN emp.empno%TYPE, v_emp_name IN emp.ename%TYPE, v_emp_job IN emp.job%TYPE, v_mgr_no IN emp.mgr%TYPE, v_emp_sal emp.sal%TYPE) IS v_emp_hiredate emp.hiredate%TYPE ; v_emp_comm emp.comm%TYPE ; v_emp_deptno emp.deptno%TYPE ; BEGIN v_emp_hiredate:=sysdate; v_emp_comm:=0; SELECT deptno INTO v_emp_deptno FROM emp WHERE empno = v_mgr_no; INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (v_emp_no, v_emp_name, v_emp_job, v_mgr_no, v_emp_hiredate, v_emp_sal, v_emp_comm, v_emp_deptno); EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR (-20201, 'El jefe indicado:'|| v_mgr_no||' no es empleado'); END alta_emp; /

La siguiente sentencia provocara la respuesta:


SQL> EXECUTE alta_emp (1, 'PEPE', 'CLERK', 9999, 1000); BEGIN alta_emp (1, 'PEPE', 'CLERK', 9999, 1000); END; * ERROR en lnea 1: ORA-20201: El jefe indicado:9999 no es empleado ORA-06512: en "SCOTT.ALTA_EMP", lnea 21 ORA-06512: en lnea 1

Dep. Informtica

53

I.E.S. TRASSIERRA - Crdoba

PL/SQL

Para direccionar el flujo de programa a una excepcin Oracle no predefinida, invocndola con la sentencia EXCEPTION_INIT hay que seguir los siguientes pasos: Declarar la excepcin. Asociar la excepcin declarada con un nmero de error Oracle utilizando la sentencia EXCEPTION_INIT.

Ejemplo: Desviar el flujo del programa a una excepcin Oracle NO-PREDEFINIDA. Mientras se almacena informacin acerca de un nuevo empleado, comprobar que su jefe existe como empleado.
CREATE OR REPLACE PROCEDURE alta_emp (v_emp_no IN emp.empno%TYPE, v_emp_name IN emp.ename%TYPE, v_emp_job IN emp.job%TYPE, v_mgr_no IN emp.mgr%TYPE, v_emp_hiredate emp.hiredate%TYPE , v_emp_sal emp.sal%TYPE, v_emp_comm emp.comm%TYPE ) IS v_emp_deptno emp.deptno%TYPE ; e_jefe_erroneo EXCEPTION; PRAGMA EXCEPTION_INIT(e_jefe_erroneo, +100); BEGIN SELECT deptno INTO v_emp_deptno FROM emp WHERE empno = v_mgr_no; INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (v_emp_no, v_emp_name, v_emp_job, v_mgr_no, v_emp_hiredate, v_emp_sal, v_emp_comm, v_emp_deptno); EXCEPTION WHEN e_jefe_erroneo THEN RAISE_APPLICATION_ERROR (-20291, 'Jefe '|| v_mgr_no||' no es empleado'); END alta_emp; /

La siguiente sentencia provocara la respuesta:


SQL> EXECUTE alta_emp(1, PEPE', 'CLERK', 9999, '01-JUL-98', 1000, NULL); BEGIN alta_emp(1, 'PEPE', 'CLERK', 9999, '01-JUL-98', 1000, NULL); END; * ERROR en lnea 1: ORA-20291: Jefe 9999 no es empleado ORA-06512: en "SCOTT.ALTA_EMP", lnea 20 ORA-06512: en lnea 1

3.7.- EJERCICIOS.3.7.1.- Ejercicios resueltos.1.- Crear el procedimiento aumento_salario para aumentar el salario de un empleado en una cantidad. El nmero de empleado y el aumento se le pasarn a la funcin, la cual deber controlar los errores producidos por la inexistencia del empleado y del salario (valor nulo).
CREATE PROCEDURE aumento_salario (empleado integer, incremento real) IS salario_actual real; Dep. Informtica

54

I.E.S. TRASSIERRA - Crdoba

PL/SQL

falta_salario exception; BEGIN SELECT salario INTO salario_actual FROM empleados WHERE no_empleado = empleado; IF salario_actual IS NULL THEN RAISE falta_salario; ELSE UPDATE empleados SET salario = salario + incremento WHERE no_empleado = empleado; END IF EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO mensajes VALUES (empleado, 'No existe el empleado'}; WHEN falta_salario THEN INSERT INTO mensajes VALUES (empleado, 'No tiene salario'); END; /

2.- Determinar si un nmero es par o impar. Recursividad por interinvocacin.


CREATE OR REPLACE package pq_par IS FUNCTION par(n number) RETURN boolean; FUNCTION impar(n number) RETURN boolean; END pq_par; / CREATE OR REPLACE PACKAGE BODY pq_par IS FUNCTION par (n number) RETURN boolean IS BEGIN IF n = 0 THEN RETURN TRUE; ELSE RETURN impar(n-1); END IF; END; FUNCTION impar (n number) RETURN boolean IS BEGIN IF n = 0 THEN RETURN FALSE; ELSE RETURN par(n-1); END IF; END; END pq_par;

/ 3.- Ejemplo completo de paquete: Consideremos el siguiente paquete emp_actions. La parte de especificacin del paquete declara los siguientes tipos de datos, variables y subprogramas: Tipos EmpRecTyp y DeptRecTyp Cursor desc_salary Excepcin invalid_salary Funciones hire_employee y nth_highest_salary Procedimientos fire_employee y raise_salary

Tras la escritura del paquete, podemos escribir subprogramas que hagan referencia a los tipos que declara, a su cursor y a su excepcin, por que el paquete se almacena en la base de datos para uso pblico.

Dep. Informtica

55

I.E.S. TRASSIERRA - Crdoba

PL/SQL

CREATE PACKAGE emp_actions AS /* Declaracin pblica (visible desde fuera) de tipos, cursor y excepcin */ TYPE EmpRecTyp IS RECORD (emp_id INT, salary REAL); TYPE DeptRecTyp IS RECORD (dept_id INT, location VARCHAR2); CURSOR desc_salary RETURN EmpRecTyp; invalid_salary EXCEPTION; /* Declaracin de subprogramas. */ FUNCTION hire_employee ( ename VARCHAR2, job VARCHAR2, mgr REAL, sal REAL, comm REAL, deptno REAL) RETURN INT; PROCEDURE fire_employee (emp_id INT); PROCEDURE raise_salary (emp_id INT, grade INT, amount REAL); FUNCTION nth_highest_salary (n INT) RETURN EmpRecTyp; END emp_actions; / CREATE PACKAGE BODY emp_actions AS number_hired INT;

-- visible solo desde este paquete

CURSOR desc_salary RETURN EmpRecTyp IS SELECT empno, sal FROM emp ORDER BY sal DESC; FUNCTION hire_employee ( ename VARCHAR2, job VARCHAR2, mgr REAL, sal REAL, comm REAL, deptno REAL) RETURN INT IS

new_empno INT; BEGIN SELECT empno_seq.NEXTVAL INTO new_empno FROM dual; INSERT INTO emp VALUES (new_empno, ename, job, mgr, SYSDATE, sal, comm, deptno); number_hired := number_hired + 1; RETURN new_empno; END hire_employee; PROCEDURE fire_employee (emp_id INT) IS BEGIN DELETE FROM emp WHERE empno = emp_id; END fire_employee; /* Definiciones locales de funciones accesibles solo desde este paquete. */ FUNCTION sal_ok (rank INT, salary REAL) RETURN BOOLEAN IS min_sal REAL; max_sal REAL; BEGIN SELECT losal, hisal INTO min_sal, max_sal FROM salgrade WHERE grade = rank; RETURN (salary >= min_sal) AND (salary <= max_sal); END sal_ok; PROCEDURE raise_salary (emp_id INT, grade INT, amount REAL) IS salary REAL; BEGIN SELECT sal INTO salary FROM emp WHERE empno = emp_id; IF sal_ok(grade, salary + amount) THEN

Dep. Informtica

56

I.E.S. TRASSIERRA - Crdoba

PL/SQL

UPDATE emp SET sal = sal + amount WHERE empno = emp_id; ELSE RAISE invalid_salary; END IF; END raise_salary; FUNCTION nth_highest_salary (n INT) RETURN EmpRecTyp IS emp_rec EmpRecTyp; BEGIN OPEN desc_salary; FOR i IN 1..n LOOP FETCH desc_salary INTO emp_rec; END LOOP; CLOSE desc_salary; RETURN emp_rec; END nth_highest_salary; -- Esta es la parte opcional de inicializacin. Solo se ejecuta la primera vez BEGIN -- que se hace referencia al paquete dentro de una misma sesin */ INSERT INTO emp_audit VALUES (SYSDATE, USER, EMP_ACTIONS); number_hired := 0; END emp_actions; /

4.- Un ejemplo de paquete sin cuerpo:


CREATE PACKAGE trans_data AS TYPE TimeRec IS RECORD ( horas NUMBER, minutos NUMBER); TYPE TransRec IS RECORD ( categoria VARCHAR2, cuenta NUMBER, cantidad NUMBER, horario TimeRec); minimo_balance CONSTANT NUMBER := 10.00; numero_procesado NUMBER; insuficiente_saldo EXCEPTION; END trans_data;

Este paquete no tiene ningn cuerpo, porque los registros, variables, constantes y excepciones no tienen ninguna implementacin especial. Se definen variables globales -utilizables por subprogramas y disparadores de bases de datos- que persistirn durante toda la sesin.

3.7.2.- Ejercicios propuestos.1.- Crear el procedimiento ALTA_DEP, que permitir dar de alta un nuevo departamento en la tabla TDEPTO. Por defecto pondr el campo tidir a 'F' y el campo presu a cero. 2.- Crear la funcin SUELDO_EMP, que recibir el nmero de un empleado y devolver su sueldo total (salario+comisin). Si el empleado suministrado nbo existe, devolver el error -20222 'Empleado inexistente'. 3.- Crear la funcin SUELDO_DEP, que haciendo uso de la funcin anterior, reciba un nmero de departamento y devuelva su sueldo total. 4.- Disear el procedimiento AUMENTO_SALARIO, que reciba un nmero de departamento y un porcentaje, realice el incremento de los salarios de los empleados de ese departamento en el porcentaje dado. Si no se especifica porcentaje, se supondr del 0,5%. 5.- Crear el paquete PAQ_EMPLE que recoja todos los procedimientos y funciones desarrollados (alta_emp, baja_emp, consulta_emp, sueldo_emp y aumento_salario).
Dep. Informtica

57

You might also like