PL_SQL.pdf

March 24, 2018 | Author: Carlos Felipe Ramos Sequeira | Category: Pl/Sql, Sql, Table (Database), Oracle Corporation, Computer Data


Comments



Description

Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASEPLSQL - 161 - Mario Juncos Murcia TEMA PLSQL Introduccion ...................................................................................................................... 164 Relacion Entre Sql Y Pl/Sql ............................................................................................... 164 Ventajas De Pl/Sql ............................................................................................................. 164 Estructura De Bloques....................................................................................................... 165 Ambito De Visibilidad ....................................................................................................... 166 Soporte De Sql .................................................................................................................. 167 Tipos De Datos ................................................................................................................. 168 Escalares ........................................................................................................................ 168 Binary-Integer ............................................................................................................. 168 Number ....................................................................................................................... 168 Char .................................................................................................................................. 169 Varchar2 ........................................................................................................................... 169 Long ................................................................................................................................. 169 Raw .................................................................................................................................. 169 Long Raw ......................................................................................................................... 170 Boolean ............................................................................................................................. 170 Date .................................................................................................................................. 170 Rowid ............................................................................................................................... 170 Conversiones Implícitas Entre Tipos De Datos ................................................................. 171 Rowid ............................................................................................................................... 171 7.2 Compuestos ................................................................................................................. 173 Registros ........................................................................................................................... 173 Atributo %Rowtype: ......................................................................................................... 173 Registros Explicitos ........................................................................................................... 174 Registros Anidados............................................................................................................ 175 Tablas(Arrays) ................................................................................................................... 175 8.- Variables, Constantes Y Registros .................................... ¡Error! Marcador no definido. Variables ............................................................................... ¡Error! Marcador no definido. Constantes ......................................................................................................................... 172 Declaraciones Con Atributos ............................................................................................. 172 Atributo %Type: ............................................................................................................... 172 9.- Comentarios De Programador ...................................................................................... 172 10.- Comparaciones Logicas .............................................................................................. 183 11.- Control Condicional ................................................................................................... 183 Sentencia If ... Then ... Else ... End If ................................................................................ 183 12.- Control Iterativo ........................................................................................................ 184 Sentencia Loop: ................................................................................................................ 184 Bucles For ......................................................................................................................... 184 Bucles While ..................................................................................................................... 185 Sentencia Exit ................................................................................................................... 185 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 162 - Mario Juncos Murcia Sentencia Goto .................................................................................................................. 186 13.- Manejo De Cursores ................................................................................................... 190 Cursores Explicitos............................................................................................................ 190 Cursor ............................................................................................................................... 190 Open ................................................................................................................................. 191 Fetch ................................................................................................................................. 191 Close ................................................................................................................................. 191 Atributos Predefinidos Para Cursores ................................................................................ 192 %Notfound ....................................................................................................................... 192 %Found ............................................................................................................................ 192 %Rowcount ...................................................................................................................... 192 %Isopen ............................................................................................................................ 193 Bucles For Para Cursores .................................................................................................. 193 El Cursor Implicito Sql ...................................................................................................... 194 Sql%Notfound .................................................................................................................. 194 Sql%Found ....................................................................................................................... 194 Sql%Rowcount ................................................................................................................. 194 Sql%Isopen ....................................................................................................................... 194 15.- Manejo De Errores ..................................................................................................... 199 Declaracion: ...................................................................................................................... 199 Elevacion: ......................................................................................................................... 199 Manejo: ............................................................................................................................. 200 Excepciones Predefinidas ................................................................................................... 201 Dup_Val_On_Index .............................................................. ¡Error! Marcador no definido. Invalid_Cursor....................................................................... ¡Error! Marcador no definido. Invalid_Number ..................................................................... ¡Error! Marcador no definido. Login_Denied ........................................................................ ¡Error! Marcador no definido. No_Data_Found .................................................................... ¡Error! Marcador no definido. Not_Logged_On ................................................................... ¡Error! Marcador no definido. Program_Error ...................................................................... ¡Error! Marcador no definido. Storage_Error ....................................................................... ¡Error! Marcador no definido. Timeout_On_Resource .......................................................... ¡Error! Marcador no definido. Too_Many_Rows .................................................................. ¡Error! Marcador no definido. Value_Error .......................................................................... ¡Error! Marcador no definido. Zero_Divide .......................................................................... ¡Error! Marcador no definido. Others ................................................................................... ¡Error! Marcador no definido. Encapsulamiento De Errores Oracle ...................................... ¡Error! Marcador no definido. Sqlcode: ................................................................................ ¡Error! Marcador no definido. Sqlerrm: ................................................................................ ¡Error! Marcador no definido. 14.- Subprogramas ............................................................................................................ 209 Procedimientos .................................................................................................................. 209 Funciones .......................................................................................................................... 210 Declaracion Diferida .......................................................................................................... 211 Notacion Posicional Y Nominal ......................................................................................... 211 Valores Por Defecto Para Los Argumentios ....................................................................... 212 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 163 - Mario Juncos Murcia Sobrecarga ........................................................................................................................ 212 RECURSIVIDAD ............................................................................................................. 213 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 164 - Mario Juncos Murcia INTRODUCCION PL/SQL consiste en un lenguaje recubrimiento de SQL que dota a este de las capacidades clásicas de los lenguajes procedurales o procedimentales, como estructuras de bucle, saltos condicionales, variables, asignaciones, etc. Combina la potencia de SQL para manipulación de datos con la capacidad para procesamiento de estos de los lenguajes procedimentales. Se trata de una herramienta fundamental para aquellos programadores que desarrollen aplicaciones para el Sistema de Bases de Datos Relacionales ORACLE RELACION ENTRE SQL Y PL/SQL PL/SQL es una extensión de SQL. Por tanto, permite utilizar todas las sentencias SQL para manejo de información, como INSERT, UPDATE, DELETE, SELECT, etc. Adicionalmente permite la utilización de estructuras procedimentales: - Estructuras de flujo de control como:  IF ... THEN ... ELSE,  EXIT y  GOTO. - Estructuras repetitivas como:  FOR ... LOOP y  WHILE ... LOOP - Sentencias de asignación como: X := y + Z VENTAJAS DE PL/SQL Entre otras, PL/SQL ofrece las siguientes características: Capacidad procedimental Permite el uso de estructuras procedimentales clásicas como bucles, flujo condicional, asignaciones, etc. Aumento de la eficiencia del sistema Sin PL/SQL cada sentencia SQL se traduce en una llamada a ORACLE, con el consecuente aumento de tráfico de información. Por el contrario, un bloque PL/SQL (formado por un conjunto de sentencias procedimentales mas sentencias SQL) se traduce en una única llamada. PL/SQL es fácilmente integrable con el resto de las herramientas ORACLE como Forms. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 165 - Mario Juncos Murcia Portabilidad Las aplicaciones escritas en PL/SQL son portabas a cualquier sistema informático y cualquier sistema operativo, con la única condición de que ejecuten el Sistema de Bases de Datos Relacionales ORACLE. Alta integración con ORACLE PL/SQL permite el acceso al Diccionario de Datos ORACLE de tal manera que la consistencia de tipos de datos esta asegurada. Es el caso del atributo %TYPE, que permite definir variables del mismo tipo que tenga determinada columna de una tabla. ESTRUCTURA DE BLOQUES La unidad mínima ejecutable de PL/SQL es el BLOQUE. Cada bloque esta dividido en tres partes: Sección DECLARATIVA Contiene la declaración de constantes, variables, etc. Sección EJECUTABLE Contiene las sentencias a ejecutar. Sección de MANEJADORES DE EXCEPCION Contiene las sentencias de manejo de errores. La Sección EJECUTABLE es obligatoria, mientras que las otras dos son opcionales. Cada bloque puede, a su vez, contener otros bloques, a los que llamaremos sub-bloques. Los sub-bloques se declaran en la parte ejecutable. BLOQUE PL/SQL DECLARE ... Sentencias de Declaración ... BEGIN ... Sentencias Ejecutables ... EXCEPTION ... Manejadores de Excepciones END; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 166 - Mario Juncos Murcia EJEMPLO DE UN BLOQUE PL/SQL DECLARE V_cantidad NUMBER (5); BEGIN SELECT cantidad INTO v_cantidad FROM inventario WHERE producto ='RAQUETA' IF v_cantidad > 0 THEN UPDATE inventario SET cantidad = cantidad - 1 WHERE producto ='RAQUETA'; INSERT INTO registro VALUES ('RAQUETA VENDIDA', SYSDATE); ELSE INSERT INTO registro VALUES ('RAQUETA SIN STOCK, SYSDATE); END IF; COMMIT; EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO registro VALUES(„ No existe la raqueta‟, SYSDATE); COMMIT; END; AMBITO DE VISIBILIDAD En PL/SQL un identificador denota cualquier objeto, como constantes, variables, registros, cursores o excepciones. El ámbito de visibilidad de un identificador determina qué bloques pueden referenciarlo, es decir, para qué bloques es visible un identificador u objeto. Los identificadores declarados en un bloque son locales a este y globales a todos sus sub-bloques. Los identificadores globales pueden ser declarados de nuevo en un sub-bloque. En este caso el sub-bloque pierde el acceso al objeto denotado por el identificador del padre, a menos que se utilicen etiquetas para resolver la ambigüedad del nombre. Un bloque no puede referenciar identificadores de otros bloques anidados al mismo nivel (bloques hermanos), pues estos identificadores no son ni locales, ni globales. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 167 - Mario Juncos Murcia EJEMPLO << bloque_padre >> DECLARE fecha DATE; BEGIN ... <<bloque_hijo >> DECLARE fecha DATE; fecha1 DATE; BEGIN ... IF fecha = bloque_padre.fecha THEN .... END IF; END bloque_hijo; ... DECLARE fecha2 DATE; BEGIN ... IF fecha2 = fecha1 THEN ........... -- NO PERMITIDO!!! END IF; END; END bloque_Padre; SOPORTE DE SQL Toda la potencia de SQL es soportada por PL/SQL, en particular: Sentencias SQL de manipulación de datos:  SELECT  INSERT  UPDATE  DELETE - Sentencias SQL para procesamiento de transacciones: Una transacción es una secuencia de instrucciones SQL que ORACLE trata como una operación atómica, es decir, o se completa entera o no se ejecuta en absoluto. Las sentencias son: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 168 - Mario Juncos Murcia  COMMIT  SAVEPOINT  ROLLBACK - Funciones SQL  Numéricas  De Caracteres  De Fecha  De Grupo  De conversión de datos - Predicados SQL PL/SQL permite el acceso a todos los predicados del lenguaje SQL formados con los siguientes operadores:  Operadores de comparación (=, >, <, >=, etc.)  BETWEEN  IS [NOT] NULL  [NOT] LIKE  EXISTS TIPOS DE DATOS Están los escalares y los compuestos ( tablas y arrays) ESCALARES BINARY-INTEGER Permite almacenar números enteros con signo en el rango NUMBER Permite almacenar números, (enteros y fraccionarios en coma fija o en coma flotante). La sintaxis es: NUMBER [(precisión [,escala])]. Los valores son redondeados siempre a la cifra indicada por la escala NUMBER (5,2)... 26,314 -> 26,31 NUMBER (7,-3)... 2412 ---> 2000 Subtipos de NUMBER: DEC DECIMAL DOUBLE PRECISION FLOAT INTEGER INT NUMERIC REAL SMALLINT Los subtipos de NUMBER ofrecen el mismo rango y precisión que NUMBER. Se ofrecen por compatibilidad con otros gestores de datos. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 169 - Mario Juncos Murcia CHAR Permite almacenar cadenas de caracteres de longitud fija. La representación interna depende del juego de caracteres (ASCII, EBCDIC, etc.). La sintaxis es: CHAR [(long_max)] donde long_max debe estar en el rango 1 .. 32.767. Una columna CHAR admite como máximo 255 bytes. Subtipos de CHAR: CHARACTER STRING Soportan los mismos valores y se ofrecen solo por compatibilidad con otros gestores de datos VARCHAR2 Permite almacenar cadenas de caracteres en formato variable. La sintaxis es: VARCHAR2 [(long_max)] donde long_max debe estar en el rango 1 .. 32.767. Una columna VARCHAR2 admite como máximo 2000 bytes. Subtipos de VARCHAR2: VARCHAR Se ofrece por compatibilidad con otros gestores y en especial con el estándar ANSI/SQL, pero sus características son las mismas que VARCHAR2. LONG Permite almacenar hasta 32.760 bytes en formato carácter. La sintaxis es: LONG Una columna LONG puede almacenar hasta 2Gb. Cualquier valor de tipo LONG puede ser insertado en una columna LONG, pero la inversa no es siempre cierta. RAW Permite almacenar hasta 32.767 bytes. La sintaxis es: RAW A diferencia de los valores CHAR o VARCHAR, para los datos RAW el juego de caracteres no es relevante. Este tipo es utilizado para almacenar secuencias gráficas, imágenes digitalizadas, etc. Las columnas RAW solo admiten 255 bytes. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 170 - Mario Juncos Murcia LONG RAW Permiten almacenar secuencias de hasta 32.760 bytes. La sintaxis es: LONG RAW Una columna LONG RAW admite hasta 2Gb. BOOLEAN Solo admite los valores: TRUE, FALSE, NULL SINTAXIS: BOOLEAN El tipo BOOLEAN no está disponible para columnas de la Base de Datos. DATE Permite almacenar fechas en el rango 1- 1 -4.712 a.C. .. 31-12-4.712 d.C. SINTAXIS: DATE ROWID Admite valores tipo ROWID. El ROWID de una fila identifica su posición física en disco. SINTAXIS: ROWID FORMATO: BBBBBBBB.RRRR.FFFF Donde: BBBBBBBB es el bloque de disco RRRR es la posición de la fila dentro del bloque FFFF es el número de fichero físico donde esté la fila. (todos en hexadecimal) Aunque los valores ROWID de la Base de Datos se almacena en un formato interno de 6 bytes, el tipo ROWID de PL/SQL es un subtipo de CHAR. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 171 - Mario Juncos Murcia CONVERSIONES IMPLÍCITAS ENTRE TIPOS DE DATOS A De Binary Integer CHAR DATE LONG NUMBER RAW ROWID VARCHAR2 Binary Integer S S S S CHAR S S S S S S S DATE S S S LONG S S NUMBER S S S S S RAW S S S ROWID S S VARCHAR2 S S S S S S S VARIABLES Su uso mas común esta relacionado con el almacenamiento de valores para un tratamiento posterior o para calcular valores que serán introducidos en las tablas de la Base de Datos. Desde el punto de vista sintáctico, pueden usarse en cualquier lugar donde se pueda colocar una expresión. Declaración: Las variables pueden ser de cualquiera de los tipos nativos de ORACLE (como NUMBER, CHAR, o DATE) o particular de PL/SQL (como BOOLEAN). La longitud máxima del identificador (de cualquier objeto) es de 30 caracteres, debiendo de empezar siempre por un carácter y sin distinguir entre mayúsculas y minúsculas. Ejemplos: bonificacion NUMBER (11,2); en_stock BOOLEAN; Las variables pueden asignarse en tiempo de declaración como en: bonificacion NUMBER(4,2):=37.56; en_stock BOOLEAN DEFAULT TRUE; Asignación: Existen dos vías de asignación para las variables PL/SQL: - Operador de asignación  total := precio + iva;  comisión := salario * 0.10; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 172 - Mario Juncos Murcia - A través de SELECT ... INTO: SELECT salario INTO salario_actual FROM empleados WHERE nombre ='ARENAS'; CONSTANTES La declaración de constantes se realiza exactamente igual que la de variables, a excepción de la palabra reservada CONSTANT: - Ejemplo: incr CONSTANT NUMBER(2);= 10; La inicialización en tiempo de declaraciones es lógicamente obligatoria. La cláusula DEFAULT también está disponible: incr CONSTANT NUMBER(2) DEFAULT 10; DECLARACIONES CON ATRIBUTOS La utilización de atributos facilita la declaración de variables, accediendo directamente al Diccionario de Datos. Atributo %TYPE: El atributo %TYPE permite obtener el tipo de dato de una columna determinada de la Base de Datos, variable o constante: Titulo libros.titulo_libro%TYPE; var1 Number(7); var2 var1%TYPE; La variable 'titulo' obtiene como tipo de dato el mismo que tenga la columna 'titulo_líbro' de la tabla 'libros'. Este tipo de declaraciones ayuda a la transparencia del código, así como a la portabilidad y consistencia de este. COMENTARIOS DE PROGRAMADOR Los comentarios de programador en PL/SQL tienen dos formatos diferentes: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 173 - Mario Juncos Murcia - Al estilo de C: /* Esto es un comentario /* Esto, que ocupa mas de una línea, también lo es */ - Al estilo de Ada: -- Esto es un comentario --El doble guión solo -- afecta a una línea COMPUESTOS REGISTROS En PL/SQL un registro es una variable formada por la unión de varios campos. Atributo %ROWTYPE: El atributo %ROWTYPE se utiliza para que una variable de tipo registro obtenga la misma estructura que una fila (o parte de ella) de una tabla determinada. - Ejemplos: un_emp empleados%ROWTYPE; implícitamente un_emp tiene la misma estructura que la tabla empleados, así tendríamos un_emp.num_empleado, un_emp.nombre, etc. DECLARE un_emp emp%ROWTYPE; Cuando se ejecute: SELECT * INTO un_emp FROM empleados; los campos del registro toman los valores de las columnas indicadas y dichos campos se pueden referenciar como: total_sal := total_sal + un_emp.salario Los valores de los campos pueden ser cambiados según la sintaxis: nombre_reg.nombre_campo := exp Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 174 - Mario Juncos Murcia como en: un_emp.salario := 0; REGISTROS EXPLICITOS Adicionalmente, se pueden declarar registros explícitos, como en la mayoría de los lenguajes de 30 Generación. La declaración de una variable de tipo registro se apoya en la declaración de un nuevo tipo con la sintaxis. Primero hay que definir la estructura del registro y después variables de ese tipo de registro. TYPE nuevo_tipo IS RECORD ( tipo_base variable%TYPE Campo1 tabla.columna%TYPE [NOT NULL] [:= valor], tabla%ROWTYPE Campo2... ); Ejemplo: TYPE emp_sal IS RECORD ( num-emp NUMBER(4) NOT NULL, nombre empleados.nombre%TYPE, salario empleados.salario%TYPE); Permiten inicialización en tiempo de declaración: TYPE ventas IS RECORD ( hardware NUMBER(8) := 0, software NUMBER(10):= 0, servicios NUMBER(9) := 0); Las variables se declaran asociadas al tipo: un_emp emp_sal; otro_emp emp_sal; ventas_enero ventas; Los campos se referencian con la notación nominal ya vista: un_emp.salario := 7500; ventas_enero.hardware = 10.500; SELECT num_emp, nombre, salario INTO un_emp FROM empleados Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 175 - Mario Juncos Murcia WHERE num_emp = 7902; La asignación global también está permitida: otro_emp := un_emp; REGISTROS ANIDADOS Un nuevo tipo está disponible para su uso, incluso en la declaración de tipos adicionales: ejemplo: TYPE cuenta_cliente IS RECORD (nombre VARCHAR2(30), comercial NUMBER(4), cuenta ventas); mi_cliente cuenta_cliente; al definir mi_cliente del tipo cuenta_cliente puedo hacer referencia a: mi_cliente.nombre :='CHIPS, S.A.'; mi_cliente.comercial := 7902; mi_cliente.cuenta.hardware:= 299814; TABLAS(ARRAYS) El tipo TABLE es, como los registros, un tipo compuesto PL/SQL. A diferencia de los 'arrays' de otros lenguajes de programación las tablas de PL/SQL (no confundirlas con las tablas de la Base de Datos) no reservan el espacio en memoria estáticamente, sino que crecen a medida que se ocupan sus elementos. Actualmente las tablas PL/SQL solo pueden tener dos columnas. Una ha de ser de tipo BYNARY_INTEGER y actúa como índice, y otra de cualquier tipo escalar. La sintaxis es: tipo TYPE tipo_tabla IS TABLE OF variable%TYPE [NOT NULL] tabla.columna%TYPE registro/tabla%ROWTYPE INDEX BY BINARY_INTEGER; Ejemplo: TYPE tabla_nombre IS TABLE OF empleados.nombre%TYPE INDEX BY BINARY_INTEGER; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 176 - Mario Juncos Murcia Una vez definido el tipo tabla pasamos a definir tablas mi_tabla tabla_nombre; otra_tabla tabla_nombre; Por la indexación con el tipo BINARY_INTEGER la tabla tiene el rango -(2 31 - 1) a 2 31 - 1 Ejemplo: mi_tabla(7902) := 'FORD'; pero también será válido: mi_tabla(-100514) := 'JIMENEZ'; La asignación global también está permitida: otra_tabla := mi_tabla; Atributos de tablas PL/SQL Los atributos de las tablas facilitan la gestión de las variables de tipo TABLE permitiendo recorrer la tabla, contar y borrar los elementos. En general, para utilizar los atributos se empleara el siguiente formato: Variabledetabla.atributo[(parámetros)] Los parámetros harán referencia a valores de índice: - FIRST. Devuelve el valor (BINARY_INTEGER) de la clave o índice del primer elemento de la tabla. o Formato: variabletabla.FIRST - LAST. Devuelve el valor (BINARY_INTEGER) de la clave o índice del ultimo elemento de la tabla. o Formato: variabletabla.LAST Por ejemplo, para recorrer una tabla cuyo índice sabemos que tiene valores consecutivos podemos escribir: FOR i IN variabletabla.FIRST .. variabletabla.LAST LOOP …… - PRIOR. Devuelve el valor (BINARY_INTEGER) de la clave o índice del elemento anterior al elemento n. o Formato: variabletabla.PRIOR(n) - NEXT. Devuelve el valor (BINARY_INTEGER) de la clave o índice del elemento posterior al elemento n. o Formato: variabletabla.NEXT(n) Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 177 - Mario Juncos Murcia PRIOR y NEXT se pueden utilizar para recorrer una tabla (en cualquiera de los dos sentidos) con valores de índice no consecutivos. No obstante, deberemos tener cuidado con lo valores que devolverán en ambos extremos, ya que PRIOR del primer elemento devuelve NULL y lo mismo ocurre con NEXT del ultimo elemento. i:= variabletabla.FIRST WHILE i IS NOT NULL LOOP ……………….. i:= variabletabla.NEXT(i); END LOOP; - COUNT. Devuelve el número de filas que tiene una tabla. o Formato: Variabletabla.COUNT - EXISTS. Devuelve TRUE si existe el elemento n, en caso contrario devolver FALSE. Se utiliza para evitar el error ORA_1403 que se produce cuando intentamos acceder a un elemento que no existe en la tabla, lo cual levantaría la excepción NO_DATA_FOUND. o Formato: Variabletabla.EXISTS(n) - DELETE. Se utiliza para borrar elementos de una tabla. o variabletabla.DELETE. Borra todos los elementos de la tabla. o variabletabla.DELETE(n). Borra el elemento indicado por n. Si el valor de n es NULL no hará nada. o variabletabla.DELETE(nl, n2). Borra las filas comprendidas entre nl y n2, siendo nl>=n2 (en caso contrario no hará nada). Una tabla se elimina al salir del ámbito del programa en que se creo. No obstante, si quisiéramos eliminar todos los elementos de la tabla antes, en versiones que no disponen del atributo DELETE podemos crear una tabla vacía y asignársela a la tabla que queremos borrar: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 178 - Mario Juncos Murcia DECLARE TYPE T_tabla_emple IS TABLE OF emple%ROWTYPE INDEX BY BINARY_INTEGER; Tab_emple T_tabla_emple; Tablavacia T_tabla_emple; CURSOR c_emple IS SELECT * FROM emple; BEGIN ………….. /* El siguiente bucle carga en la tabla todas las filas de emple */ FOR v_reg_emple IN c_emple LOOP Tab_emple(vreg_emple.emp_no):= vreg_emple; END LOOP; …………… tab_emple := Tablavacia; END; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 179 - Mario Juncos Murcia Práctica 2 1 Evalúe las siguientes declaraciones. Determine cuáles de ellos no son legales, y explique por qué. DECLARE v_id NUMBER(4); v_x, v_y, v_z VARCHAR2(10); v_birthdate DATE NOT NULL; v_in_stock BOOLEAN :=1; 2. Determine el tipo de datos de las expresiones resultantes de las siguientes asignaciones. v_days_to_go := v_due_date - SYSDATE; v_sender := USER || „: „ TO_CHAR(v-dept_no); v_sum := €100,000 + €250,000; v_flag := TRUE; v_n1 := v_n2 > (2 * v_n3); v_value := NULL; 3. Cree un bloque anónimo para imprimir la frase “Mi Bloque PLSQL”en la pantalla. Mensaje Mi Bloque PLSQL En entorno de SQL> con VARIABLE nombre_variable tipo(escala) definimos una variable que luego podremos usar en un bloque PLSQL como: VARIABLE g_message VARCHAR2(30) BEGIN :g_message := „Mi Bloque PLSQL‟; -- fijaros que hay que poner :nombre_variable END; PRINT g_message Si queremos pedir por pantalla el valor de una variable, lo hacemos con ACCEPT. Este valor lo podemos utilizar en un bloque PLSQL utilizando & como en: ACCEPT p_num PROMPT 'Por favor Introduzca un número:‟ DECLARE v_num1 NUMBER(9,2) := &p_num; Los ACCEPT hay que ponerlos en el entorno SQL> y los bloques de codigo es preferible escribirlos en un editor y después con COPY PASTE pasarlos al entorno SQL> Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 180 - Mario Juncos Murcia SET SERVEROUTPUT ON Visualiza la salida a traves de DBMS_OUTPUT.PUT_LINE(„mensaje‟) Práctica 3 DECLARE V_weight NUMBER(3) :=600; V_message VARCHAR2(255) := 'Product 11002'; BEGIN DECLARE V_weight NUMBER(3) := 1; V_message VARCHAR2(255) := 'Product 11001'; v_new_locn VARCHAR2:= „Europa‟; BEGIN V_weight := v_weight +1; v_new_locn := „Sur „ || v_new_locn ; END V_weight := v_weight +1; V_message := V_message || „ esta en stock‟; v_new_locn := „Sur „ || v_new_locn ; END; 1.- Observe el bloque PLSQL y determine los siguientes valores de acuerdo con las reglas de ámbito. El valor de V_WEIGHT en el subbloque es El valor de V_NEW_LOCN en el subbloque es El valor de V_WEIGHT en el bloque principal es El valor de V_MESSAGE en el bloque principal es El valor de V_NEW_LOCN en el bloque principal es 2. Cree y ejecute un bloque PL/SQL que acepte dos números por medio de variables de SQL. Se debería dividir el primer número entre el segundo, y después añadir el segundo número al resultado. El resultado debería escribirse en una variable PLSQL e imprimirse en la pantalla a través de una variable de SQL Por favor Introduzca el primer número: 2 Por favor Introduzca el segundo número: 4 PL/SQL procedure successfully completed. V_RESULT 4.5 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 181 - Mario Juncos Murcia 3.- Genere un bloque PLSQL que calcule la comisión total para un año. El salario anual y el porcentaje anual de comisión se pasarán al bloque PLSQL a través de variables de sustitución SQL y el importe de las comisiones tendrá que ser convertido de un número entero a un decimal (por ejemplo, 15 a 0.15). Si el salario es nulo, asígnele cero antes de calcular la compensación total. Utilizar la función NVL para gestionar los valores nulos. (Para comprobar el caso de la función NVL, necesitaremos escribir NULL en el prompt; ya que pulsar [Return] provoca un error.) Por favor introduzca el salario: 50000 Por favor introduzca el porcentaje: 10 PL/SQL procedure successfully completed. G_TOTAL 55000 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 182 - Mario Juncos Murcia Práctica 4 1.- Cree un bloque PLSQL que seleccione el departamento con el número más alto de la tabla DEPT y lo almacene en una variable SQL. Imprima los resultados en la pantalla. Guarde su bloque PLSQL en un archivo llamado p4ql.sql. 2.- Cree un bloque PLSQL que inserte un nuevo departamento en la tabla DEPT. Guarde su bloque PLSQL en un archivo llamado p4q2.sql.  Utilice el número del departamento recuperado del ejercicio 1 y añada 10 al número como número de departamento de entrada para el nuevo departamento.  Cree un parámetro para el nombre del departamento.  Por el momento deje la ubicación como nula.  Ejecute el bloque PLSQL. Introduzca el nombre del departamento: EDUCACION PL/SQL procedure successfully completed.  Muestre el nuevo departamento que ha creado. DEPTNO DNAME LOC 50 EDUCATION 3.- Cree un bloque PLSQL que actualice la ubicación de un departamento existente. Guarde el bloque PLSQL es un archivo llamado p4q3.sql.  Cree un parámetro para el número del departamento.  Cree un parámetro para la ubicación del departamento.  Compruebe el bloque PLSQL. Introduzca el numero de departamento : 50 Introduzca la localidad del departamento: HOUSTON  Muestre el número de departamento el nombre del departamento y la ubicación del departamento actualizado. DEPTNO DNAME LOC 50 EDUCATION HOUSTON 4. Cree un bloque PLSQL que suprima el departamento creado en el ejercicio 2. Guarde el bloque PLSQL en un archivo llamado p4q4.sql.  Cree un parámetro para el número de departamento.  Imprima en la pantalla el número de filas afectadas.  Compruebe el bloque PLSQL.  ¿Qué pasa si introduce un número de departamento que no existe?  Confirme que el departamento ha sido suprimido. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 183 - Mario Juncos Murcia COMPARACIONES LOGICAS PL/SQL soporta la comparación de variables y constantes en las sentencias SQL y PL/SQL. Generalmente están compuestas de expresiones simples unidas a través de operadores ( <, >, =, !=, >=, <= ), y reciben el nombre de Expresiones Booleanas. Las Expresiones Booleanas en PL/SQL, siempre toman valores CIERTO, FALSO o NULO (TRUE, FALSE o NULL). En caso de utilizarse para control condicional, el valor NULO se considera como FALSO. Existen tres tipos de Expresiones Booleanas: - Numéricas: a > 7 2*a = b b >= a - De Carácter: Las comparaciones se realizan según orden alfabético. nombre1 > nombre2 nombre1 ='SMITH' tipo <> 'ORDENADOR' - De Fecha: Las comparaciones se evalúan en el tiempo. fecha_nacimiento <'20-OCT-65' hiredate = '27-NOV-87' CONTROL CONDICIONAL La sentencia IF ... THEN ... ELSE ... END IF permite controlar qué conjunto de instrucciones se ejecutan, en función de una condición. La sintaxis es: IF <condición> THEN .... .... [ELSIF <condición> THEN ... ...] [ELSE ... ...] END IF; Si la condición inicial es CIERTA, se ejecutan las instrucciones posteriores a THEN. Si es FALSA, pueden evaluarse otras condiciones con la cláusula ELSIF. La cláusula 'ELSE' provoca la ejecución de las instrucciones que le siguen, caso de que ninguna Solo hay un IF Puede haber varios ELSIF ( puede que no haya ninguno) Sólo puede haber un ELSE ( puede que no haya ninguno) Sólo hay un END IF Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 184 - Mario Juncos Murcia condición haya sido CIERTA. Ejemplo: DECLARE saldo cuentas.sal%TYPE; cuenta CONSTANT NUMBER(1):= 3; deuda CONSTANT NUMBER(2):= 50; BEGIN SELECT sal INTO saldo FROM cuentas WHERE num_cuenta = cuenta; IF saldo >= deuda THEN UPDATE cuentas SET sal = sal - deuda WHERE num_cuenta = cuenta; ELSE INSERT INTO temp VALUES (cuenta, saldo, deuda,'SIN FONDOS'); END IF; END; Si una SELECT recupera mas de una fila o ninguna se produce una excepción. CONTROL ITERATIVO El control iterativo ofrece la posibilidad de repetir o saltar determinadas partes de un bloque PL/SQL. Las sentencias asociadas son LOOP y GOTO. Sentencia LOOP: En su formato mas sencillo construye bucles infinitos: LOOP conjunto de sentencias END LOOP; Bucles FOR: FOR var IN [REVERSE] ent1..ent2 LOOP conjunto de sentencias END LOOP; Las sentencias internas al bucle se ejecutan tantas veces como indique el rango de enteros expresado. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 185 - Mario Juncos Murcia El índice 'var' no tiene que declararse como variable, implícitamente recibe el tipo NUMBER y no puede referenciarse fuera del bucle. Dentro de él y en cada iteración, se comporta como una constante: puede consultarse, pero no variar su valor. Bucles WHILE: Un bucle WHILE tiene asociado una condición. Las instrucciones incluidas en el bucle se ejecutan siempre que la condición sea cierta. Lógicamente esta se evalúa en cada una de las iteraciones del bucle. El bucle finaliza una vez que la condición evaluada de como resultado FALSO o NULO. WHILE (Condición) LOOP instrucciones END LOOP; DECLARE Salario empleados.salario%TYPE; jefe empleados.num_sup%TYPE; nombre empleados.nombre%TYPE; ini CONSTANT NUMBER(4):=7902; BEGIN SELECT salario, num_sup INTO salario, jefe FROM empleados WHERE num_emp = ini; WHILE salario < 4000 LOOP SELECT salario, num_sup, nombre INTO salario, jefe, nombre FROM empleados WHERE num_emp = jefe; END LOOP; INSERT INTO temp VALUES (salario, nombre); COMMIT; END; Sentencia EXIT: La sentencia EXIT se puede utilizar en la parte interna de cualquier tipo de bucle, con la finalidad de abortarlo prematuramente. Es esencial, particularmente, en los bucles infinitos. LOOP contador := contador + 1; IF contador > 1000 THEN EXIT; END IF; ... END LOOP; La sentencia EXIT también admite el formato: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 186 - Mario Juncos Murcia EXIT WHEN condición; Sentencia GOTO: La sentencia GOTO permite variar el flujo de ejecución dentro de un bloque PL/SQL. Para utilizarla es necesario etiquetar determinadas posiciones en el bloque. Es factible bifurcar la ejecución a cualquier etiqueta, siempre y cuando no se intente entrar en una secuencia de instrucciones particular, desde fuera de ella (ej.: no se puede hacer GOTO a una etiqueta interior a un bucle desde fuera de él). Las etiquetas se referencian sintácticamente de la siguiente forma: << etiqueta >> Ejemplo incorrecto: ... GOTO mi_etiqueta; IF a > b THEN b := b - c; << mi_etiqueta >> x := x + 1; END IF; Ejemplo correcto: SELECT salario, comision, puesto INTO salario, comision, trabajo FROM empleados WHERE num_emp = 7902; IF trabajo = 'VENDEDOR' THEN bonificacion := comision * 2; GOTO suma_bono; END IF; IF trabajo = 'CLERK' THEN bonificacion := salario * 0.15; GOTO suma_bono; END IF; .... << suma_bono >> total := total + bonificacion; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 187 - Mario Juncos Murcia Práctica 5 1.-Crear la tabla MENSAGES (Con una columna llamada RESULTADOS de tipo VARCHAR2(100)). Escriba un bloque PL/SQL para insertar números en la tabla MENSAJES.  Inserte los números 1 a 1 0 excepto 6 y 8.  Haga Commit antes del final del bloque.  Haga una selección en la tabla MENSAJES para verificar que funcionó su bloque PL/SQL. RESULTADOS 1 2 3 4 5 7 9 10 2. Cree un bloque PLSQL que calcule el importe de comisión de un empleado, basándose en el sueldo del empleado.  Acepte el número de empleado como entrada de usuario con un parámetro de sustitución SQL.  Si el sueldo del empleado es inferior a 1000€, establezca el importe de comisión del empleado en un 10% del sueldo.  Si el sueldo del empleado está entre 1000€ y 1500€, establezca el importe de comisión del empleado en un 15% del sueldo.  Si el sueldo del empleado pasa de 1500€, establezca el importe de comisión del empleado en el 20% del sueldo.  Si el sueldo del empleado es NULL, establezca el importe de comisión del empleado en 0.  Haga commit.  Compruebe el bloque PLSQL para cada caso utilizando los siguientes casos de prueba, y compruebe todas las comisiones actualizadas. Número de empleado Sueldo Comisión resultante 7369 800 80 7934 1300 195 7499 1600 320 8000 NULL 0 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 188 - Mario Juncos Murcia 3.- Añada una nueva columna en la tabla EMP para almacenar asteriscos (llamarla ASTERISCOS) 4.- Cree un bloque PL/SQL que recompense a un empleado añadiendo un asterisco en la columna ASTERISCOS por cada 100€ del sueldo del empleado. Redondee el sueldo del empleado hasta el número entero más cercano. Guarde el bloque PL/SQL en un archivo llamado p5q4. sql.  Acepte el Número del empleado como entrada del usuario con un parámetro de sustitución SQL.  Inicialice una variable que contenga una cadena vacía.  Añada un asterisco a la cadena por cada 100€ del sueldo. Por ejemplo, si el empleado tiene un sueldo de 800€, la cadena de asteriscos contará con ocho asteriscos.  Actualice la columna ASTERISCOS del empleado con la cadena de asteriscos.  Haga commit.  Compruebe el bloque con un empleado que no tiene sueldo y con un empleado que tiene sueldo. Introduzca el número de empleado: 7934 PL/SQL procedure successfully completed. Introduzca el número de empleado: 8000 PL/SQL procedure successfully completed. EMPNO SAL ASTERISCOS 8000 7934 1300 ************* Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 189 - Mario Juncos Murcia Práctica 6 1.- Crear una nueva tabla (llámala AYUDA)en la que almacenar empleados y sus sueldos. 2. Escriba un bloque PLSQL para recuperar el nombre y el sueldo de un empleado concreto de la tabla EMP basándose en el número del empleado. Utilice tablas PLSQL.  Declare dos tablas PLSQL, ENAME_TABLE y SAL_TABLE para almacenar temporalmente los nombres y los sueldos.  Puesto que todos los nombres y sueldos son recuperados dentro del loop, almacénelos en tablas PLSQL.  Fuera del loop, transfiera los nombres y sueldos de las tablas PLSQL a la tabla AYUDA.  Vacíe la tabla AYUDA y ejecute el ejercicio. Introduzca el número del empleado: 7934 PL/SQL procedure successfully completed. NAME SALARY MILLER 1300 Introduzca el número del empleado: 7876 PL/SQL procedure successfully completed. NAME SALARY ADAMS 1100 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 190 - Mario Juncos Murcia MANEJO DE CURSORES Cuando PL/SQL debe procesar una sentencia SQL, entonces genera una área de trabajo temporal que recibe el nombre de área de contexto (context área), y que es utilizada para ejecutar la sentencia y almacenar información relativa a esta. Un cursor referencia un área de contexto y permite acceder a la información contenida en esta. PL/SQL utiliza dos tipos de cursores: Explícitos: Provocados por el programador para controlar el acceso a múltiples filas obtenidas como consecuencia de la ejecución de una sentencia SELECT. Implícitos: Son generados automáticamente por PL/SQL cuando se ejecuta un sentencia SQL no asociada con un cursor explícito. CURSORES EXPLICITOS El número de filas devueltas por una consulta puede ser Cero uno o mayor de uno. Cuando el número de filas devueltas es superior a uno, entonces es interesante definir un CURSOR con la finalidad de:  Realizar determinados tratamientos antes de utilizar la información extraída.  Mantener la pista de que fila retornada esta siendo procesada en cada instante. Existen cuatro sentencias asociadas con el manejo de cursores: CURSOR Otorga un nombre de programador al cursor y declara la sentencia SELECT asociada. La sintaxis es: CURSOR nombre [(parámetro tipo,...)] IS sentencia_select ; Los parámetros, como veremos en los ejemplos, pueden utilizarse para cualificar la consulta. Ejemplos: CURSOR c1 IS SELECT nombre FROM empleados WHERE salario > 2000; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 191 - Mario Juncos Murcia CURSOR c2 (num NUMBER) IS SELECT nombre, salario, comision FROM empleados WHERE num_emp = num; OPEN Inicializa y abre el cursor, es decir, ejecuta la sentencia SELECT asociada. OPEN nombre [(param1 [, param2 ...])]; Ejemplos: OPEN c1; OPEN c2 (empleado); OPEN c2 (7902); FETCH Asocia los valores de cada una de las filas devueltas con determinadas variables PL/SQL. Se puede ejecutar tantas veces como filas hayan sido devueltas por la sentencia SELECT, pero no puede volver a la fila anterior. La sintaxis es: FETCH nombre_cur INTO var1 [,var2 ...]; Ejemplos: FETCH c1 INTO nombre_var; FETCH c2 INTO nomb,salar,comis; CLOSE Cierra el cursor, liberando los recursos que este hubiera obtenido. CLOSE nombre Ejemplo: CLOSE c1; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 192 - Mario Juncos Murcia ATRIBUTOS PREDEFINIDOS PARA CURSORES Todos los cursores declarados explícitamente por el programador disponen de cuatro atributos predefinidos: %NOTFOUND nombre_cursor%NOTFOUND devuelve TRUE si el último FETCH ejecutado ha fallado, puesto que se han agotado las filas devueltas por la consulta. LOOP FETCH c3 INTO nombre, num_dept; EXIT WHEN c3%NOTFOUND; ... END LOOP; %FOUND nombre-cursor%FOUND es el atributo lógico contrario al anterior. Devuelve TRUE si el último FETCH ha tenido éxito, pues aun quedan filas por evaluar. LOOP FETCH c3 INTO nombre, num_dept; IF c3%FOUND THEN INSERT INTO tabla ... ... ELSE EXIT; END IF; ... END LOOP; %ROWCOUNT nombre-cursor%ROWCOUNT devuelve el número de filas que ya se han tratado con FETCH. LOOP FETCH c3 INTO nomb, dept; IF c3%ROWCOUNT > 10 THEN .... END LOOP; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 193 - Mario Juncos Murcia %ISOPEN nombre cursor%ISOPEN devuelve TRUE si el cursor esta abierto (OPEN), y FALSE en caso contrario. Ejemplo: IF c3%ISOPEN THEN FETCH c3 INTO nombre, salario; ELSE OPEN c3; END IF; DECLARE num1 tabla1.nl%TYPE; num2 tabla1.n2%TYPE; num3 tabla1.n3%TYPE; resultado temp.col1%TYPE; CURSOR c1 IS SELECT n1, n2, n3 FROM tabla1 WHERE num = 1; BEGIN OPEN c1; LOOP FETCH c1 INTO num1, num2,num3; EXIT WHEN c1%NOTFOUND; resultado := num2/(num1+num3); INSERT INTO temp VALUES (resultado); END LOOP; CLOSE c1; COMMIT; END; BUCLES FOR para CURSORES Los BUCLES FOR para CURSORES provocan implícitamente la apertura del cursor (OPEN), el barrido de todas las filas asociadas (FETCH) y el cierre de este (CLOSE). La información se almacena implícitamente en un registro que es conveniente definir. Su utilización esta recomendada desde el punto de vista de simplificación de código. Ejemplo: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 194 - Mario Juncos Murcia DECLARE resul temp.col1%TYPE; CURSOR c1 IS SELECT n1, n2, n3 FROM tabla1 WHERE num = 1; BEGIN FOR c1_rec IN c1 LOOP resul := c1_rec.n2/(c1_rec.n1 + c1_rec.n3); INSERT INTO temp VALUES (resul); END LOOP; COMMIT; END; EL CURSOR IMPLICITO SQL Cada vez que ejecutamos una sentencia SQL desde un bloque PL/SQL, y esta no esta asociada a un cursor explícito, ORACLE genera un cursor implícito. PL/SQL permite referirse a el con la nomenclatura 'SQL%' . Lógicamente, con los cursores implícitos, no se pueden utilizar las instrucciones OPEN, FETCH, CLOSE, pero si los atributos de cualquier cursor, con la siguiente semántica: SQL%NOTFOUND Devuelve TRUE si una sentencia INSERT, DELETE o UPDATE no ha afectado a ninguna fila, o cuando una sentencia SELECT no ha devuelto ninguna fila. SQL%FOUND Devuelve TRUE si INSERT, DELETE o UPDATE han afectado a alguna fila, o si una SELECT ha devuelto información. SQL%ROWCOUNT Devuelve el número de filas afectadas por un INSERT, DELETE o UPDATE, o el número de filas devueltas por una SELECT. SQL%ISOPEN ORACLE cierra automáticamente un cursor implícito una vez que se ha ejecutado la sentencia SQL. Es por ello que, SQL%ISOPEN siempre devuelve FALSE. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 195 - Mario Juncos Murcia USO DE CURSORES PARA ACTUALIZAR FILAS Cursor FOR UPDATE. Hasta el momento hemos venido utilizando los cursores sólo para seleccionar datos, pero también se puede usar el nombre de un cursor que apunta a una fila para realizar una operación de actualización en esa fila. Cuando se prevea esa posibilidad, a la declaración del cursor habrá que añadirle FOR UPDATE al final. CURSOR nombrecursor <declaración del Cursor> FOR UPDATE [NO wait] FOR UPDATE indica que las filas seleccionadas por el cursor van a ser actualizadas o borradas. Todas las filas seleccionadas serán bloqueadas tan pronto se abra (OPEN) el cursor y serán desbloqueadas al terminar las actualizaciones (al ejecutar COMMIT explícita o implícitamente). Una vez declarado un cursor FOR UPDATE, se incluirá el especificador CURRENT OF nombredecursor en la cláusula WHERE para actualizar (UPDATE o borrar (DELETE) la ultima fila recuperada mediante la Orden FETCH. {UPDATE / DELETE} ... WHERE CURRENT OF nombrecursor El siguiente procedimiento subirá el salario a todos los empleados del departamento indicado en la llamada. La subida será el porcentaje indicado en la llamada: CREATE OR REPLACE PROCEDURE subir_salario_dpto (Vp_num_dpto NUMBER, Vp_pct subida NUMBER) AS CURSOR c_emple IS SELECT oficio, salario FROM emple WHERE dept_no = vp_num_dpto FOR UPDATE; Vc_reg_emple c_emple%ROWTYPE; V_inc NUMBER(8,2); BEGIN OPEN c_emple; FETCH c_emple INTO vc reg_emple; WHILE c emple%FOUND LOOP V_inc := (vc_reg_emple.salario /100) * Vp_pct_subida; UPDATE emple SET salario = salario + v_inc WHERE CURRENT OF c_emple; FETCH c_emple INTO vc_reg_emple; END LOOP; END subir_ salario_dpto; La cláusula FOR UPDATE bloquea las filas afectadas por el cursor. Al cerrar el cursor no es necesario hacer COMMIT. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 196 - Mario Juncos Murcia Con NO WAIT si las filas están ya bloqueadas devuelve un error, si no lo ponemos espera hasta que se desbloqueen las filas WHERE CURRENT hace referencia a la fila actual del cursor especificado. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 197 - Mario Juncos Murcia Práctica 7 1.- Cree un bloque PLSQL que determine los empleados con sueldos más altos.  Acepte un número n como entrada de usuario con un parámetro de sustitución SQL.  En un bucle, obtenga los apellidos y sueldos de los n empleados con sueldos más altos de la tabla EMP.  Almacene los nombres y sueldos en la tabla AYUDA.  Suponga que no hay dos empleados con el mismo sueldo.  Compruebe varios casos, como un n = 0, o donde n es mayor que el número de empleados de la tabla EMP. Vacíe la tabla AYUDA después de cada prueba. Introduzca el numero de empleados que quiere listar: 5 NAME SALARY KING 5000 FORD 3000 SCOTT 3000 JONES 2975 BLAKE 2850 2.- Considere el caso de que varios empleados tienen el mismo salario. Si se incluye a una persona, deberá incluirse también a todas las personas con el mismo sueldo.  Por ejemplo, si el usuario introduce un valor de 2 para n, aparecerían King, Ford y Scott. (Estos dos últimos empleados tienen el segundo sueldo más alto).  Si el usuario introduce un valor de 3, aparecerán King, Ford, Scott y Jones.  Suprima todas las filas de AYUDAy pruebe el ejercicio. Introduzca el numero de empleados que quiere listar: 2 NAME SALARY KING 5000 FORD 3000 SCOTT 3000 Introduzca el numero de empleados que quiere listar: 3 NAME SALARY KING 5000 FORD 3000 SCOTT 3000 JONES 2975 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 198 - Mario Juncos Murcia Práctica 8 1. Ejecute una consulta para recuperar todos los departamentos y empleados de cada departamento. Inserte los resultados en la tabla MENSAJES. Utilice un cursor para recuperar el número de departamento y transfiéralo a un cursor para recuperar los empleados de ese departamento. RESULTADOS . KING Departamento 10 CLARK Departamento 10 MILLER Departamento 10 DOE Departamento 10 JONES Departamento 20 FORD Departamento 20 SMITH Departamento 20 SCOTT Departamento 20 ADAMS Departamento 20 BLAKE Departamento 30 MARTIN Departamento 30 ALLEN Departamento 30 TURNER Departamento 30 JAMES Departamento 30 WARD Departamento 30 15 rows selected. 2. Modifique p5q4.sql para incorporar la funcionalidad FOR UPDATE y WHERE CURRENT OF al procesamiento de cursores. EMPNO SAL ASTERISCOS 8000 7900 950 ********** 7844 1500 *************** Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 199 - Mario Juncos Murcia MANEJO DE ERRORES PL/SQL como muchos otros lenguajes de programación de los últimos años, proporciona un mecanismo para el manejo de errores basado en EXCEPCIONES. Una EXCEPCION se podría definir como un evento que se produce cuando la ejecución del código provoca una situación anómala. El manejo de errores basado en EXCEPCIONES permite mantener una legibilidad de código desconocida en lenguajes de generaciones anteriores. PL/SQL dispone de un conjunto de sentencias que permiten manejar las EXCEPCIONES que se pudieran provocar durante la ejecución del código. Existe una colección de excepciones predefinidas en el lenguaje como ZERO_DIVIDE, que se produce cuando se intenta dividir por cero. Además, el programador puede generar sus propias excepciones (no predefinidas en el lenguaje) y provocar que se produzcan (o elevan) bajo determinadas circunstancias. La gran ventaja del tratamiento de errores por excepciones, radica en el aumento de la legibilidad del código, y su más eficiente mantenimiento, ya que las acciones a tomar cuando se eleva una excepción, se escriben aparte en el código fuente; no mezclando así el tratamiento habitual de la información con las acciones a tomar en casos anómalos. Formalmente existen tres acciones asociadas con el manejo de EXCEPCIONES: DECLARACION: Las excepciones no predefinidas se declaran en el segmento DECLARE de un bloque PL/SQL. DECLARE com_cero EXCEPTION; salario NUMBER(7,2); Lógicamente, las excepciones predefinidas no se declaran. ELEVACION: Las excepciones no predefinidas se pueden elevar explícitamente con la sentencia RAISE: IF comisión = 0 OR comisión IS NULL THEN RAISE com_cero; END IF; Las excepciones predefinidas se elevan cuando se produce el evento al que están asociadas. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 200 - Mario Juncos Murcia MANEJO: La captura y tratamiento de las excepciones se realiza en los MANEJADORES DE EXCEPCIONES. Es en estos donde se expresan las acciones a tomar en cada caso. Los MANEJADORES DE EXCEPCIONES siempre se colocan al final del bloque PL/SQL. EXCEPTION WHEN com_cero THEN -- proceso del error aquí WHEN excepcion2 THEN ..... END; Cláusula WHEN OTHERS. La cláusula WHEN OTHERS se puede utilizar en un manejador de excepciones con la finalidad de tratar todas aquellas que no dispongan de su cláusula WHEN particular. EXCEPTION WHEN excepcion1 THEN ..... WHEN OTHERS THEN -- tratamiento de cualquier otro error END; La sentencia nula (NULL) es especialmente útil en estos casos. WHEN OTHERS THEN NULL; Cuando se eleva una excepción, ya sea implícitamente o utilizando el comando RAISE, el flujo de control continua por el manejador de estas, situado en el bloque actual. Si dicho bloque no contuviera manejador (o la excepción elevada no esta contemplada en este), entonces la excepción se propaga hacia afuera, al bloque que contuviera al actual, y así sucesivamente, hasta que se encontrara un manejador adecuado. Una vez que la excepción ha sido tratada, la ejecución continua por la instrucción siguiente al bloque que la ha tratado. Si ningún manejador consigue tratar una excepción, entonces esta se propaga al entorno que invocó a PL/SQL, ya sea SQL*Plus, SQL*Forms o un lenguaje precompilado. Por ello es recomendable por claridad que cada instrucción en la que pueda producirse una EXCEPTION tenga asociado un manejo de excepciones BEGIN BEGIN ... EXCEPTION ... END; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 201 - Mario Juncos Murcia BEGIN .... EXCEPTION ... END; ......... END;EXCEPCIONES PREDEFINIDAS Las siguientes excepciones se encuentran predefinidas en el lenguaje y se elevan en los casos indicados. Código error Oracle Valor de SOL CODE Excepción Se disparan cuando... ORA_06530 -6530 ACCESS_INTO_NULL Se intenta acceder a los atributos de un objeto no inicializado. ORA_06531 -6531 COLLECTION_IS_NULL Se intenta acceder a elementos de una colección que no ha sido inicializada. ORA_06511 -6511 CURSOR_ALREADY_OPEN Intentamos abrir un cursor que ya se encuentra abierto. ORA_00001 -1 DUP_VAL_ON_INDEX Se intenta almacenar un valor que crearla duplicados en la clave primaria o en una columna con la restricción UNIQUE. ORA_01001 -1001 INVALID_CURSOR Se intenta realizar una operación no permitida sobre un cursor (por ejemplo, cursor que no se ha abierto). ORA_01722 -1722 INVALID_NUMBER Fallo al intentar convertir una cadena a un valor numérico. ORA_01017 -1017 LOGIN_DENIED Se intenta conectar a ORACLE con un usuario o una clave no validos. ORA_01012 -1012 NOT_LOGGED_ON Se intenta acceder a la base de datos sin estar conectado a Oracle. ORA_01403 -100 NO_DATA_FOUND Una sentencia SELECT ... INTO ... no devuelve ninguna fila. ORA_06501 -6501 PROGRAM_ERROR Hay un problema interno en la ejecución del programa. ORA_06504 -6504 ROWTYPE_MISMATCH La variable del cursor del HOST y la variable del cursor PLSQL pertenecen a tipos incompatibles. ORA_06533 -6533 SUBSCRIPT_OUTSIDE_LIMIT Se intenta acceder a una tabla anidada o a un array con un valor de índice ilegal ( por ejemplo, negativo). ORA_06500 -6500 STORAGE_ERROR El bloque PLSQL se ejecuta fuera de memoria (o hay algún otro error de memoria) ORA_00051 -51 TIMEOUT_ON_RESOURCE Se excede el tiempo de espera para un recurso. ORA_01422 -1422 TOO_MANY_ROWS Una sentencia SELECT ... INTO ... devuelve mas de una fila. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 202 - Mario Juncos Murcia ORA_06502 -6502 VALUE_ERROR Un error de tipo aritmético, de conversión, de truncamiento... ORA_01476 -1476 ZERO_DIVIDE Se intenta la división entre cero. OTRAS EXCEPCIONES Existen otros errores internos de Oracle, similares a los asociadas a las excepciones internas pero que no tienen asignada una excepción, sino un código de error y un mensaje d e e r r o r , a los que se accede mediante las funciones SQLCODE y SQLERRM. Cuando se produce uno de estos errores se transfiere el control a la sección EXCEPTION, donde se tratara el error en la cláusula WHEN OTHERS: EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(„Error: „ || SQLCODE || SQLERRM ); ......... END; En este ejemplo se muestra al usuario el texto 'ERROR' con el CODIGO DE ERROR y el MENSAJE DE ERROR utilizando las funciones correspondientes. Estas dos funciones, SQLCODE y SQLERRM, no son accesibles desde órdenes SQL*Plus, pero se pueden pasar a este entorno a través de soluciones como la siguiente: WHEN OTHERS THEN :cod_err := SQLCODE; :msg_err := SQLERRM; ROLLBACK; EXIT; END; También podemos asociar una excepción a alguno de estos errores internos que no TIENEN excepciones predefinidas asociadas. Para ello procederemos así: 1. Definimos una excepción en la sección de declaraciones Como si fuese una excepción definida por el. usuario: <nombreexcepción> EXCEPTION; 2. Asociamos esa excepción a un determinado código de error mediante la directiva del compilador PRAGMA EXCEPTION_INIT, según el formato siguiente: PRAGMA EXCEPTION_INIT(<nombre excepción>, <número_de_error_oracle>) 3. Indicamos el tratamiento que recibirá la excepción en la sección EXCEPTION como si se Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 203 - Mario Juncos Murcia tratase de cualquier otra excepción definida o predefinida. DECLARE err_externo EXCEPTION; __ Se define la excepción de usuario PRAGMA EXCEPTION_INIT (err_externo, _1547); __ Se asocia con un error de Oracle .................. BEGIN /*. no hay que levantar la excepción, ya que llegado el caso Oracle lo hará */ .......... EXCEPTION ......... WHEN err_externo THEN __ Se trata como cualquier otra. <tratamiento>; END; Utilización de RAISE_APPLICATION_ERROR En el paquete DBMS_STANDARD se incluye un procedimiento muy útil llamado RAISE_APPLICATION_ERROR que sirve para levantar errores y definir y enviar mensajes de error. Su formato es el siguiente: RAISE APPLICATION ERROR(número_de_error, mensaje_de_error) Donde número de error es un número comprendido entre -20000 y -20999, y Mensaje_de_error es una cadena de hasta 512 bytes. Cuando un subprograma hace esta llamada, se levanta la excepción y se deshacen los cambios realizados por el subprograma. Ejemplos de aplicación PROCEDURE subir_sueldo (Num emple NUMBER, incremento NUMBER) IS Salario_actual NUMBER; BEGIN SELECT salario INTO salario_actual FROM empleados WHERE Emp_no = Num_emple; IF salario_actual IS NULL THEN Raise_ application_error (_20010, „ Salario Nulo‟); ELSE Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 204 - Mario Juncos Murcia UPDATE empleados SET sueldo = Salario_actual + incremento WHERE Empno = Num_emple; ENDIF; END subir_sueldo; Control de transacciones Una transacción se puede definir corno un conjunto de operaciones que se realizan en la base de datos. Una transacción, por tanto, no se circunscribe al ámbito de una orden SQL o al de un bloque PL/SQL, sino que es el usuario (el programador, en este caso) quien decide cuales serán las operaciones que compondrán la transacción. Oracle garantiza la consistencia de los datos en una transacción en términos de VALE TODO o NO VALE NADA, es decir, o se ejecutan todas las operaciones que componen una transacción o no se ejecuta ninguna. así pues, la base de datos tiene un estado antes de la transacción y un estado después de la transacción, pero no hay estados intermedios. Una transacción comienza con la primera orden SQL de la sesión del usuario o con la primera orden SQL posterior a la finalización de la transacción anterior. La transacción finaliza cuando se ejecuta un comando de control de transacciones (COMMIT o ROLLBACK), una orden de definición de datos (DDL) o cuando finaliza la sesión. BEGIN UPDATE cuentas SET saldo = saldo – v_importe_tranfer WHERE num_cta = v_cta_origen; UPDATE cuentas SET saldo = saldo + v_importe_tranfer WHERE num_cta = v_cta_destino; COMMIT WORK; .......... EXCEPTION WHEN OTHERS THEN ROLLBACK WORK; END; En el ejemplo anterior se garantiza que la transferencia se llevará a cabo totalmente o que no se realizará ninguna operación, pero en ningún caso se quedará a medias. El comando COMMIT. Da por concluida la transacción actual y hace definitivos los cambios efectuados, liberando las files bloqueadas. Sólo después de que se ejecute el COMMIT los demos usuarios tendrán acceso a los datos modificados. El comando ROLLBACK. Da por concluida la transacción actual y deshace los cambios que se pudiesen haber producido en la misma, liberando las files bloqueadas. Se utiliza especialmente Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 205 - Mario Juncos Murcia cuando no se puede concluir una transacción porque se levanta una excepción. ROLLBACK implícitos. Cuando un subprograma almacenado falla y no se controla la excepción que produjo el fallo, Oracle automáticamente ejecuta ROLLBACK sobre todo lo realizado por el subprograma, salvo que en el subprograma hubiese algún COMMIT, en cuyo caso lo confirmado no sería deshecho. SAVEPOINT. Se utiliza para poner marcas o puntos de salvaguarda al procesar transacciones. Se utiliza en conjunción con ROLLBACK TO. Esto permite deshacer parte de una transacción CREATE OR REPLACE PROCEDURE prueba savepoint (numfilas POSITIVE) AS BEGIN SAVEPOINT NIGUNA; INSERT INTO temp1 (col1) VALUES ('PRIMERA FILA'); SAVEPOINT UNA; INSERT INTO temp1 (col1) VALUES ('SEGUNDA FILA'); SAVEPOINT DOS; IF numfilas = 1 THEN ROLLBACK TO UNA; ELSIF numfilas = 2 THEN ROLLBACK TO DOS; ELSE ROLLBACK TO NINGUNA; END IF; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; END; Oracle establece un punto de salvaguarda implícito cada vez que se ejecuta una sentencia de manipulación de dates. En el caso de que la sentencia falle, Oracle restaurara automáticamente los dates a sus valores iniciales. Podemos observar que ROLLBACK TO <punto de salvaguarda> deshace el trabajo realizado sobre la base de datos después del punto indicado, incluyendo posibles bloqueos. No obstante, tampoco se confirma el trabajo hecho hasta el punto de salvaguarda. La transacción no finaliza hasta que se ejecuta un comando de control de transacciones COMMIT o ROLLBACK, o hasta que finaliza la sesi6n (o se ejecuta una orden de definición de datos DDL). El ámbito de los puntos de salvaguarda es el definido por la transacción desde que comienza hasta que termina, por tanto, trasciende de las reglas de ámbito y visibilidad de otros identificadores. Por omisión, el número de SAVEPOINT está limitado a cinco por sesión, pero se puede cambiar Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 206 - Mario Juncos Murcia en el parámetro SAVEPOINTS del fichero de inicialización de Oracle hasta 255. Cuando se ejecuta un ROLLBACK TO <marca>, todas las marcas después del punto indicado desaparecen (la indicada no desaparece). También desaparecen todas las marcas cuando se ejecuta un COMMIT. Los nombres de las marcas son identificadores no declarados y se pueden reutilizar. SET TRANSACTION READ ONLY. Establece el comienzo de una transacción de solo lectura. Se utiliza para garantizar la consistencia de los datos recupererados entre distintas consultas. Todas las consultas que se ejecutan a continuación solamente verán aquellos cambios confirmados antes del comienzo de la transacción: es como si se hiciese una fotografía de la base de datos: DECLARE Num ventas dia REAL; Num_ventas semana REAL; BEGIN COMMIT; SET TRANSACTION READ ONLY; SELECT count(*) INTO ventas_dia FROM ventas WHERE fecha = SYSDATE; SELECT count(*) INTO ventas_semana FROM ventas WHERE fecha > SYSDATE _ 7; COMMIT; END; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 207 - Mario Juncos Murcia Práctica 9 1. Escriba un bloque PLSQL para seleccionar el nombre del empleado con un valor concreto de sueldo. Le preguntaremos el sueldo al usuario a través de un pararnetro SQL.  Si el sueldo introducido devuelve más de una fila, gestione la excepción con un manejador de excepciones apropiado e inserte en la tabla MENSAJES "Más de un empleado con un salario <sueldo>”.  Si el sueldo introducido no devuelve ninguna fila, gestione la excepción con un manejador de excepciones apropiado e inserte en la tabla MENSAJES "Ningún empleado con salario <sueldo> “.  Si el sueldo introducido sólo devuelve una fila, inserte en la tabla MENSAJES el nombre del empleado y el importe del sueldo.  Gestione cualquier otra excepción con un manejador de excepciones apropiado e inserte en la tabla MENSAJES "Se produjo algún otro error”.  Pruebe el bloque con varios casos. RESULTADOS SMITH 800 Más de un empleado con un salario 3000 Ningún empleado con salario 6000 2. Modifique p4q3.sql para añadir un manejador de excepciones.  Escriba un manejador para que comunique un mensaje al usuario diciendo que el departamento especificado no existe.  Ejecute el bloque PLSQL introduciendo un departamento que no existe. Introduzca el número de departamento: 50 Introduzca la población del departamento: HOUSTON PL/SQL procedure successfully completed. G MESSAGE Departamento 50 es un Departamento no valido 3.Escriba un bloque PLSQL que imprima cuantos empleados hay que ganan 100€ más o menos del valor del salario introducido.  Si no hay ningún empleado dentro de ese rango de sueldo, muestre un mensaje al usuario indicando cuál es el caso. Utilice una excepción para este caso.  Si hay uno o más empleados dentro de ese rango, el mensaje debería indicar cuántos empleados hay en ese rango de sueldo. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 208 - Mario Juncos Murcia  Gestione cualquier otra excepción con un manejador de excepciones apropiado, el mensaje debería indicar que se ha producido otro error. Introduzca el salario: 800 PL/SQL procedure successfully completed G_MESSAGE Hay 1 empleado(s) con un salario entre 700 y 900 Introduzca el salario: 3000 PL/SQL procedure successfully completen G_MESSAGE Hay 3 empleado(s) con un salario entre 2900 y 3100 Introduzca el salario: 6000 PL/SQL procedure successfully completen G_MESSAGE No hay ningun empleado con un salario entre 5900 y 6100 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 209 - Mario Juncos Murcia SUBPROGRAMAS Los subprogramas PL/SQL son bloques con nombre. Pueden recibir parámetros y ser invocados desde aquellos puntos en los que haya visibilidad sobre ellos. Las características principales son:  Modularidad  Reusabilidad  Mantenimiento  Abstracción funcional Existen dos tipos de subprogramas:  Procedimientos  Funciones PROCEDIMIENTOS Un procedimiento, en general, recibe parámetros, y realiza una acción determinada, cualificada por los valores de sus parámetros. Se declaran en la región declarativa del bloque PL/SQL con la siguiente sintaxis: PROCEDURE nombre [(parámetro [,parámetro, ... l)] IS [declaración de objetos locales] BEGIN ... [EXCEPTION ...] END [Nombre]; donde 'parámetro' equivale a: IN := nombre_par OUT tipo valor IN OUT DEFAULT IN, OUT e IN OUT IN... Un parámetro IN es de solo lectura; puede ser consultado dentro del procedimiento, pero no asignarlo. Es el tipo por defecto OUT... Puede ser asignado, pero no consultado IN OUT ... Puede ser asignado y consultado Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 210 - Mario Juncos Murcia PROCEDURE nuevo_salario ( numero_emp empleados.num_emp%TYPE, nuevo_sal empleados.salario%TYPE, realizado OUT BOOLEAN ) IS tmp BOOLEAN; BEGIN UPDATE empleados SET salario = nuevo_sal WHERE num_emp = numero_emp; -- OK nuevo_sal := O; -- ilegal. Parámetro IN realizado := TRUE; -- correcto tmp := realizado; --ilegal. Parametro OUT END; Desde la región ejecutable podemos invocar a nuestros procedimientos: DECLARE OK BOOLEAN; PROCEDURE borra_emp....... PROCEDURE nuevo_salario......... BEGIN .... nuevo_salario(7902,6500,0K); borra_emp (7902); borra_emp (un_entero); END; FUNCIONES Una función es un subprograma que, además de realizar una acción determinada devuelve un único valor. La sintaxis es: FUNCTION nombre [( argumento [, argumento, ...])] RETURN tipo IS [declaración de objetos locales] BEGIN .... [EXCEPTION .... ....] END [Nombre]; Igual que los procedimientos, las funciones se declaran en la región declarativa del bloque. La declaración de argumentos es igual a la de los procedimientos. Sentencia RETURN Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 211 - Mario Juncos Murcia Dado que las funciones han de devolver un valor, es necesaria la existencia de una sentencia que detenga el flujo de ejecución de la función y provoque la devolución del valor mencionado. Ejemplo: FUNCTION prima_anual (sal empleados.salario%TYPE, com empleados.comision%TYPE) RETURN NUMBER IS prima NUMBER; BEGIN prima := (( sal * 14 )+( com * 14 )) * 0.05; RETURN prima; END prima_anual; Igual que los procedimientos, las funciones son invocadas desde la región ejecutable del bloque: DECLARE la_Prima NUMBER; FUNCTION prima_anual...... BEGIN ... la_prima := prima_anual (3500,600); ... END; DECLARACION DIFERIDA En los bloques PL/SQL no es posible referenciar a un subprograma que no esté previamente declarado. La declaración diferida resuelve dicho problema: Ejemplo: DECLARE PROCEDURE subida_comision( .... ); PROCEDURE aumento_sueldo IS BEGIN .... subida_comisión( .....); --todavía no esta declarado; END aumento_sueldo; PROCEDURE subida_comision IS BEGIN ..... END; NOTACION POSICIONAL Y NOMINAL Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 212 - Mario Juncos Murcia Por defecto los valores de una llamada a un subprograma se asocian con los argumentos por su posición. Ejemplo: PROCEDURE prima_anual (sal NUMBER, com NUMBER) IS... ........................................................ prima_anual (3500,600) conforme esta asigna 3500 a sal y 600 a com pero también está disponible la notación nominal: prima_anual (com => 600, sal => 3500) Ambas notaciones se pueden utilizar simultáneamente en la misma llamada: PROCEDURE fija_sal (num-emp NUMBER, sal NUMBER, com NUMBER) IS ... fija_sal (7902, com=>600, sal=>3500); Una vez que un parámetro ha sido utilizado con la notación nominal, los siguientes deben usar también esta notación: Ejemplo incorrecto: fija_sal (sal=>3500, 7902, com=>600); VALORES POR DEFECTO PARA LOS ARGUMENTIOS Los parámetros de tipo IN pueden tomar valores por defecto, para aquellos caso en los que el subprograma sea invocado, sin ofrecer valor para todos los parámetros. Ejemplo: FUNCTION prima_anual (sal NUMBER DEFAULT 0, com NUMBER DEFAULT 0) ... en la llamada a := prima_anual(3500,600); sal recibe el valor 3500 y com el valor 600, pero en la llamada: a:= prima_anual (3500) com toma el valor 0 Sin utilizar la notación nominal solo es posible dejar a una valor por defecto los parámetros finales de la declaración. Ejemplo: Si queremos poner com a 600 no podemos hacer a:= prima_anual(600); cargaría 600 en 'sal' y dejaría 'com' a 0. La solución sería usar la notación nominal a:= prima_anual (com=>600); SOBRECARGA Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 213 - Mario Juncos Murcia Como algunos lenguajes de los últimos años PL/SQL admite la sobrecarga de identificadores, en especial para aquellos que denotan a los subprogramas. Dos subprogramas pueden llamarse igual, si los tipos de sus argumentos permiten diferenciarlos. En otro caso la sobrecarga no es posible, ya que la ambigüedad no es resoluble. Ejemplo: DECLARE mi_emp empleados.nombre%TYPE; mi_dpt NUMBER(2); PROCEDURE borrar (nom empleados.nombre%TYPE) IS BEGIN DELETE FROM empleados WHERE nombre = nom; END borrar; PROCEDURE borrar (d NUMBER(2)) IS BEGIN DELETE FROM departamentos WHERE num_dept = d; END borrar; BEGIN -- del bloque principal mi_emp := 'MILLER'; mi_dpt := 10; borrar(mi_emp); borrar (mi_dept); COMMIT; END; -- del bloque principal RECURSIVIDAD Un programa recursivo es aquel que se llama a si mismo. Ejemplo: FUNCTION factorial (n POSITIVE) RETURN POSITIVE IS BEGIN IF n = 0 THEN RETURN 1; ELSE RETURN n * factorial (n-1l); END IF; END factorial; SUBPROGRAMAS ALMACENADOS Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 214 - Mario Juncos Murcia Los subprogramas (procedimientos y funciones) qua hemos visto hasta ahora se compilan independientemente y almacenan en la base de datos Oracle. Cuando creamos procedimientos o funciones almacenados desde SQL*Plus utilizan comandos CREATE PROCEDURE o CREATE FUNCTION, Oracle automáticamente compila el código fuente, genera el código objeto (llamado P_código) y los guarda en el diccionario de datos. De este modo quedan disponibles para su utilización. Los programas almacenados tienen dos estados: disponible (valid) y no disponible (invalid). Si alguno de los objetos referenciados por el programa ha sido borrado o alterado desde la última compilación del programa quedara en situación de "no disponible" y se compilara de nuevo automáticamente en la próxima llamada. Al compilar de nuevo, ORACLE determina si hay qua compilar algún otro subprograma referido por el actual, y se puede producir una cascada de compilaciones. Estos estados se pueden comprobar en la vista USER OBJECTS: SQL> SELECT OBJECT_NAME, OBJECT_TYPE, STATUS FROM USER_OBJECTS WHERE OBJECT_NAME='CAMBIAR_OFICIO'; OBJECT_NAME OBJECT_TYPE STATUS CAMBIAR OFICIO PROCEDURE VALID La estructura completa de la vista utilizada es SQL> DESCRIBE USER_OBJECTS: Name Null? Type OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(15) CREATED DATE LAST_ DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 215 - Mario Juncos Murcia También se puede encontrar el código fuente en la vista USER_SOURCE: SQL> SELECT LINE, SUBSTR(TEXT,1,60) FROM USER_SOURCE WHERE name ='CAMBIAR_OFICIO'; Line SUBSTR(TEXT,1,60) ____ ______________________________________________________________________ 1 PROCEDURE cambiar_oficio 2 (Num empleado NUMBER, 3 Nuevo oficio VARCHAR2) 4 IS 5 Anterior_oficio emple.oficio%TYPE; 6 BEGIN 7 SELECT oficio INTO anterior_oficio FROM emple 8 WHERE emp no =num_empleado; 9 10 UPDATE emple SET oficio = nuevo_oficio 11 WHERE emp_no = num_empleado; 12 DBMS_OUTPUT.PUT_LINE(num_empleado || 13 „*oficio Anterior: „ || anterior_oficio || 14 „*oficio Nuevo : „ || Nuevo_oficio ); 15 END cambiar_oficio; SQL> DESCRIBE USER_SOURCE Name Null? Type NAME NOT NULL VARCHAR2(30) TYPE VARCHAR2(12) LINE NOT NULL NUMBER TEXT VARCHAR2(4000) Para volver a compilar un subprograma almacenado en la base de datos se emplea la Orden ALTER, indicando PROCEDURE o FUNCTION, según el tipo de subprograma: ALTER {PROCEDURE/FUNCTION} nombresubprograma COMPILE; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 216 - Mario Juncos Murcia SUBPROGRAMAS LOCALES Dentro de un subprograma se puede declarar y crear otro subprograma: CREATE OR REPLACE pr_ejeml /* programa que contiene el subprograma local */ ...................... AS .............. /* lista de declaraciones: variables, etc. */ PROGRAM sprlocl /* comienza el subprograma local ........... /*lista de parametros del subprograma local IS ............................ /*declaraciones locales al subprograma local */ BEGIN ............................. /*instrucciones del subprograma local */ END; BEGIN ............ sprlocl; /* llamada al subprograma local */ .............. END; Estos subprogramas reciben el nombre de subprogramas locales y tienen las siguientes particularidades:  Se declaran al final de la sección declarativa de otro subprograma o bloque.  Se les aplica las mismas reglas de ámbito y visibilidad que a las variables declaradas en el mismo bloque.  Se utilizara este tipo de subprogramas cuando no se contemple su reutilización otros subprogramas (distintos a aquel en el que se declaran).  En el caso de subprogramas locales con referencias cruzadas o de subprogramas mutuamente recursivos, hay que realizar declaraciones anticipadas, tal como se explica a continuación. PL/SQL necesita que todos los identificadores, incluidos los subprogramas, estén declarados antes de usarlos. Por eso, la llamada que aparece en el siguiente subprograma es ilegal: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 217 - Mario Juncos Murcia DECLARE .......... PROCEDURE subprograma1 ( .......) IS BEGIN .......... Subprograma2( ... ); __ > ERR. identificador no declarado ....... END; PROCEDURE subprograma2 (......) IS BEGIN ...... END; Se podía haber invertido el orden de declaración de los procedimientos anteriores, lo cual hubiera resuelto este caso, pero no sirve para procedimientos mutuamente recursivos. El problema se resuelve utilizando declaraciones anticipadas de subprogramas Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 218 - Mario Juncos Murcia Práctica 10 1.-Crear y llamar al Procedimiento ADD_PROD y considerar los resultados. - Crear un procedimiento llamado ADD_PROD para insertar un departamento nuevo en la tabla DEPARTAMENTOS. - Compilar el código, llamar al procedimiento y consultar la tabla DEPARTAMENTOS para ver los resultados. - Llamar al procedimiento otra vez, pasando un identificador de DEPARTAMENTO de 10 ¿Qué ocurre y por qué?. 2.- Crear un procedimiento llamado UPD_DEP para modificar un departamento en la tabla DEPARTAMENTOS. - Crear un procedimiento llamado UPD_DEP para actualizar el nombre del departamento. Incluir el manejo de excepciones necesario. - Compilar el código, llamar al procedimiento y consultar la tabla DEPARTAMENTOS para ver los resultados. También comprobar el manejo de excepciones intentando actualizar un departamento que no existe. 3.- Crear un procedimiento llamado DEL_DEP para borrar un departamento en la tabla DEPARTAMENTOS - Crear un procedimiento llamado DEL_DEP para borrar un departamento. Incluir el manejo de excepciones necesario. - Compilar el código, llamar al procedimiento y consultar la tabla DEPARTAMENTOS para ver los resultados. También comprobar el manejo de excepciones intentando borrar un departamento que no existe. 4. Crear un procedimiento para consultar la tabla EMP, recuperando el salario y cargo del empleado 7839. - Crear un procedimiento que devuelve el valor de las columnas SAL y JOB de un empleado especificado (utilizar EMPNO). - Utilizar variables “host” para los dos parámetros OUT. - Proporcionar el manejo de error apropiado si el usuario llama al procedimiento sin que exista valor para EMPNO. - Compilar el código, llamar al procedimiento y visualizar el salario y el cargo del empleado 7839. - Llamar al procedimiento otra vez, pasando un EMPNO de 9898. ¿Qué ocurre y por qué?. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 219 - Mario Juncos Murcia Práctica 11 1.- Crear y llamar a la función Q_DEP para devolver un nombre de departamento. - Crear una función llamada Q_DEP para devolver un nombre de departamento a una variable “host”. - Compilar el código, llamar a la función y después consultar la variable “host” para ver el resultado. 2. Crear una función almacenada ANNUAL_COMP para devolver el salario anual cuando se pasa el salario mensual y la comisión de un empleado. Asegurarse de que la función controla valores NULL.  Crear y llamar a la función ANNUAL_COMP, pasando los valores del salario mensual y de la comisión. La función debería devolver el salario anual, definido por: (sal * 12) + comision  Utiliza la función almacenada en una sentencia SELECT contra la tabla EMP. 3.- Crear un procedimiento, NEW_EMP para insertar un empleado nuevo dentro de la tabla EMP. El procedimiento debería contener una llamada a la función VALID_DEPTNO para comprobar si existe en la tabla el departamento especificado para el nuevo empleado.  Crear una función VALID_DEPTNO para validar un número de departamento especificado. La función debería devolver un BOOLEAN.  Crear el procedimiento NEW EMP para añadir un empleado a la tabla EMP. Debería añadirse un nuevo registro a EMP si la función devuelve TRUE. Si la función devuelve FALSE, el procedinúento debería alertar al usuario con un mensaje apropiado  Definir valores DEFAULT para la mayoría de argumentos. La comisión por defecto es 0, el salario por defecto es 1000, el número de departamento por defecto es 30, el puesto por defecto es SALESMAN y el número de gestor por defecto es 7839.  Comprobar el procedimiento NEW_EMP añadiendo un nuevo nombre de empleado al departamento 99 (HARRIS). Dejar el resto de los parámetros con sus valores por defecto. ¿Cual es el resultado?.  Comprobar el procedimiento NEW_EMP añadiendo un nuevo nombre de empleado al departamento 30 (HARRIS). Dejar el resto de los parámetros con sus valores por defecto. ¿Cual es el resultado?. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 220 - Mario Juncos Murcia VISIÓN GENERAL SOBRE LOS PAQUETES Los paquetes agrupan conceptos PLSQL relacionados, tipos, items, subprogramas. Por ejemplo un paquete de recursos humanos puede contener procedimientos de contratación y despido, funciones de comisiones, variables de exención de impuestos, etc. Normalmente un paquete tiene una especificación y un cuerpo, los cuales estan almacenados por separado en la base de datos.  La especificación es el interface de nuestras aplicaciones. Esta declara los conceptos, variables, excepciones, constantes, cursores y subprogramas disponibles para el usuario.  El cuerpo define completamente cursores y subprogrames, implementando así la especificación. El paquete en si mismo no puede ser llamado. El formato de un paquete es similar al de un subprograma; una vez escrito y compilado, el contenido puede ser compartido por varias aplicaciones. Cuando llamamos por primera vez al programa PLSQL empaquetado, se carga el paquete completo en memoria, por lo que llamadas posteriores a los programas relacionados no necesitan hacer I/O en el disco. El uso de paquetes es una alternativa a la creación de procedimientos y funciones ofreciendo las siguientes ventajas:  Modularidad.- Encapsulamos en un paquete las estructuras de programa relacionadas  Información oculta.- Podemos decidir qué programas son públicos o privados. El paquete oculta la definición de los programas privados de forma que sólo se vea afectado el paquete ( no la aplicación). También, protegemos la integridad del paquete ocultando detalles de implementación.  Funcionalidad añadida.- Durante la sesión persisten las variables y cursores públicos empaquetados, de modo que pueden ser compartidos por todos los subprogramas que se ejecutan en el entorno, además nos permite mantener los datos en las transacciones sin tener que almacenarlos en la base de datos.  Mejor rendimiento.- Al llamar por primera vez a un subprograma empaquetado, se carga en memoria el paquete completo.  Sobrecarga.- Los paquetes permiten sobrecargar procedimientos y funciones. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 221 - Mario Juncos Murcia DESARROLLO DE UN PAQUETE Creamos un paquete en dos partes: primero la especificación y después el cuerpo. Los programas públicos de un paquete son aquellos que se declaran en la especificación y que se definen en el cuerpo. Los programas privados de un paquete son aquellos que se definen sólo en el cuerpo y solamente pueden ser referenciados por otros programas que están dentro del mismo paquete CREACIÓN DE LA ESPECIFICACIÓN DE UN PAQUETE CREATE [ OR REPLACE ] PACKAGE nombre_paquete IS Declaración de variables, constantes, cursores, excepciones o tipos públicos Declaración de los subprogramas públicos END nombre_paquete; CREATE OR REPLACE PACKAGE paquete_comisiones IS G_comision NUMBER:= 10; PROCEDURE reset_comision (V_comision IN NUMBER); END paquete_comisiones; Declaramos una variable global para mantener el valor de la comisión para la sesión de usuario, que puede ser cambiada por el usuario directamente. O podemos declarar un procedimiento público para permitir al usuario resetear la comisión en cualquier momento durante la sesión CREACIÓN DEL CUERPO DE UN PAQUETE CREATE [OR REPLACE] PACKAGE BODY nombre_paquete IS Declaración de variables, constantes, cursores, excepciones y tipos privados Definición de los subprogramas PLSQL Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 222 - Mario Juncos Murcia CREATE OR REPLACE PACKAGE BODY paquete_comisiones IS FUNCTION validar_comision (v_comision IN NUMBER) RETURN BOOLEAN IS V_max_comision NUMBER; BEGIN SELECT MAX(comision) INTO v_max_comision FROM emp; IF v_comision > v_max_comision THEN RETURN (FALSE); ELSE RETURN (TRUE); END IF; END validar_comision; PROCEDURE reset_comision ( v_comision IN NUMBER) IS V_valido BOOLEAN; BEGIN V_valido := validar_comision(v_comision); IF v_valido = TRUE THEN G_comision := v_comision; ELSE RAISE APLICATION_ERROR (-20210, „Comision invalida‟) END IF; END reset_comision; END paquete_comisiones; Para llamar a un procedimiento empaquetado debemos de hacer referencia al nombre del paquete SQL> EXECUTE paquete_comisiones.reset_comision(1500) Si fuese de un usuario distinto SQL> EXECUTE usuario.paquete_comisiones.reset_comision(1500) Declaración de cursores en paquetes Para declarar cursores en paquetes de forma que estén accesibles en la especificación deberemos separar la declaración del cursor del cuerpo (en este es donde va la cláusula SELECT). La declaración del cursor se incluir en la cabecera del paquete indicando el nombre del cursor, los parámetros (si procede) y el tipo devuelto. Este ultimo se indicara mediante RETURN tipodedato; para cursores que devuelven filas enteras normalmente se usara %ROWTYPE. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 223 - Mario Juncos Murcia CREATE PACKAGE empleados_acc AS ……. CURSOR C1 RETURN emple%ROWTYPE; ….. END empleados_acc; CREATE PACKAGE BODY empleados_acc AS CURSOR C1 RETURN emple%ROWTYPE SELECT * FROM emple WHERE salario > 100000; ....... END empleados_ acc; Ámbito y otras características de las declaraciones Los objetos declarados en la especificación del paquete son globales al paquete y locales al contexto. Asimismo, todas las variables declaradas en la especificación del paquete mantienen su valor durante la sesión, por tanto, el valor no se pierde entre las llamadas de los subprogramas. También cabe mencionar respecto a las variables constantes y cursores declarados en un paquete lo siguiente:  El propietario es la sesión.  En la sesión no se crean los objetos hasta que se referencia el paquete.  Cuando se crean los objetos su valor será nulo (salvo que se inicialice).  Durante la sesión los valores pueden cambiarse.  Al salir de la sesión los valores se pierden. Características de almacenamiento y compilación Tanto el código fuente como el código compilado de los paquetes se almacena en la base de datos. Al igual que ocurría con los subprogramas almacenados, el paquete (la especificación) puede tener dos estados: - Disponible (valid). - No disponible (invalid). Cuando se borra o modifica alguno de los objetos referenciados el paquete pasará a invalid. Si la especificación del paquete se encuentra "no disponible", Oracle invalida (pasa a invalid) cualquier objeto que haga referencia al paquete. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 224 - Mario Juncos Murcia Cuando recompilamos la especificación, todos los objetos que hacen referencia al paquete pasan a "no disponible" hasta que sean compilados de nuevo. Cualquier referencia o llamada a uno de estos objetos antes de ser recompilados producirá que Oracle automáticamente los recompile. Esto ocurre con el cuerpo del paquete, pero en este caso se puede recompilar el paquete sin invalidar la especificación. Esto evita las recompilaciones en cascada innecesarias. Si se cambia la definición (cuerpo) de una función o procedimiento incluido en un paquete, no hay que recompilar todos los programas que llaman al subprograma (como ocurre con los subprogramas almacenados), a no ser que también se cambie la especificación de dicha función o procedimiento. Paquetes suministrados por Oracle Oracle incluye con su gestor de bases de datos diversos paquetes como STANDARD, DBMS_OUTPUT, DBMS_STANDARD, DBMS_SQL, y muchos otros que incorporan diversas funcionalidades. A continuación veremos una breve reseña del contenido de estos paquetes: - En STANDARD se declaran tipos, excepciones, funciones, etc., disponibles desde el entorno PL/SQL. Por ejemplo, en el paquete STANDARD están definidas las funciones: FUNCTION ABS (n number) RETURN NUMBER; FUNCTION TO_CHAR (right DATE) RETURN VARCHAR2; FUNCTION TO_CHAR (left NUMBER) RETURN VARCHAR2; FUNCTION TO_CHAR (left DATE, right VARCHAR2) RETURN VARCHAR2; FUNCTION TO_CHAR (left NUMBER, right VARCHAR2) RETURN VARCHAR2; Si, por alguna circunstancia, volvemos a declarar alguna de esas funciones desde un programa PL/SQL, siempre podremos hacer referencia a la original mediante la notación de punto. Por ejemplo, . . . STANDARD.ABS ( ... ) ... - DBMS_STANDARD incluye utilidades, come, el procedimiento RAISE_APLICATION ERROR, que facilitan la interacci6n de nuestras aplicaciones con Oracle. - En DBMS_OUTPUT se encuentra el procedimiento PUT_LINE que hemos venido uti- lizando para visualizar datos, y otros como ENABLE y DISABLE, que permiten con- figurar y purgar el buffer utilizado por PUT_LINE. - DBMS_SQL incorpora procedimientos y funciones que permiten utilizar SQL dinámico en nuestros programas, tal come, veremos en el apartado siguiente. SQL DINÁMICO Cuando se compila un procedimiento PL/SQL, comprueba en el diccionario de datos y completa todas las referencias a objetos de la base de datos (tablas, columnas, usuarios, etc.). De esta forma, si algún objeto no existe en el momento de la compilación, el proceso fallará, indicando el error correspondiente. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 225 - Mario Juncos Murcia Esta manera de trabajar tiene importantes ventajas, especialmente en cuanto a velocidad en la ejecución y a la seguridad de que las referencias a los objetos han sido comprobadas previamente; pero también tiene algunos inconvenientes:  Todos los objetos tienen que existir en el momento de la compilación.  No se pueden crear objetos nuevos con el programa.  No se puede cambiar la definición de los objetos.  Cualquier referencia a un objeto debe ser conocida y resuelta en tiempo de compilación. Esta forma de trabajar con SQL se denomina SQL estático, por razones obvias. El paquete DBMS_SQL permite superar estas limitaciones y ejecutar instrucciones de definición de datos, así como resolver referencias a objetos en el momento de la ejecución. A esta forma de trabajar se le denomina SQL dinámico. El siguiente programa es capaz de ejecutar órdenes de definición y manipulación sobre objetos que solo se conocerán al ejecutar el programa, ya que el comando se pasará en la llamada al procedimiento: CREATE OR REPLACE PROCEDURE ejsqldin (instrucción VARCHAR2) AS Id_cursor INTEGER; V_dummy INTEGER; BEGIN Id_cursor := DBMS_SQL.OPEN_CURSOR; /*Abrir*/ DBMS_SQL.PARSE(id_cursor, instrucción, DBMS_SQL.V7); /*Analiz*/ V_dummy := DBMS_SQL.EXECUTE(id_cursor); /*Ejecut*/ DBMS_SQL.CLOSE CURSOR(id_cursor); l*Cerrar*/ EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE CURSOR(id_cursor); /*Cerrar/ RAISE; END ejsqldin; En este ejemplo podemos observar resaltadas las líneas correspondientes a las cuatro operaciones más frecuentes que se suelen realizar al programar con SQL dinámico, así como los subprogramas que se encargan de realizar dichas operaciones, incluidos en DBMS_SQL: - DBMS_SQL.OPEN_CURSOR abre el cursor y devuelve un numero de identificación para poder utilizarlo. Recordemos que todos los comandos SQL se ejecutan dentro de un cursor. - DBMS_SQL.PARSE analiza la instrucción que se va a ejecutar. Este paso es necesario, ya que cuando se compiló el programa, la instrucción no pudo ser analizada, pues aun no se habrá construido. - DBMS_SQL.EXECUTE se encarga de la ejecución. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 226 - Mario Juncos Murcia - DBMS_SQL.CLOSE CURSOR cierra el cursor utilizado. Es muy importante tener en cuenta que, cuando se definen objetos dinámicamente, el propietario del procedimiento deberá tener concedidos los privilegios necesarios (CREATE USER, CREATE TABLE, etc.) de forma directa, no mediante un rol. SQL> EXECUTE EJSQLDIN('CREATE USER DUMM1 IDENTIFIED BY DUMM1') begin EJSQLDIN('CREATE USER DUMM1 IDENTIFIED BY DUMM1'); end; * ERROR en línea 1: ORA_01031: privilegios insuficientes ORA_06512: en "SYSTEM.EJSQLDIN", línea 14 ORA_06512: en línea 1 Observemos que se trata del usuario SYSTEM, pero tiene el privilegio CREATE USER mediante un rol. Para solucionar el problema se concede el privilegio de forma directa: SQL> GRANT CREATE USER TO SYSTEM; Concesión terminada con éxito. SQL> EXECUTE EJSQLDIN('CREATE USER DUMM1 IDENTIFIED BY DUMM1') Procedimiento PL/SQL terminado con éxito. A continuación podemos observar otros ejemplos de ejecución: SQL> EXECUTE EJSQLDIN('CREATE TABLE PR1 (C1 CHAR)'); Procedimiento PL/SQL terminado con éxito. SQL> EXECUTE EJSQLDIN('ALTER TABLE PR1 ADD COMENTARIO VARCHAR2(20)') Procedimiento PL/SQL terminado con éxito. SQL> DESCRIBE PR1 Name Null? Type ______________________ ________ _______________ C1 CHAR (1) COMENTARIO VARCHAR2(20) Pasos para utilizar SQL dinámico Aunque hay diferencias dependiendo del tipo de instrucción que se va a procesar, los pasos que hay que seguir son los siguientes:  Abrir el cursor (OPEN_CURSOR) y guardar su numero de identificación para posteriores referencias.  Analizar (PARSE) el comando que se va a procesar.  Si se van a pasar valores al comando, acoplar las variables de entrada (BIND). Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 227 - Mario Juncos Murcia o Si el comando es una consulta: o Definir columnas (DEFINE_COLUMN). o Ejecutar (EXECUTE).  Recuperar los valores con FETCH_ROWS y COLUMN_VALUE  Si se trata de un comando de manipulación, ejecutar (EXECUTE).  Cerrar el cursor (CLOSE CURSOR). Comandos de definición de datos Como ya hemos visto, utilizando los procedimientos y funciones incluidos en el paquetes DBMS_SQL, podemos crear objetos en tiempo de ejecución. Por ejemplo, el siguiente procedimiento creará una tabla de una sola columna cuyos datos se pasarán en la llamada al procedimiento CREATE OR REPLACE PROCEDURE creartabla (nombretabla VARCHAR2, nombrecol VARCHAR2, longitudcol POSITIVE) AS Id_cursor INTEGER; V_ comando VARCHAR2(2000); BEGIN Id_cursor := DBMS_SQL.OPEN CURSOR; /*Abre el cursor*/ V_comando := „CREATE TABLE „ || nombretabla || „( „ || nombrecol || „ VARCHAR2(„ || longitudcol || „))‟; DBMS_SQL.PARSE(id_ cursor, v_comando, DBMS_SQL.V7);/*Analiza y ejecuta*/ DBMS_SQL.CLOSE_CURSOR(id_cursor); /*Cierra el cursor*/ EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR(id_cursor); RAISE; END creartabla;  OPEN_CURSOR. Esta función abre un cursor nuevo y retorna un identificador del mismo, que se guarda en una variable y servirá para hacer referencia al cursor desde el programa.  PARSE. Analiza el comando comprobando posibles errores. Si se trata de un comando DDL (como en este caso), PARSE, además, lo ejecuta. Recibe tres parámetros:  El identificativo del cursor.  La cadena que contiene el comando (sin el ;).  El indicador del lenguaje, que determina la manera en que Oracle manejará el comando SQL. Están disponibles las siguientes opciones: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 228 - Mario Juncos Murcia  V6: estilo Oracle versión 6.  V7: estilo Oracle versión 7 (es el mismo para la versión 8).  NATIVE: estilo normal de la base en la que este conectado el programa. - CLOSE CURSOR. Cierra el cursor especificado y libera la memoria utilizada por el cursor. Los objetos se crearán por defecto en el esquema del usuario propietario del procedimiento. Si se pretende crearlos en otro esquema, se deberá indicar explícitamente. Comandos de manipulación de datos El siguiente ejemplo muestra la utilización de PL/SQL dinámico para introducir datos en una tabla que se indicará en la llamada (las llamadas a PUT_LINE se utilizan para ilustrar el fun- cionamiento del programa): CREATE OR REPLACE PROCEDURE introducir_fila (nombretabla VARCHAR2, valor VARCHAR2) AS Id _ cursor INTEGER; v_comando VARCHAR2(2000); v_filas INTEGER; BEGIN Id_cursor := DBMS_SQL.OPEN_CURSOR; V_comando := INSERT INTO „ || nombretabla || „ VALUES (:val_1)'; DBMS_OUTPUT.PUT_LINE(v_comando); DBMS_SQL.PARSE (id_cursor, v_comando, DBMS_SQL.v7); DBMS_SQL.BIND_VARIABLE(id_cursor, „:val 1‟, valor); /*Acopla la variable que pasara el valor de entrada*/ v_filas := DBMS _ SQL.EXECUTE(id_cursor); /*Ejecuta el comando*/ DBMS_SQL.CLOSE_CURSOR(id_cursor); DBMS_OUTPUT.PUT_LINE( „Filas introducidas: „ || v_filas); EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR(id_cursor); RAISE; END introducir_fila; En este ejemplo podemos apreciar que, para introducir dinámicamente comandos de manipulación de datos, utilizamos los procedimientos y funciones estudiados en el apartado anterior: OPEN_CURSOR, PARSE y CLOSE_CURSOR. Pero, además, utilizaremos el procedimiento EXECUTE (necesario para comandos de manipulación y consulta). También se utiliza en este caso el procedimiento BIND_VARIABLE para acoplar la variable con el valor de entrada. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 229 - Mario Juncos Murcia A continuación podemos observar un ejemplo de ejecución del procedimiento cuyo resultado es insertar una fila en la tabla mitabla que suponemos creada previamente: SQL> execute introducir_fila( „mitabla‟, „HOLA MUNDO‟); INSERT INTO mitabla VALUES (:val_1) Filas introducidas: 1 Procedimiento PL/SQL terminado con éxito. Podemos comprobar el resultado de la ejecución introduciendo la siguiente consulta: SQL> select * from mitabla; COL1 HOLA MUNDO CONSULTAS El siguiente ejemplo permite obtener el número de empleado, el apellido y el oficio de los empleados que cumplan la condición que se especificará en la llamada: CREATE OR REPLACE PROCEDURE consultar emple (condición VARCHAR2, valor VARCHAR2) AS Id_cursor INTEGER; v_comando VARCHAR2(2000); v_dummy NUMBER; v_emp_no emple.emp_no%TYPE; v_apellido emple.apellido%TYPE; v_oficio emple.oficio%TYPE; BEGIN id cursor := DBMS _ SQL.OPEN_CURSOR; v_comando := SELECT emp_no, apellido, oficio FROM emple WHERE „|| condicion || „:val_1‟; DBMS_OUTPUT.PUT_LINE(v_comando); DBMS_SQL.PARSE(id_cursor, v_comando, DBMS_SQL.V7); DBMS_SQL.BIND_VARIABLE(id_cursor, „:val_1‟, valor); /* A continuación se especifican las variables que recibirán los valores de la seleccion*/ DBMS_SQL.DEFINE _ COLUMN(id_cursor, 1, vemp_no); DBMS_SQL.DEFINE _ COLUMN(id_cursor, 2, v_apellido,14); DBMS_SQL.DEFINE_COLUMN(id_cursor, 3, v_oficio, 14); v_dummy := DBM_SQL.EXECUTE(id_cursor); /* La función FETCH ROWS recupera filas y retoma el numero de filas que quedan */ Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 230 - Mario Juncos Murcia WHILE DBMS_SQL.FETCH_ROWS(idcursor)>0 LOOP /* A continuación se depositaran los valores recuperados en las variables PL/SQL */ DBMS_SQL.COLUMN_VALUE(id_cursor, 1, v_emp_no); DBMS_SQL.COLUMN_VALUE(id_cursor, 2, v_apellido); DBMS_SQL.COLUMN_VALUE(id_cursor, 3, v_oficio); DBMS_OUTPUT.PUT _ LINE (v_emp_no || „*‟ || vapellido || „*‟ || v_oficio); END LOOP; DBMS_SQL.CLOSE_CURSOR(id_cursor); EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR(id_cursor); RAISE; END consultar_emple; La principal diferencia de este programa con los anteriores es que, en este caso, como ocurre en todas las consultas, hay que recoger los valores obtenidos por la consulta. Para ello se utilizan los siguientes procedimientos y funciones:  DEFINE _COLUMN: sirve para especificar las variables que recibirán los valores de la selección. Se utilizará una llamada para cada variable indicando: o El identificador del cursor. o El numero de la columna de la cláusula SELECT de la que recibirá el valor. o El nombre de la variable. o Si la variable es de tipo CHAR, VARCHAR2 o RAW, hay que especificar la longitud.  FETCH_ROWS: es similar al comando FETCH de PL/SQL estático, pero con dos par- ticularidades muy importantes: o Devuelve un valor numérico que indica el numero de files que quedan por recuperar en el cursor. Puede servir, como en el ejemplo, para controlar el numero de iteraciones del bucle. o Para introducir los datos recuperados en las variables PL/SQL todavía hay que uti- lizar el siguiente procedimiento.  COLUMN_VALUE: deposita el valor de un elemento del cursor (recuperado con FETCH ROWS) cuya posición se especifica en una variable PL/SQL. Normalmente se trata de la variable indicada en COLUMN_VALUE para el mismo elemento del cursor. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 231 - Mario Juncos Murcia SQL> EXECUTE CONSULTAR EMPLE(„SALARIO > „,38500) SELECT emp_no, apellido, oficio FROM emple WHERE SALARIO > :val l 7566*JIMÉNEZ*DIRECTOR 7788*GIL*ANALISTA 7839*REY*PRESIDENTE 7902*FERNANDEZ*ANALISTA Procedimiento PL/SQL terminado con éxito. SQL> EXECUTE CONSULTAR_EMPLE('APELLIDO LIKE ', '%EZ') SELECT emp_no, apellido, oficio FROM emple WHERE APELLIDO LIKE :val_1 7369*SANCHEZ*EMPLEADO 7566*JIMENEZ*DIRECTOR 7902*FERNANDEZ*ANALISTA Procedimiento PL/SQL terminado con éxito. ELIMINACIÓN DE UN PAQUETE DROP PACKAGE nombre_paquete DROP PACKAGE BODY nombre_paquete Al igual que en los subprogramas se permite la sobrecarga y la declaración avanzada Creación de un Procedimiento "One_Time_Only" Un procedimiento "one_time_only" se ejecuta una sola vez, la primera vez que se llama al paquete dentro de la sesión de usuario. En el ejemplo de arriba, el valor de la comisión que prevalece está definido al principio de la sesión para hacer la media de comisión entre los empleados. Es aconsejable inicializar las variables públicas o privadas con un procedimiento automático one_time_only cuando la derivación es demasiado compleja para definirse dentro de la declaración. En este caso, no inicializar la variable en la declaración, ya que el valor será redefinido por el procedimiento one_time_only. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 232 - Mario Juncos Murcia CREATE OR REPLACE PACKAGE BODY comision_Package IS FUNCTION validar_comision ...................... END validar_comision; PROCEDURE reset_comision ....................................... END reset_comision; BEGIN SELECT AVG(comision) INTO g_comision FROM emp; END comision_package; Restricciones sobre las Funciones de un Paquete utilizadas en SQL  No se permiten INSERT, UPDATE, o DELETE.  Sólo funciones locales pueden modificar variables de un paquete.  Las funciones remotas no pueden leer o escribir variables remotas de un paquete.  Las funciones que leen o escriben variables de un paquete no pueden usar la opción de consultas en paralelo (parallel query) Las llamadas a subprogramas que rompan las restricciones anteriores no se permiten. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 233 - Mario Juncos Murcia VISIÓN GENERAL DE LOS TRIGGERS Un trigger es un bloque PLSQL que se ejecuta implícitamente cuando ocurre un evento. Un trigger puede ser "de la base de datos" o "de la aplicación". Los triggers de la base de datos se ejecutan de forma implícita cuando se lanza una sentencia INSERT, UPDATE, o DELETE (sentencias de triggers) contra la tabla asociada, sin importar qué usuario está conectado o qué aplicación se está utilizando. Los triggers de la aplicación se ejecutan de forma implícita cuando ocurre un evento en particular dentro de la aplicación. Un ejemplo de una aplicación que usa triggers, es una aplicación desarrollada con Oracle Developer Form Builder. Los triggers de la base de datos pueden definirse solamente sobre tablas, no sobre vistas. Sin embargo, si se lanza una operación DML contra una vista, se ejecutan los triggers sobre las tablas de la vista. Guia para el Diseño de Triggers  Realizar acciones relacionadas a la operación específica.  Utilizar triggers de la base de datos para las operaciones globales centralizadas que serían ejecutadas por la sentencia de trigger, independientemente de qué usuario o aplicación emita la sentencia No “reinventar la rueda”. No definir triggers para duplicar o reemplazar la funcionalidad ya construida dentro de la base de datos Oracle. Por ejemplo no definir triggers para implementar las reglas de integridad que puede hacerse utilizando restricciones declarativas.  Prestar atención a los excesos, pueden causar dependencias complejas El uso excesivo de triggers puede dar lugar a interdependencias complejas, lo que podría dificultar el mantenimiento en aplicaciones largas. Utilizar triggers solo cuando sea necesario, y tener cuidado con los efectos recursivos y en cascada. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 234 - Mario Juncos Murcia Creación de Triggers CREATE [OR REPLACE] TRIGGER nombre_trigger momento evento1 [OR evento2 OR evento3] ON table name PL/SQL block; Antes de codificar el cuerpo del trigger hay que decidir sobre los componentes del trigger: tiempo, evento y tipo. Momento: ¿Cuándo debería dispararse? BEFORE: El código del cuerpo del trigger se ejecutará antes del evento DML.Este tipo de trigger se utiliza frecuentemente en las siguientes situaciones:  Cuando la acción del trigger debería determinar si se tendría que permitir que se complete la sentencia de trigger. Esto nos permite eliminar el proceso innecesario de sentencias y su posible rollback en casos donde se produce una excepción en la acción del trigger.  Para derivar valores de columna antes de completar una sentencia INSERT o UPDATE. AFTER: El código del cuerpo del trigger se ejecutará despues del evento DML.Este tipo de trigger se utiliza frecuentemente en las siguientes situaciones:  Cuando queremos que la sentencia se complete antes de ejecutar la acción del trigger.  Si ya hay un "BEFORE trigger" y un "AFTER trigger" podemos realizar diferentes acciones sobre la misma sentencia. Evento: ¿Qué operación DML provocará la ejecución trigger? El evento puede ser una sentencia INSERT, UPDATE, o DELETE sobre una tabla. Cuando el evento es UPDATE, podemos incluir una lista de columnas para identificar que columna(s) deben cambiarse para ejecutar el trigger. No podemos especificar una lista de columnas para una sentencia INSERT o DELETE, ya que siempre afectan a los registros enteros. El evento puede contener múltiples sentencias DML. De esta forma, podemos diferenciar qué código ejecutar dependiendo de la sentencia que hace que se ejecute el trigger. Tipo de Trigger: Podemos especificar el número de veces que se va a ejecutar la acción de un trigger: una vez para cada registro afectado por la sentencia del trigger (como un UPDATE de múltiples registros), o una vez para la sentencia del trigger, sin importar a cuantos registros afecte. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 235 - Mario Juncos Murcia Sentencia Un trigger a nivel de sentencia se ejecuta una vez cuando se produce el evento, incluso si no se ve afectado ningún registro. Los triggers a nivel de sentencia son útiles si la acción del trigger no depende de datos o de los registros que son afectados por los datos proporcionados por el evento mismo. Por ejemplo, un Trigger que realiza una comprobación completa de seguridad sobre el usuario actual. Fila Un trigger a nivel de fila se ejecuta cada vez que la tabla es afectada por el evento. Si el evento no afecta a ningún registro, el trigger no se ejecuta. Los triggers a nivel de fila son útiles si la acción del disparador depende de los datos de los registros que están afectados o de los datos proporcionados por el evento mismo. Cuando la manipulación de los datos de la sentencia afecta a un registro único, el trigger a nivel de sentencia y el trigger a nivel de registro se ejecutan exactamente una vez. Cuerpo del Trigger: La acción del trigger define qué se necesita hacer cuando se emite el evento. Puede contener sentencias SQL Y PL/SQL, definir construcciones PL/SQL como pueden ser variables, cursores, excepciones, etc. Adicionalmente, los registros tienen acceso a los valores de columna nuevos y viejos del registro que se está procesando, utilizando nombres de correlación. Trigger "Before Statement". Podemos crear un trigger BEFORE a nivel de sentencia con el fin de prevenir que suceda la operación del trigger si se viola cierta condición. Por ejemplo vamos a crear un disparador para restringir las inserciones en la tabla EMP a ciertas horas, de Lunes a Viernes. Si un usuario intenta insertar un registro en la tabla EMP en Sábado, por ejemplo, el usuario verá el mensaje, fallará el trigger y se hará rollback sobre las sentencias del Trigger. RAISE_APPLICATION_ERROR es un procedimiento que imprime un mensaje para el usuario y provoca que falle el bloque PL/SQL. Cuando falla un trigger de la base de datos, Oracle Server hace rollback sobre las sentencias del trigger. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 236 - Mario Juncos Murcia SQL> CREATE OR REPLACE TRIGGER secure_emp 2 BEFORE INSERT ON emp 3 BEGIN 4 IF (TO_CHAR (sysdate,‟DY‟) IN („SAT','SUN')) 5 OR (TO_CHAR(sysdate,‟HH24‟) NOT BETWEEN '08' AND '18' 6 THEN RAISE_APPLICATION_ERROR (_20500, „ 'You may only 7 insert into EMP during normal hours‟); 7 END IF; 8 END; Uso de Predicados Condicionales Podemos combinar varios eventos de trigger en uno sólo aprovechando los predicados condicionales INSERTING, UPDATING y DELETING dentro del cuerpo del trigger. Por ejemplo vamos a crear un trigger para restringir todos los eventos de manipulación de datos en la tabla EMP a ciertas horas, de Lunes a Viernes. CREATE OR REPLACE TRIGGER secure_emp BEFORE INSERT OR UPDATE OR DELETE ON emp BEGIN IF(TO_CHAR (sysdate,‟DY‟) IN ('SAT‟,‟SUN‟)) OR (TO_CHAR(sysdate, „HH24‟) NOT BETWEEN '08' AND '18') THEN IF DELETING THEN RAISE_APPLICATION_ERROR (_20502, 'You may only delete from EMP during normal hours.'); ELSIF INSERTING THEN RAISE_APPLICATION_ERROR (_20500, You may only insert into EMP during normal hours.‟); ELSIF UPDATING ('SAL') THEN RAISE_APPLICATION_ERROR (_20503, 'You may only update SALARIOduring normal hours.‟); ELSE RAISE_APPLICATION_ERROR (_20504, 'You may only update EMP during normal hours.‟); END IF; END IF; END; Utilizar también triggers BEFORE a nivel de sentencia para inicializar las variables globales o flags, y para validar reglas de negocio complejas. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 237 - Mario Juncos Murcia Trigger After Sentencia: Ejemplo Crear un trigger AFTER a nivel de sentencia con el fin de auditar la operación de trigger o realizar un cálculo después que se ha completado la operación. Supongamos que tenemos una tabla de auditoria definida por el usuario (usuario, tabla, columna, inserciones, actualizaciones, borrados, max_inser, max_act, max_borrados) que relaciona los usuarios y cuenta sus operaciones de manipulación de datos. Después de que cualquier usuario haya actualizado la columna SALARIO en la tabla EMP, utilizaremos la tabla de auditoria para asegurarnos que el número de cambios del salario no excede del máximo permitido para ese usuario. CREATE OR REPLACE TRIGGER check_salario_count AFTER UPDATE OF salarioON emp DECLARE V_salario_changes NUMBER; V_max_changes NUMBER; BEGIN SELECT actualizaciones, max_act INTO v_salario_changes, v_max_changes FROM audit_table WHERE user_name = user AND table_name = „EMP‟ AND column_name = 'SAL'; IF v_salario_changes > v max_changes THEN RAISE_APPLICATION_ERROR (_20501, „You may only make a maximum of „ || TO_CHAR (v_max_changes) || „changes to the SALARIOcolunm' ); END IF; END; Creación de un Trigger a Nivel de Registro CREATE [OR REPLACE] TRIGGER trigger_name momento eventol [OR evento2 OR evento3] ON table_name [REFERENCING OLD AS old / NEW AS new ] FOR EACH ROW [WHEN condition] PL/SQL block; Referencing Especifica los nombres de correlación de los valores nuevo y antigüo del registro actual. (Por defecto son OLD y NEW) FOR EACH ROW Designa el trigger a nivel de registro. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 238 - Mario Juncos Murcia WHEN Especifica la restricción del trigger. (Este predicado condicional es evaluado por cada registro para determinar si se ejecuta o no el cuerpo del trigger) PL/SQL block Es el cuerpo del trigger que define la acción realizada por el trigger, comenzando con DECLARE or BEGIN, y finalizando con END Trigger After a nivel fila: ejemplo Crear un trigger de registro para mantenter un contador de las operaciones de manipulación de datos en las tablas de la base de datos por diferentes usuarios. Si la rutina de un trigger no tiene lugar antes de la operación de trigger, crear un trigger "AFTER row" en lugar de un trigger "BEFORE row". CREATE OR REPLACE TRIGGER audit_emp AFTER DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW BEGIN IF DELETING THEN UPDATE audit_table SET del = del + 1 WHERE user_name = user AND table_name = „EMP‟ AND column_name IS NULL; ELSIF INSERTING THEN UPDATE audit_table SET ins = ins + 1 WHERE user_name = user AND tabla name = „EMP‟ AND column_name IS NULL; ELSIF UPDATING ('SAL') THEN UPDATE audit_table SET upd = upd + 1 WHERE user_name = user AND table_name = „EMP‟ AND colunm_name = 'SAL'; ELSE /* The data manipulation operation a general UPDATE*/ UPDATE audit_table SET upd = upd + 1; WHERE user_name = user AND table_name = „EMP‟ AND column name IS NULL; END IF; END; Uso de los calificadores “0LD” y "NEW” Crear un trigger sobre la tabla EMP para añadir registros a la tabla de auditoría, AUDIT_EMP_VALUES, que contiene la actividad del usuario contra la tabla EMP. El trigger registra los valores de varias columnas antes y después del cambio de datos utilizando cualificadores OLD y NEW con el respectivo nombre de columna. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 239 - Mario Juncos Murcia Dentro de un trigger a nivel de fila, referenciamos el valor de una columna antes y después del cambio de los datos, prefijándolo con los cualificadores OLD y NEW. CREATE OR REPLACE TRIGGER audit_emp_values AFTER DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW BEGIN INSERT INTO audit_emp_values (usuario, fecha, empleado, nombre_ant, nombre_nuevo, oficio_ant, oficio_nuevo, salario_ant, salario_nuevo) VALUES (USER, SYSDATE, :old.empno, :old.ename, :new.ename, :old.oficio, :new.oficio, :old.sal, :new.sal); END; Operación de Datos Valor Antiguo Valor Nuevo INSERT NULL Valor insertado UPDATE Valor antes de actualizar Valor después de actualizar DELETE Valor antes de borrar NULL  Los cualificadores OLD y NEW solo están disponibles en triggers a nivel de fila  Prefijar estos cualificadores con dos puntos en cada sentencia PL/SQL y SQL.  No se ponen los dos puntos si están referenciados en la condición de restricción WHEN. Condicionando un Trigger de Registro Para restringir la acción del trigger sólo a aquellos registros que satisfacen cierta condición, proporcionar una claúsula WHEN. Crear un trigger en la tabla EMP para calcular la comisión de un VENDEDOR cuando se añade un registro a la tabla EMP o cuando se modifica el salario del VENDEDOR. El cualificador NEW no necesita ir prefijado con dos puntos en la claúsula WHEN. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 240 - Mario Juncos Murcia CREATE OR REPLACE TRIGGER derive_comision_pct BEFORE INSERT OR UPDATE OF salario ON emp FOR EACH ROW WHEN (new.oficio = 'VENDEDOR') BEGIN IF INSERTING THEN :new.comision:= 0; ELSE -- UPDATE of salario IF :old.comisionIS NULL THEN :new.conm :=0; ELSE :new.comision:= :old.comision* (:new.sal/:old.sal); END IF; END IF; END; Diferenciando entre Triggers y Procedimientos Almacenados TRIGGER PROCEDURE Usa CREATE TRIGGER Usa CREATE PROCEDURE El Dic. de datos contiene el código fuente y el P_Code Invocados implícitamente Invocados explícitamente NO se permite: COMMIT,SAVEPOINT, ROLLBACK Si se permite COMMIT,SAVEPOINT, ROLLBACK Los triggers se compilan completamente cuando se lanza el comando CREATE TRIGGER y se almacena el p_code en el Diccionario de Datos. Por lo tanto, la ejecución del trigger ya no requiere la apertura de un cursor compartido para realizar la acción. En su lugar, el trigger se ejecuta directamente. Aun habiendo algún error durante la compilación de un trigger, éste se crea. RESTRICCIONES PARA LA CREACIÓN DE TRIGGERS El código PL/SQL del cuerpo del trigger puede contener instrucciones de consulta y de manipulación de datos, así como llamadas a otros subprogramas. No obstante, existen restricciones que deben ser contempladas: Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 241 - Mario Juncos Murcia  El bloque PL/SQL no puede contener sentencias de control de transacciones como COMMIT, ROLLBACK o SAVEPOINT.  Tampoco se pueden hacer llamadas a procedimientos que transgredan la restricción anterior.  No se pueden utilizar comandos DDL.  Un trigger no puede contener instrucciones que consulten o modifiquen tablas mutan- tes. Una tabla mutante es aquella que esta siendo modificada por una sentencia UPDATE, DELETE o INSERT en la misma sesión.  No se pueden cambiar valores de las columnas que sean claves primaria, única o ajena de tablas de restricción. Una tabla de restricción es una tabla que debe ser consultada o actualizada directa o indirectamente por el comando que disparó el trigger (normalmente, debido a una restricción de integridad referencial) en la misma sesión. Los triggers a nivel de comando (FOR EACH STATEMENT) no se verán afectados por las restricciones que acabamos de enunciar para las tablas mutantes y tablas de restricción, excepto cuando el trigger se dispare como resultado de una restricción ON DELETE CASCADE. Disparadores de sustitución Se pueden crear triggers que no se ejecuten ni antes ni después, sino en lugar de (INSTEAD OF) la orden de manipulación que da lugar al disparo del trigger. Se denominan disparadores de sustitución. El formato genérico para la creación de estos disparadores de sustitución es: CREATE [OR REPLACE] TRIGGER nombretrigger INSTEAD OF {DELETE / INSERT / UPDATE [OF <lista columnas> } [OR {DELETE / INSERT / UPDATE [OF <lista columnas> }] .. ON nombrevista [FOR EACH ROW] /* aquí comienza el bloque PL/SQL */ [DECLARE <declaraciones>] BEGIN <acciones> [EXCEPTION <gestión de excepciones>] END; Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 242 - Mario Juncos Murcia Los disparadores de sustitución tienen estas características diferenciales:  Solamente se utilizan en triggers asociados a vistas, y son especialmente útiles para realizar operaciones de actualización complejas.  Actúan siempre a nivel de fila, no a nivel de orden, por tanto, a diferencia de lo que ocurre en los disparadores asociados a una tabla, la opción por omisión es FOR EACH ROW  No se puede especificar una restricción de disparo mediante la cláusula WHEN (pero se puede conseguir una funcionalidad similar utilizando estructuras alternativas dentro del bloque PL/SQL). Activar, desactivar, compilar y eliminar un trigger Un trigger puede estar activado o desactivado. Cuando se crea está activado, pero podemos variar esta situación mediante: ALTER TRIGGER nombretrigger DISABLE. Para volver a activarlo utilizamos: ALTER TRIGGER nombretrigger ENABLE. Para volver a compilar emplearemos: ALTER TRIGGER nombretrigger COMPILE Para eliminar un trigger escribiremos: DROP TRIGGER nombretrigger. Vistas con información sobre los triggers Las vistas dba_triggers y user_triggers contienen toda la información sobre los triggers. La estructura de estas vistas se muestran a continuación con algunos ejemplos. SQL> SELECT TRIGGER_NAME, TABLE_NAME, TRIGGERING_EVENT, TRIGGER_TYPE, STATUS FROM USER_TRIGGERS; TRIGGER_NAME TABLE_NAME TRIGGERING_EVENT TRIGGER_TYPE STATUS AUDIT_SUBIDA_SALARIO EMPLEADOS UPDATE AFTER EACH ROW ENABLED T_GES_ EMPLEAD EMPLEAD INSERT OR UPD... INSTEAD OF ENABLED Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 243 - Mario Juncos Murcia SQL> SELECT TRIGGER_BODY FROM USER_TRIGGERS; TRIGGER_BODY BEGIN insert INTO auditaremple VALUES ( „SUBIDA SALARIO EMPLEADO „ || :old.e .... DECLARE V_dept depart.dept_no ° %TYPE; BEGIN IF DELETING THEN ……. SQL> describe dba_triggers Name Null? Type OWNER NOT NULL VARCHAR2(30) TRIGGER NAME NOT NULL VARCHAR2(30) TRIGGER TYPE VARCHAR2(16) TRIGGERING_EVENT VARCHAR2(26) TABLE OWNER NOT NULL VARCHAR2(30) TABLE NAME NOT NULL VARCHAR2(30) REFERENCING NAMES VARCHAR2(87) WHEN_CLAUSE VARCHAR2(2000) STATUS VARCHAR2(8) DESCRIPTION VARCHAR2(2000) TRIGGER BODY LONG Modos de Trigger: Activado o Desactivado Cuando se crea por primera vez un trigger, éste se activa automáticamente. Oracle Server comprueba las restricciones de integridad de los triggers activados y garantiza que los triggers no pueden violar restricciones de integridad. Además, Oracle Server proporciona vistas de lectura consistente para consultas y restricciones, gestiona las dependencias y proporciona commit en dos fases si un trigger actualiza tablas remotas en una base de datos distribuida. Utilizando la sentencia ALTER TRIGGER se desactiva un trigger específico y utilizando la sentencia ALTER TABLE se desactivan todos los triggers en una tabla. Desactivar un trigger para mejorar el rendimiento o para evitar comprobaciones de la integridad de los datos cuando se cargan cantidades masivas de datos a través de utilidades como SQL*Loader. Podríamos desear desactivar el trigger cuando éste referencia a un objeto de la base de datos que actualmente no está disponible, debido a un fallo en la conexión de la red, rotura de disco, fichero de datos off_line, o tablespace off_line. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 244 - Mario Juncos Murcia Compilar un Trigger Utilizar el comando ALTER TRIGGER para recompilar explícitamente un trigger inválido. Cuando lanzamos una sentencia ALTER TRIGGER con la opción RECOMPILE, el trigger se recompila independientemente si éste es válido o inválido. Eliminación de Triggers Para borrar un trigger de la B.D, utilice la sintaxis DROP TRIGGER trigger_name Pruebas sobre Triggers  Pruebe cada una de las operaciones de datos sobre los triggers, así como las operaciones sobre otros datos.  Pruebe cada posibilidad de la cláusula WHEN.  Provoque el disparo del trigger directamente desde una operación básica de datos, así como de forma indirecta desde un procedimiento.  Pruebe el efecto del triggers sobre otros triggers.  Pruebe el efecto de otros triggers sobre el trigger actual. Asegurarse que el trigger funciona de forma apropiada comprobando un número de casos por separado. Aprovechar los procedimientos DBMS_OUTPUT para depurar triggers. Modelo de Ejecución de un Trigger y Comprobación de Constraints 1. Ejecutar todos los triggers. BEFORE STATEMENT 2. Hacer un loop para los registros afectados:  Ejecutar todos los triggers BEFORE ROW  Ejecutar la sentencia DML y efectuar comprobaciones en cuanto al chequeo de la integridad.  Ejecutar todos los triggers AFTER ROW 3. Completar el chequeo de las restricciones de integridad diferidas. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 245 - Mario Juncos Murcia 4. Ejecutar todos los triggers AFTER STATEMENT Una única sentencia DML puede ejecutar potencialmente hasta cuatro tipos de triggers: a nivel de sentencia y de fila , BEFORE y AFTER. Un evento trigger o una sentencia dentro del trigger puede producir una o mas restricciones que hay que comprobar. Los triggers también pueden producir que se ejecuten otros triggers (triggers en cascada). Todas las acciones y comprobaciones hechas como resultado de una sentencia SQL deben realizarse con éxito. Si aparece una excepción dentro de un trigger y la excepción no se maneja explícitamente, se hace roll back sobre todas las acciones realizadas a causa la la sentencia SQL original, incluyendo las acciones realizadas por la ejecución de los triggers. Esto garantiza que los triggers no puedan violar nunca las restricciones de integridad. Cuando se ejecuta un trigger, las tablas referenciadas en la acción del trigger podrían sufrir cambios por las transacciones de otros usuarios. En todos los casos, se garantiza una imagen de lectura consistente para los valores modificados que el trigger necesita leer (consulta) o escribir (actualización). Reglas que Gobiernan los Trigger La lectura y la escritura de datos utilizando triggers están sujetas a ciertas reglas. Regla 1: No lea datos de una tabla mutante. Regla 2: No cambie datos de la clave primaria, clave foranea, o columnas de clave única de una tabla referenciada Lectura de Datos de una Tabla Mutante Una tabla mutante es una tabla que está siendo actualmente modificada por una sentencia UPDATE, DELETE o INSERT, o una tabla que podría necesitar ser actualizada por los efectos de una acción de integridad referencial ON DELETE CASCADE. Una tabla no se considera mutante para los STATEMENT triggers. La tabla del trigger en si misma es una tabla mutante, así como cualquier otra tabla que la referencia con la restricción FOREIGN KEY. Esta restricción previene al trigger de ver un conjunto inconsistente de datos. Tabla Mutante: Ejemplo CREATE OR REPLACE TRIGGER chequea_salario Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 246 - Mario Juncos Murcia BEFORE INSERT OR UPDATE OF sal,oficio, ON emp FOR EACH ROW WHEN (new_oficio <> „PRESIDENTE‟) DECLARE V_minsalario emp.sal%TYPE; V_maxsalario emp.sal%TYPE; BEGIN SELECT MIN(sal), MAX(sal) INTO V_minsalario, v_maxsalario FROM emp WHERE oficio = :new.oficio; IF :new.salario< v_minsalario OR :new.salario> v_maxsalario THEN RAISE_APPLICATION_ERROR(_20505, 'Out of range‟); END IF; END; Este trigger, chequea_salario, garantiza que siempre que se añada un empleado a la tabla EMP o se cambie un salario o puesto de un empleado existente, el salario del empleado caiga dentro del rango del salario establecido para el puesto del empleado. UPDATE emp SET salario= 1500 WHERE ename = „SMITH‟; ERROR at line 2 ORA_4091 : Table EMP is mutating, trigger/function may not see it ORA_06512: at line 4 ORA_04088: error during execution of trigger „chequea_salario‟ Tratar de restringir el salario dentro del rango entre el valor mínimo y el valor máximo existentes produce un error en tiempo de ejecución. La tabla EMP es mutante, o está en estado de cambio, por lo tanto el trigger no puede leerla. Cambiando Datos de una Tabla Restrictiva Una tabla restrictiva es una tabla que el evento de trigger podría tener la necesidad de leer, directamente por una sentencia SQL, o indirectamente por una restricción de integridad referencial. Los STATEMENT triggers no consideran restrictivas a las tablas. Ejemplo Cuando cambia el valor de DEPTNO en la tabla padre DEPT, si intentamos actualizar en cascada los registros correspondientes en la tabla hija EMP, se producirá un error en tiempo de ejecución. CREATE OR REPLACE TRIGGER cascade_updates AFTER UPDATE OF deptno on DEPT FOR EACH ROW BEGIN UPDATE emp SET emp.deptno = :new.deptno Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 247 - Mario Juncos Murcia WHERE emp.deptno = :old.deptno; END; Este trigger intenta actualizar en cascada la clave foranea para los registros hijos en la tabla EMP, a partir del cambio de la clave primaria de la tabla DEPT. UPDATE dept SET deptno = 1 WHERE deptno = 30; * ERROR at line 1: ORA_04091: table DEPT is mutating, trigger/function may not see it Cuando un usuario intenta modificar la tabla DEPT se produce un error. La tabla del trigger, DEPT, referencia a EMP a través de una restricción FOREIGN KEY. Por lo tanto, decimos que EMP es una tabla restrictiva. El trigger CASCADE_UPDATES intenta cambiar los datos en la tabla restrictiva, lo cual no está permitido. lmplementación de Triggers Desarrollar triggers de base de datos con el fin de realzar las características que no se pueden ser mejoradas de otra forma por Oracle Server. Característica Mejora Seguridad Oracle Server permite a los usuarios o roles el acceso a las tablas. Los triggers pemiten el acceso a las tablas según el valor de los datos Auditorías Oracle Server audita las operaciones de datos sobre las tablas. Los triggers auditan los valores de las operaciones de datos sobre las tablas. Integridad de datos Oracle Server refuerza las restricciones de integridad. Los triggers implementan las reglas complejas de integridad. Integridad referencial Oracle Server refuerza las reglas de integridad. Los triggers implementan la funcionalidad no standard. Replicación de tablas Oracle Server copia tablas de forma asíncrona dentro del mecanismo de snapshots. Los triggers copian tablas de manera sincronizada a través de réplicas Datos derivados Oracle Server calcula manualmente los valores de datos derivados. Los triggers calculan automáticamente los valores de los datos derivados. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 248 - Mario Juncos Murcia Control de eventos Oracle Server controla eventos explícitamente. Los triggers controlan eventos de forma transparente Práctica 12 1.- Crear la especificación y el cuerpo de un paquete llamado PROD_PACK que contenga los procedimientos ADD_DEP, UPD_DEP, DEL_DEP y la función Q_DEP.  Hacer todos los programas públicos.  Llamar al procedimiento DEL_DEP.  Consultar la tabla DEPARTAMENTOS para ver el resultado. 2.- Crear y llamar a un paquete que contenga programas privados y públicos.  Crear la especificación y el cuerpo de un paquete llamado EMP_PACK que contenga el procedimiento NEW_EMP como un programa público, y la función VALID_DEPTNO como un programa privado.  Llamar al procedimiento NEW_EMP utilizando 99 como número de departamento.  Llamar al procedimiento NEW_EMP utilizando 30 como número de departamento. 3. Crear un paquete llamado CHK_PACK que contenga los procedimientos CHK_HIREDATE y CHK_DEPT_MGR. Hacerlos como programas públicos.  El procedimiento CHK_HIREDATE comprueba si la fecha de contratación de un empleado está dentro del siguiente rango: [sysdate - 50 years, sysdate + 3 months] o Si la fecha es invalida, aparecería un mensaje de error en la aplicación indicando por que no se acepta la fecha. o Asegurese que se ignora el componente del tiempo en el valor de la fecha. o Utilizar una constante para referirse al límite de 50 años. o Si el valor de la fecha de contrato es un valor nulo, sería considerada como una fecha de contrato inválida.  El procedimiento CHK_DEPT_MGR comprueba la combinación del departamento y del director para un empleado dado. Esto significa que el número de director facilitado debe ser igual al número de director que supervisa el departamento del empleado.  Si la combinación número/director del departamento es inválida, aparecería un mensaje de error en la aplicación.  Asegurese de controlar el caso en el que no hay director para el departamento.  Comprobar el procedimiento CHK_HIREDATE con los siguientes comandos. SQL> execute chk_pack.chk_hiredate(„01-JAN-47‟) SQL> execute chk_pack.chk_hiredate(NULL) SQL> execute chk_pack.chk_hiredate(„01-JAN-98‟) Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 249 - Mario Juncos Murcia Práctica 13 1. Crear un paquete para implementar una nueva regla de negocio.  Crear un procedimiento llamado chk_dept_job para verificar si una combinación dada de número de departamento y oficio es válida. En ese caso "válida" significa que debe ser una combinación que actualmente existe en la tabla EMP. o Utilizar una tabla PLSQL para almacenar las combinaciones válidas de departamento y oficio. o La tabla PLSQL necesita ser rellenada solamente una vez. o Si la combinación no es válida, emitir un mensaje de error en la aplicación.  Probar el procedimiento CHK_DEPT_JOB ejecutando SQL> execute chk_pack.chk_dept_job(20, „CLERK‟); SQL> execute chk_pack.chk_dept_job(40, 'CLERK‟) 2. Crear dos funciones, cada una llamada PRINT_IT para imprimir una fecha, un número dependiendo de cómo se llamó a la función.  Para imprimir el valor de fecha, utilizar como formato de entrada "DD-MON-YY" y como formato de salida "FmMonth/dd/yyyy". Asegurarse de que se manejan entradas inválidas.  Para imprimir el número, utilizar como formato de salida "999,999.00".  Comprobar la primera versión de PRINT_IT con el siguiente comando. SQL> variable todays_date varchar2(20) SQL> execute :todays_date := over_load.print_it(sysdate) PL/SQL Procedure successfully completas. SQL> print todays_date TODAYS_DATE January,29/1998  Comprobar la segunda versión de PRINT_IT con el siguiente comando. SQL> variable g_emp_sal number SQL> execute :g_emp_sal := over_load.print_it(„33,600‟) PL/SQL procedure successfully completas. SQL> print g_emp_sal G_EMP_SAL 33600 Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 250 - Mario Juncos Murcia Práctica 14 1.- Solo se permitirá DML en las tablas durante horas normales de oficina, de 8,45 de la mañana a 5,30 de la tarde, de Lunes a Viernes.  Cree un procedimiento almacenado llamado SECURE_DML que muestre un mensaje de error, en caso de no cumplir la regla especificada anteriormente, como "Sólo puede modificar datos durante las horas oficiales de trabajo". 2.-Crear un trigger en la tabla DEPARTAMENTOS que llame al procedimiento de arriba.  Comprobar el procedimiento modificando temporalmente las horas del procedimiento e intentando insertar un registro en DEPARTAMENTOS. Después de la comprobación, volvamos a definir las horas del procedimiento como se especifica en el paso 1. 3.- La comisión de un vendedor cambiaría con cualquier pedido nuevo o por cambios en los pedidos existentes. Su comisión se almacena en la columna COMM de la tabla EMP. En la tabla CUSTOMER se asigna un vendedor a un cliente en particular.  Crear un procedimiento que actualizará la comisión de un vendedor. Utilizaremos parámetros para enviar el identificador del cliente, el total antiguo del pedido y el total nuevo del pedido, desde el trigger que hace la llamada. El procedimiento necesitará localizar el número del empleado en la tabla CUSTOMER y actualizar el registro del vendedor en la tabla EMP, añadiendo una nueva comisión al valor existente. Para este ejercicio pondremos una porcentaje de comisión del 5%.  Crear un trigger en la tabla ORD el cual llamará al procedimiento, pasando los parámetros necesarios.  Modificar el pedido 601, para asignarle un total de 3€. Verificar que la comisión de WARD se ha incrementado en 0.03. La comisión original era 500. 4. A las tablas EMP y DEPT se les aplica una serie de reglas de negocio.  Decidir cómo implementar cada regla: por medio de restricciones declarativas o triggers. Reglas del Negocio  Los vendedores siempre deberían recibir una comisión. Los empleados que no son vendedores nunca deberán recibir una comisión.  La tabla EMP debería contener exactamente un PRESIDENT. Comprobar nuestra respuesta.  Los salarios solo podrían aumentarse, no disminuirse. Comprobar nuestra respuesta.  Si un departamento se traslada a otra parte, cada empleado de ese departamento tendrá automáticamente un incremento de salario del 2%. Desarrollo de Aplicaciones en Entornos de Cuarta Generación y Herramientas CASE PLSQL - 251 - Mario Juncos Murcia Práctica 15 1.-Responder las siguientes preguntas:  ¿Pueden una tabla o un sinónimo ser inválidos?  Supongamos el siguiente escenario:  El Procedimiento standalone MY_PROC depende del Procedimiento empaquetado MY_PROC_PACK.  La definición del procedimiento MY_PROQ_PACK se cambia mediante la recompilación del cuerpo del paquete.  La especificación del Procedimiento MY_PROQ_PACK no se altera en la modificación del paquete. ¿Se invalida el Procedimiento standalone MY_PROC? 2. Supongamos que hemos perdido el código del Procedimiento NEW_EMP y de la función VALID_DEPTNO que creamos en la práctica 11. Para regenerar el código, crear un fichero SQL "spool" para consultar la vista apropiada del Diccionario de Datos. 3. Ejecutar el script utldtree. Imprimir una estructura de árbol que muestre todas las dependencias relacionadas con nuestro procedimiento NEW_EMP y nuestra función VALID_DEPTNO. Consultar la vista "ideptree" para ver los resultados 4.-Validar los objetos inválidos de forma dinámica.  Hacer una copia de la tabla EMP, llamada EMP_COP.  Modificar la tabla EMP y añadir la columnna TOTSAL(NUMBER(9,2))  Crear un fichero script para imprimir el nombre, tipo y status de los objetos inválidos.  Crear un procedimiento llamado COMPILE_OBJ que recompile todos los objetos inválidos en nuestro esquema.  Utilizar el procedimiento ALTER_COMPILE en el paquete DBMS_DDL.  Ejecutar de nuevo el fichero script previo y comprobar el valor del status de la columna.
Copyright © 2024 DOKUMEN.SITE Inc.