You are on page 1of 34

DESARROLLO DE APLICACIONES CON PL/SQL

JUAN MANUEL MADRID MOLINA

TABLA DE CONTENIDO

1. INTRODUCCION 1.1. Qu es PL/SQL? 1.2. Estructura de un bloque PL/SQL. 2. DECLARACION DE VARIABLES 2.1. Objetivos 2.2. Declaracin de variables y constantes. 2.3. Alcance de las variables y las constantes. EJERCICIOS CAPITULO 1 3. CODIFICACION DE INSTRUCCIONES SQL EN PL/SQL 3.1. Objetivos. 3.2. Instrucciones SQL en PL/SQL. 3.3. Uso de funciones de SQL. 4. CODIFICACION DE INSTRUCCIONES DE CONTROL CONDICIONAL E ITERATIVO 4.1. Objetivos. 4.2. Comparaciones lgicas. 4.3. Uso del IF-THEN-ELSE. LABORATORIO CAPITULO 4 - PARTE 1 4.4. Uso de ciclos repetitivos. 4.4.1. Ciclos simples. 4.4.2. Ciclos FOR numricos. 4.4.3. Ciclos WHILE. 4.5. Uso del GOTO y las etiquetas. LABORATORIO CAPITULO 4 - PARTE 2 5. DECLARACION Y USO DE CURSORES 5.1. Objetivos. 5.2. Qu es un cursor? 5.3. Uso de cursores explcitos. 5.4. Ciclos FOR de cursor. 5.5. Uso del cursor implcito. LABORATORIO CAPITULO 5 6. MANEJO DE ERRORES EN PL/SQL 6.1. Objetivos. 6.2. Qu son las excepciones? 6.3. Funciones para reporte de errores. LABORATORIO CAPITULO 6

1 1 1 3 3 3 5 6 8 8 8 9 11 11 11 11 13 15 15 15 16 17 19 20 20 20 20 24 25 26 27 27 27 31 32

1. INTRODUCCION

1.1. Qu es PL/SQL? Muchas veces, durante la programacin en Oracle, el programador se ve en la necesidad de combinar instrucciones procedimentales (instrucciones que se emplearan en un lenguaje de 3 generacin, como WHILE, REPEAT, IF...THEN...ELSE, etc.). Trabajando en SQL*Plus, esto es demasiado difcil, si no imposible. Surgen entonces, dos maneras de combinar instrucciones procedimentales con instrucciones 4GL: Hacer una interfaz con un lenguaje de 3 generacin (C, Pascal...) Integrar un procesador de instrucciones procedimentales en Oracle. Ambas soluciones existen. La primera es la serie de preprocesadores Pro*Oracle para lenguajes de 3 generacin (Pro*C, Pro*COBOL, etc.). La segunda es una extensin del motor de Oracle llamada PL/SQL. Un bloque de programa PL/SQL es enviado como un todo al motor de la base de datos. El motor enva las instrucciones procedimentales a una seccin llamada ejecutor de instrucciones procedimentales, y las instrucciones SQL al ejecutor normal de instrucciones SQL. Todas estas actividades se coordinan de la manera adecuada para obtener los resultados deseados. Entre las ventajas de PL/SQL se pueden mencionar: Mayor integracin de las aplicaciones con el motor ORACLE. Portabilidad. Velocidad de ejecucin. 1.2. Estructura de un bloque PL/SQL. Un bloque PL/SQL se compone de 3 partes: Una seccin de declaracin de variables y estructuras. Una seccin de cdigo ejecutable. Una seccin de manejadores de excepcin. Esto se conoce como la estructura DECLARE, BEGIN, EXCEPTION, END; y el orden que lleva se puede apreciar en la figura siguiente.

DECLARE

BEGIN

EXCEPTION

END;

Debe notarse que los bloques pueden contener, a su vez, subbloques. Estos subbloques pueden aparecer en cualquier sitio que una instruccin ejecutable podra aparecer legalmente. Cada instruccin finaliza con un ; (punto y coma). Los comentarios se rodean con /* */ , o se preceden con -- (doble guin).

La existencia de los objetos que se declaren en el bloque DECLARE est sujeta a su alcance (ver ms adelante la parte referente a este tema).

2. DECLARACION DE VARIABLES

2.1. Objetivos Al finalizar esta seccin, el lector estar en capacidad de: Identificar declaraciones legales e ilegales de PL/SQL. Aplicar el concepto de alcance de las variables y constantes para hallar valores de variables en un bloque PL/SQL. 2.2. Declaracin de variables y constantes. Los tipos de datos soportados en PL/SQL son: NUMBER CHAR/VARCHAR2 DATE BOOLEAN Los tipos NUMBER, CHAR y DATE se comportan de la misma manera que en el RDBMS. El tipo BOOLEAN no tiene correspondencia en el RDBMS, y puede contener los valores TRUE, FALSE o NULL. SINTAXIS DE LA DECLARACION:
identificador [CONSTANT] tipo_de_datos [NOT NULL] [:=expresin_plsql];

Ejemplos: DECLARE nombre, apellido CHAR(20); -- ilegal DECLARE nombre apellido CHAR(20); -- legal CHAR(20); -- legal

NOTAS: Las reglas para los identificadores son las mismas que para los objetos SQL. Se puede usar el NOT NULL para restringir una variable, de manera que nunca pueda ser nula. Las variables declaradas como NOT NULL deben inicializarse. Slo se permite un identificador por lnea. No se permiten arreglos. Ms ejemplos: DECLARE cuenta ventas segundos_da

NUMBER; NUMBER(9,2); CONSTANT NUMBER := 60*60*24;

total_corrida inicial apellido nom_cia aniversario fin_proyecto sig_chequeo valido disponible

NUMBER(10,0) := 0; CHAR; CHAR(10) NOT NULL := 'SNOOPY'; CONSTANT CHAR(12) := 'ORACLE'; DATE := '05-JUL-59'; DATE; DATE NOT NULL := '28-DEC-89'; BOOLEAN NOT NULL := FALSE; BOOLEAN := NULL;

USO DE %TYPE Y %ROWTYPE Los objetos de la base de datos (tablas, columnas) y los objetos de PL/SQL (variables y constantes) se pueden relacionar mediante el uso de %TYPE y %ROWTYPE. Estos atributos son tiles cuando se desea estar seguro que dos tipos de objetos coinciden totalmente. Ejemplos: DECLARE libros_impresos libros_vendidos nombre_dama dept_row

NUMBER(6); libros_impresos%TYPE; emp.ename%TYPE; dept%ROWTYPE;

En este caso, la variable libros_vendidos tendr el mismo tipo de datos de libros_impresos. La variable nombre_dama tendr el tipo de la columna ENAME de la tabla EMP. La variable dept_row ser un registro con la misma estructura de una fila de la tabla DEPT. Su componentes se acceden de la siguiente manera: dept_row.deptno, dept_row.ename y dept_row.loc. EXPRESIONES Y ASIGNACIONES La sintaxis de una asignacin es: variable_plsql := expresin_plsql; Ntese que se emplea el smbolo := para asignar, y = para comparaciones. El tipo de datos en ambos lados de la asignacin debe ser igual, o convertible implcitamente. (Las conversiones de CHAR a NUMBER o DATE son automticas; de NUMBER o DATE a CHAR deben efectuarse explcitamente con la funcin TO_CHAR). No se admiten referencias a columnas o a tablas en ninguno de los lados de la asignacin.

Ejemplos de asignaciones: N=Number C=Char B=Boolean := 5 * N2 *O.7; N := TRUE; B

D=Date

D N B N/C/B/D B C B

:= := := := := := :=

'11-JUN-87'; N2 + SQRT((N2**2 - 4*N3*N4) / 2*N3); (EN_STOCK) OR (PUEDE_PEDIRSE); 'Antonio ' || 'y' || ' Cleopatra'; NULL; TO_CHAR(N1**2); N1 > 2*N2;

2.3. Alcance de las variables y las constantes. El ALCANCE se refiere a la visibilidad de los identificadores en diferentes puntos de un bloque PL/SQL. REGLAS DE ALCANCE: 1. Un identificador es visible en el bloque en que se declare y en todos sus sub-bloques, a menos que se cumpla la regla #2. 2. Si un identificador declarado en un bloque es redeclarado en un sub-bloque, el identificador original no es visible dentro del sub-bloque (se ve el redeclarado). Este identificador redeclarado tiene las reglas de alcance definidas en la regla 1.

EJERCICIOS CAPITULO 1 Revise cada una de las siguientes declaraciones. Diga cules no estn correctas, y explique por qu. DECLARE mi_var NUMBER(7,2); DECLARE x,y,z CHAR(10); DECLARE tam_max CONSTANT NUMBER := 256; DECLARE fecha_nac DATE NOT NULL; DECLARE en_stock BOOLEAN := 1; DECLARE nombre CHAR(15); inicial CHAR; DECLARE peso lugar BEGIN

NUMBER := 600; CHAR(10) := 'Europa';

DECLARE peso NUMBER := 1; otro_lugar CHAR(20); BEGIN peso := peso+1; otro_lugar := lugar || ' Occidental'; END; DECLARE otro_peso NUMBER; continente CHAR(10); BEGIN continente := lugar; otro_peso := peso + 10; END; peso := peso -50; lugar := otro_lugar || ' y Asia'; END; Evale el bloque PL/SQL anterior. Aplique las reglas de alcance para determinar los valores donde se indica.

3. CODIFICACION DE INSTRUCCIONES SQL EN PL/SQL

3.1. Objetivos. Al finalizar esta seccin, el lector estar en capacidad de: Usar instrucciones SQL en procedimientos PL/SQL. Identificar las funciones SQL soportadas en PL/SQL. 3.2. Instrucciones SQL en PL/SQL. Los comandos SQL soportados en PL/SQL son los comandos DML del SQL (INSERT, UPDATE, DELETE, SELECT...INTO). Se pueden colocar variables PL/SQL en cualquier lugar que se pueda colocar legalmente una constante. Los identificadores se chequean primero para comprobar si son columnas de la base de datos. Si no se encuentra en la base de datos, se asume que es un identificador PL/SQL. Las instrucciones PL/SQL no pueden aparecer como parte de una expresin. No se soportan comandos DDL ni DCL del SQL (ALTER, DROP, CREATE...) Ejemplo de INSERT: DECLARE sueldo NUMBER(7,2) := 3040.55; nombre CHAR(25) := 'PEDRO'; ingreso DATE := '08-SEP-88'; BEGIN INSERT INTO emp (empno, ename, job, hiredate, sal, deptno) VALUES (2741,nombre,'CHOFER',ingreso,sueldo,20); END; Ejemplo de UPDATE: DECLARE maximo CONSTANT NUMBER := 5000; buen_cli CHAR(8) := 'VIP'; BEGIN UPDATE cuentas SET limite_credito = maximo WHERE estado = 'PAZ Y SALVO' OR estado = 'buen_cli'; END; Ejemplo de DELETE: DECLARE tipo_pers CHAR(8) := 'Odioso'; BEGIN

DELETE FROM lista_amigos WHERE tipo_amigo = tipo_pers; END; Uso del SELECT INTO: El SELECT es la nica instruccin DML que retorna datos. El sitio donde quedarn estos datos se especifica por medio de la clusula INTO. Las instrucciones SELECT...INTO deben retornar exactamente una fila. Si no se retorna ninguna o se retornan varias, se genera un error. Para SELECTs de mltiples filas se emplean los cursores (ver ms adelante). SINTAXIS DEL SELECT...INTO: SELECT col1, col2, ... INTO var1, var2, ... FROM nombre_tabla WHERE ... Ejemplo: DECLARE nom_parte en_stock

partes.nombre%TYPE; partes.num%TYPE;

BEGIN SELECT nombre, num INTO nom_parte, en_stock FROM partes WHERE cod_parte = 624; -- manipule los datos obtenidos aqu... END; Procesamiento de transacciones: Se pueden emplear los comandos COMMIT, ROLLBACK y SAVEPOINT para el procesamiento de las transacciones. 3.3. Uso de funciones de SQL. Las funciones que se pueden referenciar en un comando DML pueden ser de estos tipos: Numricas (SQRT, ROUND, POWER...) Caracter (LENGTH,UPPER...) Fecha (ADD_MONTHS, MONTHS_BETWEEN...) Grupo (AVG, MAX, COUNT...) Por fuera de los comandos DML (en expresiones, por ejemplo) se permiten todas las funciones, exceptuando las de grupo. Ejemplos: x := SQRT(y); apellido := UPPER(apellido); dif_aos := MONTHS_BETWEEN(fecha1, fecha2)/12;

4. CODIFICACION DE INSTRUCCIONES DE CONTROL CONDICIONAL E ITERATIVO

4.1. Objetivos. Al finalizar esta seccin, el lector estar en capacidad de: Demostrar el dominio del control condicional en PL/SQL, escribiendo un programa PL/SQL que ejecute instrucciones de manera condicional. Identificar los cuatro tipos de ciclos soportados por PL/SQL. Demostrar el dominio del control iterativo en PL/SQL, escribiendo un programa PL/SQL que emplee ciclos. Diferenciar entre instrucciones GOTO vlidas e invlidas. Identificar tres usos para las etiquetas de PL/SQL. 4.2. Comparaciones lgicas. Las comparaciones lgicas son la base del control condicional en PL/SQL. Los resultados de estas comparaciones son siempre TRUE, FALSE o NULL. Comparaciones con valores nulos. Cualquier cosa comparada con NULL da NULL. Un NULL en una expresin hace que dicha expresin valga NULL (excepto en la concatenacin). Ejemplos: 5 + NULL -- retorna NULL 'PL/' || NULL || 'SQL' -- retorna PL/SQL Comportamiento de los operadores booleanos: AND, OR, NOT.
AND TRUE FALS E NULL TRUE T F N FALS E F F F NULL N F N OR TRUE FALS E NULL TRUE T T T FALS E T F N NULL T N N NOT TRUE FALS E NULL F T N

4.3. Uso del IF-THEN-ELSE. SINTAXIS: IF <condicin> THEN <secuencia de instrucciones> [ELSIF <condicin> THEN <secuencia de instrucciones>] -- se pueden colocar ms ELSIF [ELSE <secuencia de instrucciones>]

10

END IF; Notas: Las condiciones deben evaluar a un valor booleano (TRUE, FALSE, NULL) Si la condicin es verdadera, la secuencia de instrucciones asociada se ejecuta; de lo contrario, no. A lo ms una secuencia de instrucciones se ejecuta. Ejemplo:
DECLARE papeles NUMBER(7);

BEGIN SELECT COUNT(*) INTO papeles FROM actuaciones WHERE cod_actor = &&actor AND activo = 'SI'; IF papeles > 90 THEN UPDATE actor SET calidad_actor = 'Tiempo de OSCAR' WHERE cod_actor = &&actor; ELSIF papeles > 75 THEN UPDATE actor SET calidad_actor = 'Telenovelero' WHERE cod_actor = &&actor; ELSE UPDATE actor SET calidad_actor = 'Mesero' WHERE cod_actor = &&actor; END IF; COMMIT; END;

Evite la trampa del nulo cuando codifique instrucciones IF-THEN-ELSE. BLOQUE 1 BLOQUE 2 IF a >= b THEN IF b > a THEN haga_esto; haga_aquello; ELSE ELSE haga_aquello; haga_esto; END IF; END IF; Dado cualquier par de valores no nulos para a y b, Bloque 1 y Bloque 2 hacen lo mismo? Qu ocurre si a b (o las dos) son NULL?

11

LABORATORIO CAPITULO 4 - PARTE 1 Emplee las descripciones de tablas dadas para escribir un programa PL/SQL que solucione el problema mostrado abajo. Ensaye el bloque ejecutndolo desde SQL*Plus. Obtenga un nmero de pedido del operador y encuentre el COD_PRODUCTO asociado. Busque en la tabla INVENTARIO. Si el producto est "EN EXISTENCIA", coloque una fecha en la FECHA_ENTREGA asociada al pedido, la cual debe estar 7 das despus de la fecha actual (SYSDATE + 7). Si el producto est "PEDIDO", coloque una fecha en la FECHA_ENTREGA asociada al pedido, la cual debe estar 1 mes despus de la fecha actual (ADD_MONTHS(SYSDATE,1)). Si el producto es un "PEDIDO ESPECIAL", coloque una fecha en la FECHA_ENTREGA asociada al pedido, la cual debe estar 2 meses despus de la fecha actual (ADD_MONTHS(SYSDATE,2)) y rellene el pedido especial insertando una fila en la tabla PEDIDOS_ESPECIALES. Use el NUM_PED de la tabla PEDIDOS_CLI. Emplee la cantidad estndar de pedido (CANT_STD_PED) en el pedido.
TABLA INVENTARIO NUMBER(6) COD_PRODUCTO -----------1 2 3 CHAR(30) DESCRIP_PROD ----------------Chaqueta estilo 1 Chaqueta estilo 2 Chaqueta estilo 3 CHAR(20) ESTADO ---------------EN EXISTENCIA PEDIDO PEDIDO ESPECIAL NUMBER(3) CANT_STD_PED -----------100 200 300

TABLA PEDIDOS_CLI NUMBER(12) NUM_PED ------------1 2 3 4 5 6 NUMBER(6) COD_PRODUCTO ------------1 1 2 1 2 3 DATE FECHA_ENTREGA -------------

TABLA PEDIDOS_ESPECIALES NUMBER(12) NUM_PED -----------NUMBER(6) COD_PRODUCTO ------------NUMBER(3) CANTIDAD -------------

No olvide colocar lo siguiente al final del bloque PL/SQL: . /

12

Esto es necesario para que el bloque se ejecute. Las instrucciones del SQL*Plus (si coloca alguna) deben ir por fuera del bloque PL/SQL.

13

4.4. Uso de ciclos repetitivos. Existen 4 tipos diferentes de ciclos repetitivos: Ciclos simples Ciclos FOR numricos Ciclos WHILE Ciclos FOR de cursor 4.4.1. Ciclos simples. SINTAXIS: LOOP <secuencia de instrucciones> END LOOP; -- este es un ejemplo de ciclo infinito Cualquier tipo de ciclo se abandona de forma inmediata con la orden EXIT. SINTAXIS: EXIT [WHEN <condicin> ]; -- para evitar el ciclo infinito Ejemplos:
DECLARE cont NUMBER(3) :=0; BEGIN LOOP INSERT INTO LOG VALUES (cont, 'ciclos realizados'); cont := cont + 1; IF cont = 500 EXIT; END IF; END LOOP; END; DECLARE cont NUMBER(3) := 0; BEGIN LOOP UPDATE tabla1 SET comentario = 'ACTUALIZADA' WHERE columna = cont; cont := cont + 1; EXIT WHEN cont = 100; END LOOP; END;

4.4.2. Ciclos FOR numricos. SINTAXIS: FOR <ndice> IN [ REVERSE ] <entero>..<entero> LOOP <secuencia de instrucciones> END LOOP; El ndice del ciclo toma cada valor del rango, uno a la vez, en orden ascendente o descendente. Ejemplo:

14

BEGIN FOR i IN 1..500 LOOP INSERT INTO temp (col1, mensaje) VALUES (i, 'No debo dormirme en clase.'); END LOOP; END;

Acerca del ndice: Es implcitamente de tipo NUMBER. Slo est definido dentro del ciclo. El ndice se puede referenciar en expresiones, pero su valor no puede ser cambiado. Ejemplos:
DECLARE ndice CHAR(20) := 'Pedro Pechugas'; BEGIN FOR ndice IN REVERSE 21..30 LOOP -- redeclara a ndice INSERT INTO TEMP (col1) VALUES (ndice); -- inserta los nmeros del 30 al 21 END LOOP; END; ... FOR i IN 1..256 LOOP x := x + i; -- legal i := i + 5; -- ilegal END LOOP; ...

4.4.3. Ciclos WHILE. SINTAXIS: WHILE <condicin> LOOP <secuencia de instrucciones> END LOOP; La condicin puede ser cualquier condicin legal PL/SQL (debe retornar TRUE, FALSE o NULL). La secuencia de instrucciones ser repetida mientras la condicin sea evaluada como TRUE.

Ejemplo:
DECLARE cont NUMBER(3) :=0; BEGIN WHILE cont < 500 LOOP INSERT INTO temp (col1, mensaje) VALUES (cont, 'Bueno, tal vez podra dormir un poco.'); cont := cont +1; END LOOP; END;

4.5. Uso del GOTO y las etiquetas. SINTAXIS:

15

<<nombre_etiqueta>> ... GOTO nombre_etiqueta; OJO! No todos los GOTOs son legales! (Un GOTO slo se puede hacer a una etiqueta que est antes de dicho GOTO en el programa; en otras palabras, no se puede hacer GOTO hacia adelante). USO DE LAS ETIQUETAS. Se pueden usar para etiquetar los bloques, con el fin de diferenciar los BEGINs y los ENDs (til en programas largos):
<<etiqueta>> [DECLARE -- declaraciones ] BEGIN -- cdigo ejecutable [EXCEPTION -- manejadores de excepcin ] END etiqueta; -- debe incluirse aqu el nombre de la etiqueta

Los bloques tambin se pueden etiquetar para permitir que una variable que resultara escondida por el nombre de una columna se pueda referenciar.

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

Los bloques y ciclos tambin se pueden etiquetar para referenciar un objeto que de otra manera no sera visible a causa de las reglas de alcance.

<<ciclo_clculo>> FOR i IN 1..10 LOOP <instrucciones...> DECLARE i NUMBER := 0; BEGIN INSERT INTO temp (col1, col2, mensaje) VALUES (i, ciclo_clculo.i, 'COMPLETO'); END; END LOOP ciclo_clculo; -- debe incluirse el nombre del ciclo aqu

Los EXITs tambin se pueden etiquetar para especificar salidas de ciclos ms externos.

... <<ciclo_externo>> WHILE a>b LOOP b := b+1; <<ciclo_interno>> WHILE b>c LOOP

16

c := c+2; EXIT ciclo_externo WHEN c > 200; END LOOP ciclo_interno; END LOOP ciclo_externo; ...

17

LABORATORIO CAPITULO 4 - PARTE 2 Emplee las descripciones de tablas dadas para escribir un programa PL/SQL que solucione el problema mostrado abajo. Ensaye el bloque ejecutndolo desde SQL*Plus. Escriba un programa PL/SQL que tenga un ciclo externo y uno interno. El externo itera 5 veces, el interno 4. En el ciclo externo, inserte una fila en la tabla TEMP que tenga el ndice del ciclo en COL1, NULL en COL2 y el mensaje 'Ciclo externo' en MENSAJE. En el ciclo interno, inserte una fila en la tabla TEMP que contenga el ndice del ciclo externo en COL1, el ndice del ciclo interno en COL2, y el mensaje 'Ciclo interno' en MENSAJE. TABLA TEMP NUMBER(9,4) COL1 -----------NUMBER(9,4) COL2 -------------CHAR(55) MENSAJE -------------

18

5. DECLARACION Y USO DE CURSORES

5.1. Objetivos. Al finalizar esta seccin, el lector estar en capacidad de: Identificar los dos tipos de cursores soportados por PL/SQL. Esquematizar los pasos para usar cursores explcitos en bloques PL/SQL. Mostrar el dominio de los cursores, escribiendo programas PL/SQL que los usen. Usar los atributos del cursor implcito. 5.2. Qu es un cursor? Un cursor es un rea de memoria que se reserva para efectuar una serie de operaciones sobre una serie de filas, en los casos en que un SELECT retorna ms de una fila. Todo comando DML de SQL (en particular, todo INSERT, UPDATE, DELETE, SELECT...INTO, COMMIT y ROLLBACK) que sea procesado por PL/SQL tiene asociado un cursor. Existen dos tipos de cursores: EXPLICITOS: Se usan en SELECTs que retornan ms de una fila. IMPLICITOS: Todos los INSERTs, UPDATEs, DELETEs y los SELECTs que retornan una sola fila hacen uso del cursor implcito. 5.3. Uso de cursores explcitos. El conjunto de filas que retorna una consulta puede constar de ninguna, una o varias filas. Esto depende del nmero de filas que satisfagan la condicin de bsqueda. Cuando se sabe que una consulta puede retornar mltiples filas, se puede definir un cursor explcito para: Procesar, adems de la primera fila retornada, todas las que le sigan. Tener presente qu fila es la que se est procesando. Los pasos para el uso de un cursor explcito son: Declarar el cursor Abrir el cursor Extraer los datos del cursor Cerrar el cursor DECLARACION DE UN CURSOR La declaracin del cursor asocia su nombre con una instruccin SELECT. Este SELECT no debe tener clusula INTO. Los cursores tienen alcance, as como las variables.

19

SINTAXIS: DECLARE CURSOR <nombre_cursor> IS SELECT ... ; Ejemplo:


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

APERTURA DE UN CURSOR La apertura de un cursor procesa la orden SELECT, asigna los recursos ORACLE y el rea de memoria necesarios para almacenar temporalmente el resultado de este SELECT, y almacena el resultado en el rea de memoria. SINTAXIS: OPEN <nombre_cursor>; EXTRACCION DE DATOS DE UN CURSOR Se hace mediante el comando FETCH: FETCH <nombre_cursor> INTO <var1, var2, ...>; Debe haber exactamente una variable en el INTO por cada columna que traiga el SELECT. La primera columna quedar en var1, la segunda en var2, y as sucesivamente. CLAUSURA DE UN CURSOR La clausura de un cursor marca los recursos usados por el cursor como reutilizables. No se pueden extraer filas de un cursor cerrado. SINTAXIS: CLOSE <nombre_cursor>; ATRIBUTOS DE LOS CURSORES EXPLICITOS %NOTFOUND toma el valor TRUE si el FETCH anterior no retorn fila alguna. LOOP FETCH cur1 INTO nombre, sueldo; EXIT WHEN cur1%NOTFOUND; -- sigue el procesamiento de los datos END LOOP; %FOUND toma el valor TRUE si el FETCH anterior retorn una fila. FETCH cur1 INTO nombre, sueldo; WHILE cur1%FOUND LOOP -- sigue el procesamiento de los datos FETCH cur1 INTO nombre, sueldo;

20

END LOOP; %ROWCOUNT toma el valor de las filas a las que se les ha hecho FETCH hasta el momento. LOOP FETCH cur1 INTO nombre, sueldo; EXIT WHEN (cur1%NOTFOUND) OR (cur1%ROWCOUNT > 10); -- sigue el procesamiento de los datos END LOOP; %ISOPEN toma el valor TRUE si el cursor asociado est abierto. IF cur1%ISOPEN THEN FETCH cur1 INTO nombre, sueldo; ELSE OPEN cur1; END IF; Ejemplo completo del uso de un cursor:
DECLARE limite NUMBER(4):=0; nombre emp.ename%TYPE; sueldo emp.sal%TYPE; CURSOR cur1 IS SELECT ename, sal FROM emp WHERE sal > limite; BEGIN limite := 1200; OPEN cur1; -- emplea 1200 como lmite LOOP FETCH cur1 INTO nombre, sueldo; EXIT WHEN cur1%NOTFOUND; INSERT INTO otra_tabla VALUES (nombre, sueldo); END LOOP; CLOSE cur1; COMMIT; END;

Si el cursor posee en su declaracin variables que cambien su valor en el transcurso del programa, el hecho de cerrar el cursor y volverlo a abrir hace que el SELECT se vuelva a ejecutar con el nuevo valor de la variable. Ejemplo:
DECLARE total_bonos NUMBER(8,2) := 0; minimo emp.sal%TYPE := 1000; -- para iniciar valor_bono emp.sal%TYPE; CURSOR bono IS SELECT sal*.10 FROM emp WHERE sal > minimo; BEGIN OPEN bono; -- usa un mnimo de 1000 LOOP FETCH bono INTO valor_bono; EXIT WHEN bono%NOTFOUND; total_bonos := total_bonos + valor_bono; IF total_bonos > 2000 THEN /* subir el mnimo e intentarlo de nuevo */

21

minimo := minimo + 500; total_bonos := 0; -- poner a ceros el total CLOSE bono; OPEN bono; -- reabre el cursor con el minimo nuevo END IF; END LOOP; END;

Si se desea referenciar por algn motivo la fila de la tabla que est siendo procesada, se emplea la clusula WHERE CURRENT OF. En este caso, el cursor debe ser declarado con la clusula FOR UPDATE OF. Ejemplo:
DECLARE CURSOR cur1 IS SELECT ename, sal FROM emp FOR UPDATE of sal; nombre emp.ename%TYPE; sueldo emp.sal%TYPE; BEGIN OPEN cur1; LOOP FETCH cur1 INTO nombre, sueldo; EXIT WHEN cur1%NOTFOUND; IF sueldo > 3000 THEN DELETE FROM emp WHERE CURRENT OF cur1; END IF; ... END LOOP; ...

5.4. Ciclos FOR de cursor. Los ciclos FOR de cursor son similares a los ciclos FOR numricos: Mientras que los ciclos FOR numricos precisan de un rango numrico para funcionar, los ciclos FOR de cursor emplean un subconjunto de filas de la tabla determinado por el cursor. Un ciclo FOR numrico se ejecuta una vez para cada valor del rango numrico. Un ciclo FOR de cursor se ejecuta una vez para cada fila trada por el cursor. SINTAXIS: FOR <nombre_registro> IN <nombre_cursor> --- lista de comandos a repetir END LOOP; <nombre_registro > se declara de manera implcita como: nombre_registro nombre_cursor%ROWTYPE; Para referenciar alguno de sus componentes, se emplea nombre_registro.columna. El funcionamiento es el siguiente: Cuando se inicia el ciclo, se hace un OPEN implcito del cursor. Para cada fila del cursor se hace un FETCH implcito.

la

notacin

22

Cuando no quedan ms filas por traer, el ciclo finaliza y se efecta un CLOSE implcito del cursor.

Ejemplo:
DECLARE lmite NUMBER(4) := 0; total NUMBER(9,2) := 0; CURSOR cur1 IS SELECT ename, sal FROM emp WHERE sal > lmite; BEGIN lmite := 1200; -- a continuacin se hace un OPEN implcito FOR fila IN cur1 LOOP -- aqu se efecta un FETCH implcito INSERT INTO otra_tabla VALUES (fila.ename, fila.sal); total := total + fila.sal; END LOOP; -- sa hace aqu un CLOSE implcito ... END;

USO DE PARAMETROS EN LOS CURSORES DECLARE CURSOR <nombre_cursor> [(nombre_parmetro tipo)] IS SELECT... ; Ejemplo: DECLARE CURSOR cur2 (valor NUMBER) IS SELECT ename FROM emp WHERE sal > valor; BEGIN OPEN cur2 (1200) ... END; 5.5. Uso del cursor implcito. Toda instruccin DML de SQL (INSERT, UPDATE, DELETE, SELECT...INTO) que no tenga un cursor explcito asociado con ella tiene asociado el cursor implcito, conocido tambin como el cursor SQL. Para este cursor no tienen sentido las operaciones de OPEN, FETCH ni CLOSE. Todos los atributos de cursor son vlidos en su contexto. SQL%NOTFOUND retorna TRUE si la instruccin SQL ms recientemente ejecutada no afect ninguna fila. SQL%FOUND retorna TRUE si la instruccin SQL ms recientemente ejecutada afect una o ms filas. SQL%ROWCOUNT retorna el nmero de filas afectadas por un DELETE o un UPDATE, el nmero de filas insertadas, o el nmero de filas retornadas por un SELECT...INTO.

23

SQL%ISOPEN siempre retorna FALSE.

24

LABORATORIO CAPITULO 5 Escriba un programa PL/SQL que solucione el problema mostrado abajo. Ensaye el bloque ejecutndolo desde SQL*Plus. Obtenga un nmero n del operador, usando una variable &. Usando un ciclo simple, obtenga el nombre y sueldo de los empleados con los n salarios ms altos de la empresa. Estos nombres y salarios se pueden encontrar en la tabla EMP. Almacnelos en una tabla llamada SALARIOS_ALTOS, con columnas NOMBRE CHAR(20) y SALARIO NUMBER(7,2). Repita este punto usando un ciclo FOR de cursor.

25

6. MANEJO DE ERRORES EN PL/SQL

6.1. Objetivos. Al finalizar esta seccin, el lector estar en capacidad de: Identificar los dos tipos de manejo de excepciones en PL/SQL. Identificar las funciones de PL/SQL que dan informacin acerca de los errores. Escribir bloques de PL/SQL que contengan instrucciones para manejo de excepciones. 6.2. Qu son las excepciones? En PL/SQL, los errores se denominan excepciones. Cuando una excepcin es disparada (ocurre un error), el flujo del procesamiento salta a los manejadores de excepciones. Un manejador de excepcin es una secuencia de instrucciones que se procesan cuando ocurre determinado error. Cuando termina el manejador de excepcin, el flujo del programa abandona el bloque. TIPOS DE EXCEPCIONES: Excepciones internas predefinidas: Se corresponden con aproximadamente 20 errores ORACLE comunes. PL/SQL las dispara automticamente en respuesta a un error ORACLE. Excepciones definidas por el usuario: Deben ser declaradas, y se deben disparar manualmente. EXCEPCIONES INTERNAS PREDEFINIDAS. Todo error ORACLE dispara una excepcin automticamente; algunas de las ms comunes tienen nombres. Ejemplos:
TOO_MANY_ROWS (ORA-01427): A single row SELECT returned more than one row NO_DATA_FOUND (ORA-01403): A single row SELECT returned no data INVALID_CURSOR (ORA-01001): Invalid cursor was specified VALUE_ERROR (ORA-06502): Arithmetic, numeric, string, conversion, or constraint error occurred. ZERO_DIVIDE (ORA-01476): Attempted to divide by zero. DUP_VAL_ON_INDEX (ORA-00001) Attempted to insert a duplicate value into a column that has a unique index specified.

26

CURSOR_ALREADY_OPEN (ORA-06511) Attempted to open an already open cursor.

DECLARACION DE LOS MANEJADORES DE EXCEPCION


WHEN <nombre_excepcin> [ OR <nombre_excepcin> ... ] THEN <secuencia de instrucciones> ... [ WHEN OTHERS THEN -- si se usa, debe ser el ltimo manejador <secuencia de instrucciones> ]

Ejemplo:
DECLARE num_emp emp.empno%TYPE;

BEGIN SELECT empno INTO num_emp FROM emp WHERE ename = 'BLAKE'; INSERT INTO temp (col1, mensaje) VALUES (num_emp, 'Cdigo de Blake'); DELETE FROM emp WHERE ename = 'BLAKE'; EXCEPTION WHEN NO_DATA_FOUND THEN ROLLBACK; INSERT INTO temp (mensaje) values ('No se encontr a BLAKE'); COMMIT; WHEN TOO_MANY_ROWS THEN ROLLBACK; INSERT INTO temp (mensaje) values ('Se hall ms de un BLAKE'); COMMIT; WHEN OTHERS THEN ROLLBACK; END;

EXCEPCIONES DEFINIDAS POR EL USUARIO. Ejemplo: DECLARE x NUMBER; excep EXCEPTION; -- un tipo nuevo de objeto Cmo dispararla: RAISE excep; Una vez que una excepcin es disparada manualmente, se la trata exactamente de la misma manera que a una excepcin predefinida. Las excepciones tienen alcance, como las variables. Las excepciones definidas por el usuario se chequean manualmente y luego se disparan, si es necesario.

Ejemplo:

27

DECLARE nombre proy_asig pocos_proy BEGIN

emp.ename%TYPE := 'BLAKE'; NUMBER; EXCEPTION;

-- obtener nmero de proyectos asignados a Blake ... IF proy_asig < 3 THEN RAISE pocos_proy; END IF; EXCEPTION WHEN pocos_proy THEN INSERT INTO temp VALUES (nombre, proy_asig, 'Menos de 3 proyectos!'); COMMIT; ... END;

PROPAGACION DE LAS EXCEPCIONES Procedimiento: Al dispararse la excepcin, se busca un manejador en el bloque actual. Si no se encuentra, se sigue con el paso 2. Si este bloque tiene un padre, se busca el manejador en el padre. Se repiten los pasos 1 y 2 hasta que se llega al bloque raz, o se encuentra un manejador. - Si se llega al bloque raz y no se encuentra un manejador, la excepcin se le pasa al ambiente que llama al PL/SQL (SQL*Plus, SQL*Forms, etc.) - Si se encuentra un manejador, se ejecuta. Cuando termina, el control pasa al bloque padre del bloque donde se encontr el manejador, o al ambiente si el bloque donde se hall el manejador es el raz. Puede haber nicamente un manejador activo al tiempo por bloque. Si se dispara una excepcin en un manejador, la bsqueda de un manejador para esta nueva excepcin se inicia en el bloque padre del bloque actual.

Ejemplo:

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

28

EXCEPTION
WHEN B THEN ...

END;
En este ejemplo, en la ejecucin del bloque ms interno: Si se dispara la excepcin A, se encuentra un manejador en el mismo bloque. Este se ejecuta y el flujo del programa contina en el bloque exterior. Si se dispara la excepcin B, sta se propaga al bloque exterior, donde es manejada. Una vez ejecutado el manejador, el control se le devuelve al ambiente. Si se dispara la excepcin C, no se encuentra manejador alguno, y el programa finaliza con un error en tiempo de ejecucin. OTROS USOS DE RAISE RAISE, por s solo, vuelve a disparar la excepcin actual (como si sta se propagara). Esta modalidad de RAISE slo se puede emplear en un manejador de excepcin. COMO PONERLE NOMBRE A LOS ERRORES DE ORACLE Las excepciones slo pueden ser manejadas por nombre (no por cdigo ORACLE de error). Para colocarle un nombre a un error de ORACLE, de manera que se le pueda programar un manejador, se emplea la clusula EXCEPTION_INIT:
PRAGMA EXCEPTION_INIT(<nombre_de_usuario>, <cdigo_error_ORACLE>);

Ejemplo: DECLARE abrazo_mortal EXCEPTION PRAGMA EXCEPTION_INIT(abrazo_mortal, -60); 6.3. Funciones para reporte de errores. Estas funciones dan informacin acerca de la excepcin que se est manejando en determinado momento. Son especialmente tiles en el manejador OTHERS. SQLCODE: Retorna el cdigo de error ORACLE asociado con la excepcin, o 1 si la excepcin es definida por el usuario. SQLERRM: Retorna el error relacionado con el valor actual de SQLCODE. Puede usarse tambin como argumento cualquier cdigo de error ORACLE. Si no hay excepcin activa, SQLCODE retorna 0, y SQLERRM retorna 'Normal, successful completion'. SQLCODE y SQLERRM no se pueden usar en instrucciones SQL.

Ejemplo: DECLARE sqlcode_val sqlerrm_val

NUMBER; CHAR(55);

29

BEGIN ... EXCEPTION WHEN OTHERS THEN sqlcode_val:=SQLCODE; -- SQLCODE no se puede insertar directamente sqlerrm_val:=SUBSTR(SQLERRM,1,55); -- SQLERRM no se puede insertar directamente -- Debe usarse SUBSTR para garantizar que la -- cadena retornada cabe en -- la variable de destino. INSERT INTO temp (col1, mensaje) VALUES(sqlcode_val, sqlerrm_val); END;

30

LABORATORIO CAPITULO 6 Emplee las descripciones de tablas dadas para escribir un programa PL/SQL que solucione el problema mostrado abajo. Ensaye el bloque ejecutndolo desde SQL*Plus. La tabla ACCION define un conjunto de acciones que se realizarn sobre la tabla CUENTAS. Las acciones (almacenadas en la columna TIPO) puede ser 'I' para insertar una cuenta nueva, o 'A' para actualizar una cuenta existente. Por ejemplo, si una fila en ACCION se lee 6, 'I', 2099; debe crearse la cuenta 6 con $2,099 como balance inicial. Si en esta fila de muestra hubiera habido una 'A' en lugar de una 'I', se tendra que actualizar la cuenta 6, y colocar el nuevo balance en $2,099 Escriba un bloque PL/SQL que tome cada fila de ACCION (ayuda: aqu se puede usar un cursor) y ejecute las acciones especificadas. Si se intenta insertar una fila que ya exista, actualcela. La excepcin interna DUP_VAL_ON_INDEX se disparar, de manera que su bloque debe incluir un manejador que efecte la actualizacin. Si se intenta actualizar una fila que no exista, insrtela (Cmo se sabe que no se actualiz ninguna fila?)
TABLA CUENTAS *INDICE UNICO* NUMBER(4) NUM_CUENTA ----------1 2 3 4 5 NUMBER(11,2) BALANCE -----------1000 2000 1500 6500 500 TABLA ACCION NUMBER(4) NUM_CUENTA ---------3 6 7 1 CHAR(1) TIPO -------A I A I NUMBER(11,2) VALOR_NUEVO ------------

31

LABORATORIO OPCIONAL CAPITULO 6 Pida al usuario un nmero de departamento, y empleando un cursor, obtenga el sueldo, comisin y nombre de cada empleado del departamento. Esta informacin se inserta en las columnas COL1, COL2 y MENSAJE de la tabla TEMP usada en la segunda parte del laboratorio del captulo 4. Sin embargo, si un empleado de ese departamento tiene comisin nula, dispare una excepcin definida por el usuario. En el manejador deshaga primero todos los INSERTs hechos. Luego cierre y reabra el cursor, insertando esta vez nicamente el sueldo y el nombre de cada empleado en las columnas COL1 y MENSAJE de la tabla TEMP.

32