You are on page 1of 69

CASE

DESARROLLO DE APLICACIONES INFORMTICAS EN LEGUAJES DE CUARTA GENERACIN CON HERRAMIENTAS CASE.

DeMoN

BLOQUE II

T.7. LENGUAJE PL/SQL


1. INTRODUCCIN HISTRICA. 2. ESTRUCTURA DE UN PROGRAMA PL/SQL: BLOQUES 3. CARACTERSTICAS GENERALES DEL LENGUAJE PL/SQL 4. VARIABLES PL/SQL 5. ESTRUCTURAS DE CONTROL 6. TIPOS COMPUESTOS DE DATOS: REGISTROS 7. TABLAS O ARRAYS. 8. YTILIZACIN DE SENTENCIAS SQL EN PL/SQL 9. CURSORES 10. SUBPROGRAMAS: PROCEDIMIENTOS Y FUNCIONES. 11. PAQUETES 12. DISPARADORES O TRIGGERS DE LA BD 13. TRATAMIENTO DE ERRORES 1. INTRODUCCIN HISTRICA
En PL/SQL se pueden escribir sentencias SQL, que se encargan de acceder a los datos y que estn intercaladas con las sentencias de control propias de un lenguaje de 3G. En este caso se dice que SQL est embebido en un lenguaje de programacin al que se le llama lenguaje anfitrin. El lenguaje anfitrin puede ser cualquiera que est preparado por el fabricante para contener SQL como C, ADA, JAVA, PL/SQL... PL/SQL significa PROCEDURAL LANGUAJE/SQL. Es prodedimental, creado por ORACLE , que combina SQL con instrucciones y caractersticas procedimentales de un lenguaje de 3G, como son: Variables y tipos de datos. Estructuras de control. Subprogramas: funciones y procedimientos.

PL/SQL est basado en lenguaje ADA.

2. ESTRUCTURA DE UN PROGRAMA PL/SQL: BLOQUES.


La unidad bsica en PL/SQL es el bloque. Todo programa est compuesto por bloques, que pueden estar situados de manera secuencial o anidados. Un bloque tiene 3 secciones: [DECLARE /*seccin declarativa*/ ] BEGIN /*seccin de instrucciones*/ ] [EXCEPTION /*seccin de excepciones*/ ] END; La seccin DECLARE es donde se declaran las variables, tipos de datos definidos por el usuario y cursores usados por el bloque. En ella tambin se pueden declarar funciones y procedimientos locales. La seccin BEGIN es donde aparecen las sentencias procedimentales de PL/SQL y las sentencias de SQL embebidas. La seccin EXCEPTION se ejecuta si se produce una excepcin o un error y se utiliza para detectar y gestionar los errores y excepciones.

Hay varios tipos de bloques: Bloques no almacenados en la BD: Son bloques que se escriben para ser ejecutados una sola vez y no se almacenan como objetos en la BD. Existen dos formas de escribirlos: Bloques annimos: no se identifican por ningn nombre. Bloques nominados: bloques identificados mediante una etiqueta.

Bloques almacenados en la BD: Son bloques de cdigo que se almacenan en la BD como objetos de la BD, al igual que otros objetos como tablas, usuarios, roles... Se construyen para ser ejecutados mltiples veces. Existen dos tipos:

Subprogramas: funciones, procedimientos o paquetes. Han de ser llamados de forma explcita para ser ejecutados, mediante una llamada. Disparadores o Triggers: que se ejecutan de una manera implcita cada vez que acontece el suceso o disparo asociado al trigger. Un suceso de disparo es una sentencia DML de SQL que modifica la BD (insert, update...).

3. CARACTERSTICAS GENERALES DEL LENGUAJE PL/SQL


- PLSQL no distingue entre maysculas y minsculas. - Los identificadores que dan nombre a los objetos PL/SQL como variables, tipos de datos, cursores, subprogramas, subtipos, paquetes, etc. han de empezar con una letra seguido de letras, digitos y los caracteres $,_,# hasta 30 caracteres de longitud mxima (si se encierra entre comillas dobles el identificador se pueden utilizar palabras reservadas, caracteres no permitidos y diferenciar entre maysculas y minsculas: BEGIN o Una variable o x*y. - Constantes de 3 tipos: . Cadena de Caracteres entre comillas simples: ASI o Administracion de Sistemas . Numricos: 10 o 12.93 . Booleanas: TRUE, FALSE o NULL - Comentarios de 2 tipos: . Comentarios monolnea: comienzan x dos guiones y contina hasta el final de la lnea: BEGIN V_Codrama char(3):=COI: --esto es un comentario monolinea . Comentarios multilnea: estn delimitados por /* y */: /* esto es un comentario multilnea */ - Todas las sentencias finalizan con ;

Operadores:
Los operadores del lenguaje PLSQL son: - Operador de asignacin: := - Operador de concatenacin: || - Operadores relacionales: >,>=,<,<=,= y != - Operador IS NULL devuelve TRUE si el valor de la variable es nulo: IF v_salida IS NULL THEN DBMS_OUT....PUT_LINE (No hay nada para escribir); - Operador LIKE compara cadenas de caracteres con patrones: IF v_salida LIKE %INFORMATIC% THEN DBMS_OUT.... (La nueva rama es del ciclo de informatica); - Operador BETWEEN permite combinar los operadores <= y >= en una sola expresin: IF v_salar BETWEEN 1000 AND 1500 THEN DBMS_OUT... (El salario no sta mal);

Operador IN devuelve TRUE si el valor est contenido en la lista de constantes entre parntesis: IF v_codrama IN (DAI, ASI) THEN DBMS_OUTPUT_PUT_LINE (Es un ciclo de Informatica); Operadores lgicos AND, OR y NOT Operadores aritmticos +, -, / y *

4. VARIABLES PL/SQL
4.1 DECLARACIN DE VARIABLES Se han de declarar las variables en la seccin declarativa de un bloque. La sintaxis para declara una variable es: nombre.variable [CONSTANT] tipo [NOT NULL] [{:=|DEFAULT}valor]; Donde con la clausula CONSTANT el valor inicial no podra ser modificado por lo que se convierte en un dato constante con nombre: NUNMAX CONSTANT NUMBER:=80; Con la clausula NOT NULL es obligatorio inicializar la variable y no se le podra asignar el valor NULL,para incializar una variable se utiliza := o DEFAULT. Ambos tienen idntico comportamiento de inicializar la variable,si al declarar una varibale no se inicializa,se le asigna el valor NULL. Solo se puede declarar una variable por linea,as,es erroneo poner: v_var1, v_var2 number;--esto es una declaracin errnea ejem: DECLARE v_codigo number := 100; v_denominacin char(50) DEFAULT SEDE CENTRAL; PI Constant number:=3.1416; v_respuesta char NOT NULL:=S; 4.2 TIPOS DE DATOS Los tipos de datos determinan los valores q pueden contener una variable. Hay tres categoras de tipos de datos: - Escalares: son datos simples, es decir, q no tienen componentes (CHAR, FLOAT, etc) - Compuestos: tipo RECORD y tipo TABLE. - Referencias como los punteros de C. Los tipos de datos disponibles en PL/SQL se definen en un paquete llamado STANDARD que se encuentr en C: \ORACLE\ORA81\RDBMS\ADMIN\STANDARD.SQL Los tipos de datos disponibles en PL/SQL son los mismos que pueden utilizarse en una columna de la Base de Datos ms algunos tipos adicionales - NUMBER (p,s)

BINARY_INTEGER es un tipo para datos numricos que se almacena en binario con complemento A2,no se utiliza para datos que vayan a ser almacenados en una columna de una tabla de la base de datos,se suele usar como ndice de arrays,su rango es:+2.147.483.647 VARCHAR2(n):Para cadenas de longitud variable,su longitud mxima es:32.767 bytes CHAR(n):Para cadenas de lo0ngitud fija,su longtud mxima es de:32.767 B LONG:Es una cadena de longitud variable,su longitud mxima es de 32.760 B (El tipo de dato LONG de base de datos es de cuatro GB,asi que no se uede asignar una columna de la base de datos de tipo LONG a una varibale de tipo SQL de tipo LONG DATE:Contien fechas,incluyendo ao,mes,dia,hora,minutos y segundo BOOLEAN:Puede tener los valores TRUE,FALSE,NULL ROWID:Almacena identificadores unvocos de filas de la base de datos,cada fila de una tabla tiene un identificador nico en toda la base de datos,este es su ROWID,que puede ser consultado en una sentencia SELECT usando la pseudocolumna ROWID.

4.3. ATRIBUTO % TYPE Para declarar una variable de un tipo determinado se puede indicar que tome el mismo tipo que una columna de una tabla de la base de datos o de otra varibale del programa Mediante el atributo % TYPEl.El atributo %TYPE cuando se aplica sobre una columna o una variable devuelve su tipo de dato. 4.4 SUBTIPOS Es un nombre alternativo para un tipo de dato,y que ademas puede restringir el conjunto de valores del tipo original.PLSQL define varios subtipos en el paquete estandar.La sintaxis para definir los subtipos es:SUBTYPE tipo_contador is number; v_i1 tipo_contador; 4.5 ATRIBUTO Y VISIBILIDAD DE LAS VARIOABLES El ambito de una variable es la parte del programa en la que se puede acceder a una variable.El ambito de una variable PLSQL es desde que se declara hasta el final del bloque en que se declara.La visibilad de una variable es la parte del programa donde se puede acceder a la variable sin necesidad de calificar.Si una variable esta declarada en un bloque y otra variable con el mismo nombre se declara en un bloque anidado para referirse desde el bloque anodado a la variable mas externa habra que calificarla con la etiqueta del bloque donde se declaro la variable y un punto.

5. ESTRUCTURAS DE CONTROL
5.1 ESTRUCTURA IF THEN-ELSE. Alternativa Simple: If condicion THEN Instrucciones; END IF; Alternativa doble: If condicion THEN Instrucciones; ELSE Instrucciones; END IF; Alternativa multiple: IF condicion THEN Instrucciones; ELSIF condicin THEN Instruccin; ELSIF condicin THEN Instruccin ... [ELSE instruccin;] END IF; 5.2 REPETITIVA: BUCLE SIMPLE SINTAXIS: LOOP Sentencia de ordenes; [EXIT [WHEN condicin]] sentencia de ordenes; END LOOP; La sentencia EXIT WHEN permite salir del bucle cuando se cumpla la condicin, puede aparecer en cualquier sitio dentro de la estructura repetitiva e incluso no aparecer. Ejerc: Crear una tabla llamada TEMP con una columna llamada NUM tipo NUMBER e insertarle los numeros naturales del 1 al 50;

1 2 3 4 5 6 7 8 9

DECLARE v_var number(2):=0; BEGIN LOOP v_var:=v_var+1; INSERT INTO TEMP VALUES(v_var); EXIT WHEN v_var=50; END LOOP; END; 5.3 REPETITIVA BUCLE WHILE

Sintaxis: WHILE condicion LOOP Secuencia de ordenes; END LOOP; La condicin se evala antes de cada interaccin del bucles. La secuencia de rdenes se ejecuta mientras la condicin es verdad, si es falsa o nula finaliza la ejecucin del bucle. Se puede utilizar la orden EXIT o EXIT WHEN xa salir prematuramente del bucle. 5.4 REPETITIVA: BUCLE FOR Son bucles en los que el numero de iteraciones de la repetitiva se conoce de antemano,su sintaxis es: FOR contador_bucle IN [REVERSE] limiteinferior..limitesuperior LOOP Secuencia_de_rdenes; END LOOP; Donde contador bucle es una variable numerico entera que se declara de forma implcita No hay que declararla xo si se declara al llegar al bucle se declara otra variable con el mismo nombre pero local al bucle. Lmite inferior es el valor inicial de la variable contador Lmite superior es el valor final de la variable contador. En cada iteracin la variable contador_bucle se implementa en 1,si se especifica la clausula REVERSE la variable contador bucle toma como valor inicial el lmite superior y se decrementa en cada iteracion en 1 hasta llegar al valor del lmite inferior. 1 2 3 4 5 BEGIN FOR v_contador IN 1..50 LOOP INSERT INTO TEMP VALUES(C_CONTADOR); END LOOP`; END;

5.5 SENTENCIA GOTO Es una bifurcacin incondicional a otra parte del cdigo. Sintaxis: GOTO etiqueta; La etiqueta indica el lugar del cdigo que tiene esa etiqueta y al que se ha de bifurcar la ejecucin del programa. Las etiquetas colocadas en el cdigo han de estar encerradas entre corchete s angulares dobles: <<etiqueta>> xo para referirse a la etiqueta desde una instruccin, x ejemplo, GOTO EXIT no se aaden los corchetes dobles. Es ilegal realizar un salto al interior de un bucle, al interior de una sentencia IF y tambin es ilegal ir desde la seccin de excepciones a la seccin procedimental. Ejem: DECLARE v_acumulador number(2):=0; CTE_LIMITE constant number(2):=50; BEGIN LOOP v_acumulador:=v_acumulador+1; insert into temp values(v_acumulador); IF v_acumulador=CTE_LIMITE THEN GOTO despues_del_bucle; END IF; END LOOP; <<despues_del_bucle>> END; 5.6 SENTENCIA EXIT Hace que finalice un bucle antes de que se cumpla su condicin de salida. Sintaxis: EXIT [etiqueta] [WHEN condicion]; Un bucle puede estar etiquetado antes de su inicio, esto permite a la sentecia EXIT indicar cual es el bucle del que se quiere salir. BEGIN <<bucle_externo>> FOR idx_i IN 1..50 LOOP ......... <<bucle_interno>> FOR cdx_j IN 1..100 LOOP ... 10

... EXIT bucle_externo WHEN condicion; END LOOP; ..... .... END LOOP; .... .... END;

5.7 SENTENCIA NULL Indica que no realice ninguna accin. Ejem: IF condicion THEN NULL; ELSE Condicion; END IF; 5.8 ESTILO DE IDENTIFICADORES DE OBJETOS DE UN PROGRAMA PL/SQL Para clarificar la funcin que realizan las variables que aparecen en un programa y distinguirlas de las columnas de una tabla se sugieren los siguientes prefijos: v_nombrevariable -> variable de programa e_nombreexcepcin -> excepciones definidas por el usuario tipo_nombretipo -> tipos definidos por el usuario p_nombreparametro -> parametros de procedimientos y funciones c_nombrecursor -> cursores cte_nombreconstante -> constantes i_nombreindice -> indices de bucles FOR y arrays t_nombrearray -> arrays reg_nombregistro -> datos compuestos de tipo RECORD

11

6. TIPOS COMPUESTOS DE DATOS: REGISTROS


Son similares a las estructuras de C, es decir, es un tipo compuesto de varios campos de tipos ya definidos. 6.1 DECLARACIN DE REGISTRO: Al contrario q los tipos escalares q estn predefinidos en el lenguaje los tipos compuestos han de ser definidos por el usuario. Para poder utilizar una variable compuesta primero ha de definirse el tipo y luego declarar variables de ese tipo. La sintasix para definir un tipo de registro es la siguiente: TYPE nombre_tipo_registro IS RECORD( campo1 tipo1 [NOT NULL] [:=valor1], campo2 tipo2 [NOT NULL] [:=valor2], . . . campon tipon [NOT NULL] [:=valorn]); ejem: DECLARE TYPE REG_ALUMNO IS RECORD( dni char(10), nomalumno varchar2(65), codrama char(3), codgrupo char(2)); v_alumno1 REG_ALUMNO; v_alumno2 REG_ALUMNO; 6.2 REFERENCIA A UN CAMPO Para hacer referencia a un campo de una variable de tipo registro es con la sintasix: variable.campo. Ejem: BEGIN v_alumno1.dni:=10; v_alumno2.codrama:=DAI; Para poder asignar una variable de tipo registro a otra han de ser del mismo tipo de registro. Se hara de la siguiente manera: v_alumno1:=v_alumno2; nota: aunque fueran dos registros con los mismos campos no se pueden asignar variables registros de distintos tipos, aunque si se puede realizar la asignacin campo a campo.

12

Tambin se puede asignar los valores a una variables registro mediante una sentencia SELECT Ejem: select * INTO v_alumno2 from alumno where dni=380; DBMS_OUTPUT.PUT_LINE (v_alumno2.numalum no); END; 6.3 ATRIBUTO %ROWTYPE Para facilitar la declaracin de una variable de tipo registro con los mismos campos y tipos que una tabla de la base de datos se proporciona el atributo %ROWTYPE que devuelve un tipo de registro basandose en la definici n de una tabla: Ejem: DECLARE v_alumno alumno%ROWTYPE; BEGIN select * INTO v_alumno from alumno where dni=380; DBMS_OUTPUT.PUT_LINE (v_alumno.nomalumno); END;

7. TABLAS O ARRAYS (ver fotocopias). 8. UTILIZACIN DE SECUENCIAS SQL EN PL/SQL


Las nicas rdenes SQL permitidas en PL/SQL son las DML(INSERT, UPDATE, DELETE, SELECT) y las de control de transacciones DCL (COMMIT, ROLLSEEK). 8.1 VARIABLES DE ACOPLAMIENTO: En las expresiones de una sentencia DML se podr especificar variables de programa que al estar dentro de una sentencia SQL se llaman variables de acoplamiento. Ejem: DECLARE v_dni alumno.dni%TYPE; v_codigoasignatura asigna.codasigna%TYPE; v_nota estudia.notaf%TYPE; BEGIN v_dni:=380; v_codigoasignatura:=CASE; 13

v_nota:=6; update estudia s et nota1=v_nota where dni=v.dni AND codasigna=v_codigoasignatura; END; 8.2 SENTENCIA SELECT El formato de la sentencia SELECT en PL/SQL es: SELECT {*|columna[,columna]...} INTO {variable1[,variable2]...|variable.registro} FROM tabla1[,tabla2]... [WHERE predicado] [GROUP BY columna[,columna]...] [HAVING predicado] [ORDER BY columna[,columna]...]; La clausula INTO permite almacenar en variables de programa el resultado de una sentencia SELECT. Puede ser una variable de tipo registro o una lista de variables simples. La clausula WHERE ha de devolver una sola fila ya que el resultado se almacenar en variables, si devuelve varias filas la sentencia SELECT se produce el siguiente error: ORA-1427: Single-row query returns more than one row; Para recoger varias filas de una SELECT se ha de utilizar cursores. 8.3 SENTENCIA UPDATE Sentencia: UPDATE tabla SET { columna=expresion [,columna=expresion]|(columna[,columna]...)=(subconsulta)} WHERE {predicado|CURRENT OF nombre_cursor}] La sintasix CURRENT OF nombre_cursor se utiliza junto con la definicin de un cursor actualizable. 8.4 SENTENCIA DELETE Sintasix: DELETE [FROM] tabla [WHERE {predicado|CURRENT OF nombre_cursor}] 8.5 CONTROL DE TRANSACCIONES Una transaccin es una serie de rdenes SQL que se completan o fallan como una unidad y sirven para evitar la inconsistencia de los datos. En Oracle una transaccin comienza con la primera orden SQL emitida despus de terminar la transaccin anterior o con la primera orden SQL despus de iniciar una sesin.

14

8.5.1 COMMIT Y ROLLBACK Cuando se ejecuta una orden COMMIT termina la transaccin y suceden : 1 Se hacen permanentes los cambios realizados en la transaccin. 2 Otras sesiones pueden ver los cambios realizados por la transaccin. 3 Se liberan todos los bloques establecidos por la transaccin. Sintasix: COMMIT [WORK]; Cuando se ejecuta una orden ROLLBACK termina la transaccin y suceden 2 cosas: 1 Todo trabajo echo x la transaccin se deshace como si no se hubieran realizado cambios. 2 Se liberan todos los bloqueos establecidos por la transaccin. Sintasix: ROLLBACK [WORK]; Si una sesin se desconecta accidentalmente de la BASE de DATOS sin haber echo ni COMMIT ni ROLLBACK el sistema ejecuta automaticamente un ROLLBACK. SQL PLUS ejecuta automaticamente un ROLLBACK cuando el usuario abandona una sesin en SQL PLUS. 8.5.2 PUNTOS DE SALVAGUARDA (SAVEPOINT): La orden ROLLBACK deshace todas las modificaciones de una transaccin, xo si se utilizan puntos de salvaguarda puede deshacerse slo parte de las modificaciones de la transaccin. Para declarar un punto de salvaguarda se utiliza la siguiente sintasix: SAVEPOINT nombre_punto_salvaguarda; Una vez definido un punto de salvaguarda el programa puede deshacer parte de la transaccin utilizando la sintasix de la sentencia: ROLLBACK [WORK] TO [SAVEPOINT] nombre_punto_salvaguarda; 1 Cualquier cambio desde el punto de salvaguarda se deshace. 2 Se libera cualquier bloqueo establecido por la transaccin desde el punto de salvaguarda. 3 La transaccin no finaliza ya que hay rdenes SQL pendientes. Ejem: BEGIN INSERT INTO alumno VALUES (951,ANA,DAI,1A); /* se graba */ SAVEPOINT punto1; INSERT INTO alumno VALUES (952,CARMEN,DAI,2A); /* se graba */ SAVEPOINT punto2; INSERT INTO alumno VALUES (953,ERNESTO,DAI,1A); /* no se graba */ SAVEPOINT punto3; INSERT INTO alumno VALUES (954,OFELIA,DAI,1A); /* no se graba */ ROLLBACK TO SAVEPOINT punto2; INSERT INTO alumno VALUES (955,CARLOS,DAI,1A); /* se graba */ COMMIT; 15

END; Se suele utiliza SAVEPOINT antes de una parte compleja de una transaccin, as si esa parte de la transaccin falla se puede deshacer permitiendo que continue el efecto de los cambios previos al SAVEPOINT. Es posible iniciar una transaccin de slo lectura mediante la sentencia SET TRANSACTION READ ONLY; 8.6 COMPARACION DE CADENAS DE CARACTERES Existen dos tcnicas con relleno a blancos y sin relleno a blancos Con relleno a blancos:ABC es igual ABC Sin relleno a blancos:ABC es distintoABC DECLARE V_cadena1 char(4):= ANA; V_cadena2 varchar2(4):=ANA; BEGIN IF v_cadena1=v_cadena2 THEN DNMS_OUTPUT.PUT_LINE(LAS CADENAS SON IGUALES); ELSE DBMS_OUTPUT.PUT_LINE(LAS CADENAS SON DISTINTAS); END IF; END. PL/SQL utiliza la semntica con relleno a blancos slo cuando las dos cadenas a comparar son de longitud fija,es decir datos declarados de tipo char,o bien constantes alfanumricas entre comillas. Si alguna de las cadenas es de longitud variable entonces utiliza la semntica sin relleno a blancos.Para evitar problemas en la clausula WHERE de una sentencia SQL lo mas adecuado es declarar las variables PL/SQL del mismo tipo de las columnas a comparar utilizando el atributo %TYPE

9. CURSORES
Cuando se ejecuta una sentencia SELECT en un programa PL/SQL el resultado de la consulta ha de ser recogido en variables de programa; sin embargo si la sentencia SELECT devuelve varias filas el resultado ha de ser gestionado por cursores. Cuando se procesa una orden SQL Oracle asigna un rea de memoria que recibe el nombre de rea de Contexto que contiene el Conjunto Activo que es el conjunto de filas resultado de una consulta. Un cursor es un puntero al rea de Contexto que permite controlar los datos recuperados de la base de datos. Existen dos tipos de cursores: . Cursor explcito: que ha de ser creado por el programador y que se utiliza cuando una sentencia SELECT devuelve varias filas.

16

. Cursor implcito: que es creado y procesado automticamente por PL/SQL y lo crea con sentencia SELECT que devuelve una sola fila y con las sentencias INSERT, UPDATE y DELETE. 9.1. CURSOR EXPLCITO: Los pasos para procesar un cursor explcito son: 1. 2. 3. 4. Declaracin del cursor. Apertura del cursor. Extraccin de los datos del cursor y su almacenamiento en variables de programa. Cierre del cursor. 9.1.1. DECLARACIN DE CURSORES:

La declaracin de un cursor le da un nombre y lo asocia con una sentencia SELECT; su sintaxis: CURSOR nombre_cursor IS sentencia_select: La sentencia_select puede ser cualquier consulta que puede incluir JOINS y operadores de conjuntos como UNION MINUS. En la clausula WHERE se puede hacer referencia a variables previamente declaradas.

9.1.2. Sintaxis: OPEN nombre_cursor;

APERTURA DEL CURSOR

Donde nombre_cursor ha de ser un cursor previamente declarado. Cuando se abre un cursor suceden 2 cosas: 1. Se examinan los valores de las variables de programa acopladas en la sentencia SELECT y se determina el conjunto activo basandose en los valores de la variable de acoplamiento si stas existen, es decir, se ejecuta la sentencia SELECT almacenando los valores en el cursor. 2. Se hace apuntar el puntero del conjunto activo a la primera fila del cursor. La ejecucin de la sentencia SELECT asociada al cursor solo se ejecuta cuando se abre el cursor, de tal manera que si las variables acopladas cambian posteriormente de valor el conjunto activo no vara. Para poder variar el conjunto activo se tendra que cerrar y volver a abrir el cursor. Se puede abrir un cursor que ya est abierto ya que el sistema lo cierra antes de volver a abrirlo. Ejemplos CURSOR

17

9.1.3.

EXTRACCIN DE LOS DATOS DEL CURSOR.

Para acceder a los datos de un cursor se utiliza la sentencia FETCH cuya sintaxis es: FETCH nombre_cursor INTO {variable1[,variable2]...|variable_registro}; donde nombre_cursor es un cursor previamente declarado y abierto; la clausula INTO almacena la fila activa del cursor en variables simples o en una variable de tipo registro; en cualquier caso ha de ser compatible con los datos extraidos del cursor en cuanto al nmero y tipo. Despus de la ejecucin de una sentencia FETCH se incrementa el puntero activo del cursor que apuntara a la siguiente fila. La sentencia FETCH se suele ejecutar dentro de una sentencia de control repetitiva y de esta forma cada ejecucin de una sentencia FETCH devolver filas sucesivas del conjunto activo hasta que se extraiga el conjunto completo, por tanto, la sentencia FETCH dentro de una repetitiva permite realizar un recorido secuencial del cursor. Los atributos de cursor %FOUND y %NOTFOUND se utilizan para detectar cuando se ha terminado de extraer el conjunto activo. La ltima orden FETCH no realizara asignacin a la variable de programa, asique estas varialbes contendrn los valores de la ltima fila extraida del conjunto. Ejemplos CURSOR 9.1.4. CIERRE DEL CURSOR

Cuando se ha terminado de extraer el conjunto activo debe cerrarse el cursor y se liberan los recursos utilizados. Sintasix: CLOSE nombre_cursor; No se pueden extraer datos de un cursor cerrado y no se puede cerrar un cursor ya cerrado. 9.1.5. ATRIBUTOS DEL CURSOR

Los atributos de un cursor devuelven valores utilizados en expresiones. %FOUND: Devuelve TRUE si la ltima sentencia FETCH ejecutada devolvi una fila y FALSE en caso contrario, es decir, si ya se han extraido todas las filas del cursor. %NOTFOUND: Devuelve TRUE si la ltima sentencia FETCH no devolvi ninguna fila y FALSE en caso contrario. %ISOPEN: Sirve para determinar si un cursor est abierto o cerrado. Devuelve TRUE si est abierto y FALSE en caso contrario. %ROWCOUNT: Devuelve el nmero de filas extraidas del cursor hasta el momento.

18

9.1.6.

BUCLES DE EXTRACCIN DE UN CURSOR:

La operacin ms comn con un cursor es extraer todas las filas del conjunto activo. Para ello se utiliza un bucle de extraccin que procesa una a una todas las filas del conjunto activo dependiendo del tipo de bucle utilizado se tendrn las siguientes implementaciones: . Bucle Simple: DECLARE CURSOR c_alumno IS Select * from alumno i Where codrama=DAI v_alumno alumno%ROWTYPE; BEGIN OPEN c_alumno; LOOP FETCH c_alumno AND v_alumno; EXIT WHEN c_alumno%NOTFOUND; --Acciones DBMS... (v_alumno.nomalumno); END LOOP; CLOSE c_alumno; END; .Bucle WHILE: Parte declarativa igual. BEGIN OPEN c_alumno; FETCH c_alumno INTO v_alumno; WHILE c_alumno%FOUND LOOP --Acciones DBMS___(v_alumno.nomalumno); FETCH c_alumno INTO v_alumno; END LOOP; CLOSE c_alumno; END; BUCLE DE CURSOR FOR: El bucle de cursor FOR es un bucle especial para cursores que realiza su procesamiento de modo implcito; su sintaxis es la siguiente: FOR variable_registro IN nombre_cursor LOOP --Acciones a realizar con la fila extraida. END LOOP; Donde la variable registro no tiene que declararse en la seccin DECLARE sino que la declara implcitamente la propia sentencia FOR; el tipo de esta variable ser: variable_registro nombre_cursor%ROWTYPE; Nota: no se pueden asignar valores a la variable dentro del bucle. 19

El bucle FOR, cuando comienza su ejecucin abre el cursor, extrae una fila del cursor en cada iteracin depositndola en la variable registro, y cierra el cursor cuando acaba de leer el conjunto activo. DECLARE Cursor c_alumno IS SELECT * FROM alumno WHERE codrama=DAI; v_alumno alumno%ROWTYPE; BEGIN FOR v_alumno IN c_alumno LOOP DBMS_OUTPUT.PUT_LINE(v_alumno.nomalumno); END LOOP; END; 9.1.7. CURSORES PARAMETIZADOS. Existe otro mtodo para utilizar variables de acoplamiento en los cursores, mediante los cursores parametizados, que admiten argumentos al igual que los procedimientos y las funciones. La sentencia OPEN es la que se utiliza para pasar los valores al cursor. >DECLARE CURSOR c_alum(p_codrama rama.codrama%TYPE) IS SELECT nomalumno FROM alumno WHERE codrama=p_codrama; v_alum c_alum%ROWTYPE; BEGIN OPEN c_alum(DAI); FETCH c_alum INTO v_alum; WHILE c_alum%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_alum.nomalumno); FETCH c_alum INTO v_alum; END LOOP; CLOSE c_alum; END; 9.2. CURSORES FOR UPDATE. Si se quieren modificar las filas extradas por un cursor se ha de declarar un cursor FOR UPDATE. La forma de utilizar estos cursores es especificando la clusula FOR UPDATE en la declaracin del cursor, y la clusula WHERE CURRENT OF nombre_cursor en las sentencias UPDATE y DELETE.

20

9.2.1. CLASULA FOR UPDATE. La clusula FOR UPDATE es parte de la sentencia SELECT del cursor y se especifica despus de todas las clusulas de la sentencia SELECT, es decir despus de la clusula ORDER BY si esta existe. Su sintaxis es: CURSOR nombre_cursor IS SELECT ---------------- FROM -----------------WHERE ---------------- ORDER columna1[,columna2]...] [NOWAIT ];

BY

-----------

FOR

UPDATE[OF

La clusula OF indica las columnas del cursor que pueden ser modificadas. Si no se especifica la clusula, todas las columnas podrn ser modificadas. Si la consulta del cursor hace referencia a varias tablas mediante un JOIN entonces es obligatorio especificar la clusula OF con las columnas que se quieren modificar. Cuando se abre un cursor declarado FOR UPDATE las filas que forman el conjunto activo son bloqueadas para que no puedan acceder otras sesiones a esos datos y controlar el acceso concurrente a los datos. La clusula NOWAIT hace que si las filas estn bloqueadas por otra sesin, la sentencia OPEN termine inmediatamente devolviendo el siguiente error: ORA-54: Resource busy and acquire with NOWAIT specified. 9.2.2. WHERE CURRENT OF Se utiliza en UPDATE y DELETE cuando se declara un cursor FOR UPDATE. Su sintaxis es. WHERE CURRENT OF nombre_cursor; La clusula WHERE CURRENT OF hace referencia a la fila recien extrada por el cursor. 9.3. CURSORES IMPLCITOS. Todas las rdenes SQL se jecutan dentro de un area de contexto que tiene un cursor asociado.El cursor implcito sirve para procesar las rdenes SELECT ........INTO INSERT UPDATE DELETE

que devuelva una fila

No se puede incluir un COMMIT dentro de un bucle de extraccin ya que liberara los bloqueos establecidos por la sesin como consecuencia de la clusula FOR UPDATE y el cursor quedara invalidado.

21

Los cursores implcitos se llaman SQL y son declarados,abiertos,procesados y cerrados por el propio SQL.No tiene sentido aplicar la sentencia OPEN FETCH CLOSE,pero si tiene sentido utilizar los atributos de cursor. LA SENTENCIA SELECT INTO cuando recibe varias filas produce un error(TOO_MANY_ROWS) o si no devuelve ninguna fila(NO_DATA_FOUND) Y por tanto la ejecucion del programa se bifurca a la seccion de secciones del bloque,por lo que no se puede comprobar el valor del atributo SQL %NOT FOUND DECLARE v_nombre alumno.nomalumno%TYPE BEGIN Select nomalumno INTO v_nombre from alumno Where dni=999; IF SQL%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE(Noexiste ningun alumno con esedni:SQL%NOTFOUND); END IF; DBMS_OUTPUT.PUT_LINE(FIN DE PROGRAMA); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(No existe ningun alumno con ese dni:NO_DATA_FOUND); End, Una vez que el programa se bifurca a la seccion de secciones no se puede regresar a la secci n procedimental y por tanto acaba el programa, por tanto, PARA HACER CONSULTAS QUE DEVUELVAN UNA FILA SI NO SE TIENE LA CERTEZA DE QUE LA CONSULTA NO VA A SER VACIA Y EN EL CASO DE QUE SEA VACA SE QUIEREN REALIZAR OTRAS SENTENCIAS PROCEDIMENTALES Y CONTINUAR EL PROGRAMA, ENTONCES SE HA DE UTILIZAR UN CURSOR EXPLICITO NOTA(NUNCA UTILIZAR SENTANCIAS SELECT INTO Y EN SU LUGAR USAR UN SECTOR EXPLICITO) SERIA CON CURSOR EXPLICITO DECLARE CURSOR c_alum IS Select nomalumno from alumno Where dni=999; V_nombre alumno.nomalumn%TYPE; BEGIN OPEN c_alum; FETCH c_alumno INTO v_nombre; IF c_alum%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE(No existe ningun alumno con ese dni); END IF; --resto de acciones procedimentales; DBMS_OUTPUT.PUT_LINE(FIN DE PROGRAMA); END, 22

10. SUBPROGRAMAS: PROCEDIMIENTOS Y FUNCIONES.


Los procedimientos en PL/SQL son similares a los de los lenguajes de 3 Generacin, es decir, son un conjunto de sentencias a las que se le ha asignado un nombre y pueden ser llamadas desde otros puntos del programa pasndoles unos valores llamados argumentos. >CREATE OR REPLACE PROCEDURE aadiralumno( p_dni alumno.dni%TYPE, p_nombre alumno.nomalumno%TYPE, p_codrama alumno.codrama%TYPE, p_codgrupo alumno.codgrupo%TYPE) IS BEGIN INSERT INTO alumno values (p_dni,p_nombre,p_codrama,p_codgrupo); COMMIT; END aadiralumno; Al ejecutar este cdigo se compila y el cdigo compilado se almacena como un objeto en la BD. Lo podemos comprobar consultando la vista del diccionario de datos ALL,DBA,USER_OBJECTS. Para ver los errores de compilacin se utiliza el comando >Show errors; Para ser ejecutado este procedimiento ha de ser invocado de manera explcita dando el nombre del procedimiento y especificando los argumentos. 10.1. CREACIN DE UN PROCEDIMIENTO. La sintaxis para crear un procedimiento es: > CREATE [OR REPLACE] PROCEDURE nombre_procedimiento [(parmetro1[{IN | OUT| IN OUT}] tipo_dato [{:= | DEFAULT}valor_inicial], ... parametroN [{IN | OUT| IN OUT}] tipo_dato [{:= | DEFAULT}valor_inicial], {IS | AS} [/*seccin declarativa*/] BEGIN [/*seccin procedimental*/] [EXCEPTION /*seccin de excepciones*/] END [nombre_parametro]; donde nombre_procedimiento es el nombre con el que se almacena en la BD y con el que ser invocado. Entre parntesis se indica la lista de parmetros especificando sus tipos de datos. Si el procedimiento no tuviese parmetros no se especifican parntesis ni en la declaracin del procedimiento ni en la llamada al procedimiento. En la declaracin del procedimiento se indica para cada parmetro formal su tipo de dato , que no puede ser restringido, es decir, puede ser char pero no puede ser char(3) , as que sus longitudes vendrn dadas por los tipos de los argumentos que 23

llaman al procedimiento. La nica forma de restringir los valores de los parmetro formales es utilizando el atributo %TYPE y basarse en una columna de una tabla de la BD. IS o AS indistintamente indican el inicio del cuerpo del procedimiento. La seccin declarativa es opcional y se encuentra entre las palabras IS | AS y BEGIN, y en ella se declaran las variables locales del procedimiento. La seccin procedimental se encuentra entre las palabras BEGIN y EXCEPTION. La seccin de excepciones es opcional y se encuentra entre las palabras EXCEPTION y END. Para modificar el cdigo de un procedimiento hay que borrarlo de la BD y volverlo a crear, o bien utilizar la la opcin OR REPLACE que lo hace automticamente. El cdigo de los subprogramas est almacenado en la vista USER_SOURCE en la columna TEXT. 10.2. LLAMADA A UN PROCEDIMIENTO Y PASO DE PARMETROS. La llamada a un procedimiento puede realizarse desde cualquier otro bloque PL/SQL indicando el nombre del procedimiento y entre parntesis los valores que se van a pasar al procedimiento. Estos valores se denominan argumentos y las variables que reciben estos valores dentro del procedimiento se denominan parmetros formales . 10.2.1. Modo de paso de parmetros: En la creacin del procedimiento se indica para cada procedimiento el modo en que se pasar el argumento, por defecto es IN. IN: hace que el parmetro formal sea slo de lectura, y su valor no puede ser modificado dentro del procedimiento. Aparece siempre en la parte derecha de una expresin de asignacin. Es un paso de parmetros por valor con la singularidad de que el parmetro no puede ser modificado en el procedimiento. OUT: hace que el parmetro formal sea solo de escritura, por lo que no puede ser ledo y slo puede asignrsele valor. Aparece siempre en la parte izquierda de una expresin de asignacin. Es un paso por referencia con la singularidad de que no puede ser ledo el valor dentro del procedimiento. IN OUT: hace que el parmetro formal pueda ser ledo y escrito, puede aparecer en la parte derecha y en la parte izquierda de una expresin de asignacin, es decir, es un paso por referencia.

Al finalizar la ejecucin del procedimiento, aquellos parmetros definidos como OUT o IN OUT se les asigna su valor en los argumentos asociados en la llamada al subprograma, mientras que los parmetros definidos como IN no alteran el valor del argumento asociado en la llamada al subprograma. El argumento asociado a un parmetro IN puede ser una variable o una constante. El argumento asociado a un parmetro OUT o IN OUT ha de ser obligatoriamente una variable.

24

10.2.2. Paso de parmetros posicional y nominal. Cuando se llama a un procedimie nto los valores de los argumentos se asignan a los parmetros formales mediante dos posibles mecanismos: Paso de parmetros posicional: donde el valor del primer argumento se pasa al primer parmetro formal de la declaracin del procedimiento, el valor de l segundo argumento al segundo parmetro formal, y as sucesivamente. Paso de parmetro nominal: donde en la llamada al subprograma se indica el nombre del parmetro formal y el valor del argumento que se pasar en la llamada. Esto permite pasar los argume ntos en distinto orden en que estn declarados los parmetros formales.

E.j.:

DECLARE v_nombre alumno.nomalumno%TYPE:=PEREZ SALVADOR, GABRIEL; BEGIN aadiralumno(p_codrama=>DAI, p_dni=>912,p_nombre=>v_nombre, p_codgrupo=>2A'); END;

En una misma llamada a un procedimiento se puede mezclar el paso de parmetros nominal y posicional. En este caso los primeros argumentos han de ser pasado de forma posicional y los ltimos de forma nominal. E.j.: DECLARE v_nombre alumno.nomalumno%TYPE:=PEREZ SALVADOR, GABRIEL; BEGIN aadiralumno(p_codrama=>DAI, p_dni=>912,p_nombre=>v_nombre, p_codgrupo=>2A'); aadiralumno(913,JIMENEZ GARCIA, RAFAEL, p_codgrupo=>1A,p_codrama=>DAI); END; 10.2.3. Valores predeterminados de los parmetros. Los parmetros formales pueden tener valores predeterminados, por lo que si no reciben valores de un argumento en una llamada toman el valor predeterminado. Si se usa el paso de parmetros posicional todos los parmetros predeterminados para los que no hay argumento asociado han de estar al final de la lista de parmetros formales. CREATE OR REPLACE PROCEDURE aadiralumno( p_dni alumno.dni%TYPE, p_nombre alumno.nomalumno%TYPE, p_codrama alumno.codrama%TYPE DEFAULT DAI, p_codgrupo alumno.codgrupo%TYPE DEFAULT 1A') IS BEGIN INSERT INTO alumno values (p_dni,p_nombre,p_codrama,p_codgrupo); COMMIT; END aadiralumno; 25

Si el parmetro con valor por defecto no es el ltimo(s) de la lista, entonces se necesita un paso de parmetros nominal. 10.3. Creacin de funciones: Las funciones son subprogramas como los procedimientos y tienen las mismas caractersticas. Slo se diferencian en que las funciones devuelven un valor y los procedimientos no devuelven valores, por tanto, la llamada a una funcin formar parte de una expresin mientras que la llamada a un procedimiento es una sentencia por si misma. La sintaxis para declarar una funcin es la siguiente:

CREATE [OR REPLACE] FUNCTION nombre_funcion [(parametro1[{IN|OUT|IN OUT}] tipo_dato[{:=|DEFAULT}valor_inicial], . . . parametroN[{IN|OUT|IN OUT}] tipo_dato[{:=|DEFAULT}valor_inicial])] RETURN tipo_dato_retorno {IS|AS} [/*seccion declarativa*/] BEGIN /*seccion procedimental*/ [EXCEPTION /*seccion de excepciones*/] END [nombre_funcion];

11. PAQUETES.
Es un bloque PLSQL nominado que permite almacenar juntos una serie de objetos relacionados,tales como un conjunto de procedimientos y funciones,tipos de datos,variables,etc. que tengan que ver con una tarea concreta. Un paquete se compone de tres partes:La especificacion o cabecera y el cuerpo Cada una de estas partes se almacena por separado en el diccionario de datos. 11.1. Especificacin de un paquete La especificacion o cabecera de un paquete contiene informacion acerca del contenido del paquete pero no contiene el codigo de los subprogramas,en la cabecera aparecen las especificaciones formales de procediminetos y funciones,declaraciones de variable,de cursor,de tipos de dato y de excepciones. La especificacion de un subprograma consiste en especificar el tipo:PROCEDURE O FUNCTION,el nombre del subprograma,los parmetros formales entre parntesis en cada uno indicando el nombre,modo y tipo y en el caso de ser una funcion el tipo de valor que devuelve. SINTAXIS {PROCEDURE|FUNCTION

26

Las variables,tipos de datos de funciones definidas en la cabecera de un paquete son visibles desde cualquier otro bloque PLSQL,pues lo que la forma de disponer de variables globales es declarndolas en la cabecera de un paquete. La sintaxis para la cracin de la cabecera de un paquete es la siguiente: CREATE [OR REPLACE] PACKAGE nombre_paquete {IS|AS} {Especificacion de funcion| Especificacion de procedimiento| Declaracion_variable| Definicion de bajo| Decllaracion de excepcion| Declaracion de cursor}... End [nombre_paquete] 11.2. Cuerpo de paquete El cuerpo de un paquete contiene el cdigo de los procedimientos y funciones especificados en la cabecera de un paquete.El cuerpo de un paquete es opcional,pues si en la cabecera de un paquete no hay declaraciones formales de procedimeintos ni de funciones,es deci slo hay declaraciones de variables,cursores y tipos de datos,entonces no es necesario que el cuerpo este presente.En el cuerpo del paquete las especificaciones de los procediminos y funciones tiene que ser la misma que en la cabecera del paquete,coincidiendo el nombre del paquete,los nombres de sus parmetros,sus tipos y sus modos . El cuerpo del paquete no puede ser compilado a menos que haya sido compilado previamente la cabecera del paquete. La sintaxis para el cuerpo de un paquete es la siguiente: CREATE [OR REPLACE]PACKAGE BODY nombre_paquete{IS|AS} {FUNCTION nombre_funcion [(parmetro1[modo]tipo[,parmetro2[modo]tipo...)]RETURN tipo_dato_retorno{IS|AS} [/*declariaciones locales*/] BEGIN /*seccion procedimental*/ [EXCEPTION /*seccio n excepciones*/] END [nombre_funcion]; PRECODURE nombre_procedimiento [(parametro1[modo] tipo [, parametro2 [modo] tipo]...)] {IS|AS} [*/declaracion procedimental*/] BEGIN /*seccion procedimental*/ [EXCEPTION /*seccion excepciones*/] END [nombre_procedimie nto]; [BEGIN /*seccion de inicializacin del paquete*/] END [nombre_paquete]; 27

Ejem: (3 versiones) Construir un paquete llamado CentroPaquete que realice las siguientes tareas: 1. Procedimiento llamado AadirCentro. 2. Funcin BorrarCentro que recibe un cdigo de centro y lo borra de la tabla centro. Esta funcin devuelve 0 si se ha borrado el centro y 1 si el centro no existe. 3. Otra funcin que se llama CuantosCentrosAlta que devuelve el nmero de centros que se han dado de alta en la sesin. 4. Funcion CuantosCentrosBaja, que devuelve cuantos centros se han dado de baja en la sesin. 5. Funcin DiferenciaCentros que devuelve cuantos centros hay de diferencia desde el inicio de la sesin. 6. Procedimiento ListarDepartamentos que recibe un cdigo de centro y visualiza los nombres de los departamentos hubicados en ese centro. 7. Procedimiento ListarDepartamentos que recibe como parmetro un nombre de centro y visualiza los nombres de los departamentos ubicados en ese centro. CREATE OR REPLACE PACKAGE CentroPaquete IS PROCEDURE AadirCentro(p_codce dai2.centro.codce%TYPE, p_nomce dai2.centro.nomce%TYPE, p_dirce dai2.centro.dirce%TYPE); FUNCTION BorrarCentro (p_codce dai2.centro.codce%TYPE) RETURN number; FUNCTION CuantosCentrosAlta RETURN number; FUNCTION CuantosCentrosBaja RETURN number; g_numcentrosalta number:=0; g_numcentrosbaja number:=0; END CentroPaquete; CREATE OR REPLACE PACKAGE BODY CentroPaquete IS PROCEDURE AadirCentro(p_codce dai2.centro.codce%TYPE, p_nomce dai2.centro.nomce%TYPE, p_dirce dai2.centro.dirce%TYPE) IS BEGIN INSERT INTO centro VALUES(p_codce, p_nomce, p_dirce); COMMIT; g_numcentrosalta:=g_numcentrosalta+1; END AadirCentro; FUNCTION BorrarCentro(p_codce dai2.centro.codce%TYPE) RETURN number IS BEGIN DELETE FROM centro WHERE codce=p_codce; IF sql%NOTFOUND THEN RETURN -1; ELSE g_numcentrosbaja:=g_numcentrosbaja+1; RETURN 0; END IF; END BorrarCentro; FUNCTION CuantosCentrosAlta RETURN number IS BEGIN RETURN g_numcentrosalta; END CuantosCentrosAlta; 28

FUNCTION CuantosCentrosBaja RETURN number IS BEGIN RETURN g_numcentrosbaja; END CuantosCentrosBaja; END CentroPaquete; 11.3. mbito y llamada a los subprogramas de un paquete: Cualquier objeto declarado en la cabecera de un paquete es visible desde cualquier parte, es decir, para referenciar un objeto especificado en la cabecera de un paquete hay que calificarlo con el nombre del paquete. nombre_paquete.nombre_subprograma BEGIN CentroPaquete.AadirCentro (50,NEW CENTER,AVD. DEL OESTE); END; BEGIN DBMS_OUTPUT.PUT_LINE (Se han dado de alta en la sesion: ||CentroPaquete.CuantosCentrosAlta); END; BEGIN IF CentroPaquete.BorrarCentro(60)=0 THEN DBMS_OUTPUT.PUT_LINE (Se ha borrado el centro 60); ELSE DBMS_OUTPUT.PUT_LINE (Centro Inexistente); END; 11.4. Incializacin de un paquete. La primera vez que se llama a un paquete dentro de una sesin ste es instanciado, es decir, es leido del disco y almacenado en memoria RAM. Hay ocasiones en que la primera vez en que se hace referencia al paquete hay que inicializar algunas variables o realizar otros procesos de inicializacin, para ello hay una seccin de inicializacin que se especifica al final del cuerpo del paquete y slo se ejecuta la primera vez que es referenciado. CREATE OR REPLACE PACKAGE CentroPaquete IS PROCEDURE AadirCentro(p_codce dai2.centro.codce%TYPE, p_nomce dai2.centro.nomce%TYPE, p_dirce dai2.centro.dirce%TYPE); FUNCTION BorrarCentro (p_codce dai2.centro.codce%TYPE) RETURN number; FUNCTION CuantosCentrosAlta RETURN number; FUNCTION CuantosCentrosBaja RETURN number; g_numcentrosalta number:=0; 29

g_numcentrosbaja number:=0; FUNCTION DiferenciaCentros RETURN number; g_numcentrosinicio number; END CentroPaquete; CREATE OR REPLACE PACKAGE BODY CentroPaquete IS PROCEDURE AadirCentro(p_codce dai2.centro.codce%TYPE, p_nomce dai2.centro.nomce%TYPE, p_dirce dai2.centro.dirce%TYPE) IS BEGIN INSERT INTO centro VALUES(p_codce, p_nomce, p_dirce); COMMIT; g_numcentrosalta:=g_numcentrosalta+1; END AadirCentro; FUNCTION BorrarCentro(p_codce dai2.centro.codce%TYPE) RETURN number IS BEGIN DELETE FROM centro WHERE codce=p_codce; IF sql%NOTFOUND THEN RETURN -1; ELSE g_numcentrosbaja:=g_numcentrosbaja+1; RETURN 0; END IF; END BorrarCentro; FUNCTION CuantosCentrosAlta RETURN number IS BEGIN RETURN g_numcentrosalta; END CuantosCentrosAlta; FUNCTION CuantosCentrosBaja RETURN number IS BEGIN RETURN g_numcentrosbaja; END CuantosCentrosBaja; FUNCTION DiferenciaCentros RETURN number IS BEGIN RETURN g_nunmcentroinicio+g_numcentrosalta-g_numcentrosbaja; END DiferenciaCentros BEGIN SELECT count(*) INTO g_numcentroinicio from centro; END CentroPaquete;

30

11.5. SOBRECARGA DE SUBPROGRAMAS DE UN PAQUETE. Dentro de un paquete pueden sobrecargarse los procedimientos y funciones, es decir, puede haber ms de un procedimiento o funcin con el mismo nombre pero con distintos parmetros. Esta operacin es muy til en programacin ya que permite aplicar una misma operacin a objetos de tipos diferentes, realizando tratamentos distintos dependiendo del tipo de objeto. Tiene las siguientes restricciones: No se pueden sobrecargar dos programas si solo difieren en el nombre o modo de sus parmetros. No se pueden sobrecargar funciones que solo difieran en el tipo de retorno. No se pueden sobrecargar dos subprogramas cuyos tipos de dato de los parmetros no difieran en la familia de tipos de datos (p.ej.: char y varchar2)

CREATE OR REPLACE PACKAGE centropaquete IS ---------------------; ---------------------; ---------------------; PROCEDURE listardepartamentos(p_codce centro.codce%TYPE); PROCEDURE listardepartamentos(p_nomce centro.nomce%TYPE); END centropaquete; EX: CREATE OR REPLACE PACKAGE BODY centro paquete IS PROCEDURE listadepartamentos(p_codce codce%TYPE) IS CURSOR c_depto IS SELECT nomde FROM depto WHERE codce=p_codce; v_depto c_depto%ROWTYPE; BEGIN OPEN c_depto; FETCH c_depto v_depto; IF c_depto%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE(Cdigo inexistente o no hay departamento); ELSE WHILE c_depto%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_depto); FETCH c_depto INTO v_depto; END LOOP; END IF; CLOSE c_depto; END listadepartamentos; PROCEDURE listadepartamentos(p_nomce centro.nomce%TYPE) IS CURSOR c_depto IS SELECT nomde FROM depto, centro WHERE depto.codce=centro.codce AND 31

BEGIN

nomce=p_nomce; v_depto depto.nomde%TYPE ;

OPEN c_depto; FETCH c_depto v_depto; IF c_depto%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE(Cdigo inexistente o no hay departamento); ELSE WHILE c_depto%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_depto); FETCH c_depto INTO v_depto; END LOOP; END IF; CLOSE c_depto; END listadepartamentos;

12. DISPARADORES O TRIGGERS DE LA BD.


Un disparador es un bloque PL/SQL nominado que se almacena en la BD y se ejecuta cuando tiene lugar un suceso disparo. Un suceso o disparo es una operacin INSERT, UPDATE o DELETE sobre una tabla de la BD. Se utiliza entre otras cosas para: 1. Mantenimiento de restricciones de integridad complejas que no es posible declararlas como constraint . Por ejemplo, un alumno solo se puede matricular de asignaturas que son de la rama en la que est matriculado el alumno. 2. Auditoria de la informacion de la BD, registrando los cambios realizados en la BD y la identidad del usuario que los realiza. Los disparadores se pueden encontrar en la vista del diccionario de datos user_triggers. 12.1. CREACIN DE TRIGGERS. La sintaxis para crear un disparador es la siguiente: CREATE [OR REPLACE] TRIGGER nombre_disparador {BEFORE | AFTER} suceso_disparo [OF nombre_de_columnas] ON nombre tabla [FOR EACH ROW [WHEN(condicion_disparo)]] [DECLARE /*seccion declarativa*/] BEGIN /*seccin procedimental*/ [EXCEPTION /*seccin de excepciones*/] END[nombre_disparador]; donde suceso_disparo especifica con que acciones se dispara el trigger; si son varias van separadas por la palabra OR. BEFORE y AFTER indican que se ejecutar el disparador antes o 32

despus de que se realice la operacin del suceso disparo sobre la BD. La clusula ON especifica la tabla o vista sobre la que est asociada el disparo. La clusula OF especifica las columnas sobre las que est asociada el disparo cuando el suceso disparo es UPDATE. El disparador puede ser a nivel de fila o a nivel de sentencia. Un disparador a nivel de fila se ejecutar por cada fila afectada por la sentencia DML que provoc el disparo. Un disparador es a nivel de fila si se especifica la clusula FOR EACH ROW. Un disparador a nivel de sentencia se ejecutara solo una vez por cada sentencia que provoca el disparo. Se declara un disparador a nivel de fila cuando se encesite acceder a los valores de las filas que estn siendo procesadas por la sentencia DML que provoc el disparo. La clusula WHEN indica que slo se ejecutar el disparador si se cumple la condicin especificada en esta clusula. Esta clusula slo es vlida para disparadores a nivel de fila. El cuerpo de un disparador tiene las siguientes restricciones: 1. Cuando la orden que provoca el disparo es cancelada se cancela el trabajo realizado por el disparador. Esto puede suceder, por ejemplo, al realizar un rollback de la accin que provoc el disparo. 2. No se pueden emitir sentencias de control de transacciones (commit, rollback, savepoint) dentro del cdigo de un trigger. Caractersticas de los disparadores: 1. Si en un trigger se provoca un error, como resultado de la ejecucin del cdigo o bien planificado e introducido por el programador, entonces el suceso disparo que provoc su ejecucin se aborta. 2. La orden para eliminar un disparador es DROP TRIGGER nombre_disparador. 3. Un disparador se puede alterar con la orden ALTER TRIGGER nombre_disparador {DISABLE | ENABLE}. 12.2. PSEUDOREGISTROS :OLD Y :NEW. Como un disparador se ejecuta una vez por cada fila procesada por la orden que provoc el disparo. Dentro de un disparador a nivel de fila puede accederse a los valores de la fila que est siendo procesada utilizando los pseudoregistros :OLD y :NEW. Los valores de :old y :new segn la orden que provoca el disparo son: :NEW INSERT UPDATE DELETE
Valores de la fila despues de a inserccin. Valores de la fila despues de la actualizacin No definido

:OLD
No definido Valores de la fila antes de la actualizacion Valores originales de la fila.

No se aborta si es captado dentro del trigger en la seccin de excepciones.

33

Los valores de los campos :old no se pueden modificar, slo pueden ser ledos. Los valores de los campos :new slo se podrn modificar si es un disparador previo, es decir, est definido como BEFORE. En la condicin de la clusula WHEN del disparador se puede hacer referencia a old y new, sin utilizar los dos puntos de prefijo. Estos pseudoregistros slo pueden utilizarse en disparadores a nivel de fila. 12.3. PREDICADOS INSERTING, UPDATING, DELETING. Un mismo disparador puede estar asociado a varios sucesos disparo. En ese caso se pueden utilizar los predicados inserting, updating y deleting para determinar cuales de los sucesos disparo provoc la ejecucin del disparador. Estos predicados devuelven los valores TRUE o FALSE segn sea o no el suceso que dispar el trigger. E.j.: Crear un trigger que visualice en pantalla el tipo de accin que se realiza sobre la tabla centro. >CREATE OR REPLACE TRIGGER disparadorejemplo AFTER INSERT OR DELETE OR UPDATE ON centro BEGIN IF DELETING THEN DBMS_OUTPUT.PUT_LINE(Se ha producido una baja); END IF; IF INSERTING THEN DBMS_OUTPUT.PUT_LINE(Se ha producido una inserccin); END IF; IF UPDATING THEN DBMS_OUTPUT.PUT_LINE(Se ha producido una actualizacin); END IF; END disparadorejemplo;

13. TRATAMIENTO DE ERRORES.


El mecanismo de tratamiento de errores de ejecucin en PL/SQL se implementa mediante excepcio nes y gestores de excepciones. Cuando se produce un error se genera una excepcin y el control del programa pasa a la seccin de excepciones del bloque, separando de esta manera la lgica del programa del tratamiento de situaciones errneas. 13.1. DECLARACIN DE EXCEPCIONES.

La excepciones se declaran en la seccin declarativa del bloque. Se producen en la seccin procedimental, y se procesan en la seccin de excepciones del bloque. Existen dos tipos de excepciones: Excepciones definidas por el usuario. Excepciones predefinidas del sistema.

34

Una excepcin definida por el usuario es un error cuya definicin se realiza en el programa, y define un error lgico de la semntica de los datos del sistema de informacin. Se declara en la seccin declarativa de un bloque PL/SQL y al igual que las variables de programa tiene un tipo y un mbito. La sistaxis para declarar una expresin es: nombre_excepcin EXCEPTION; E.J.: DECLARE e_DemasiadosAlumnos EXCEPTION; v_var1 number; BEGIN ... CURSOR_ALREADY_OPEN END; e_DemasiadosAlumnos es un identificador de excepcin cuyo mbito se halla hasta el final del bloque en que ha sido definido. Las excepciones predefinidas del sistema se corresponden a errores SQL y errores ORACLE comunes. Al igual que los tipos predefinidos, como number, char, boolean, etc., los identificadores de estas excepciones estn definidas en el paquete standard.sql. Algunas de la excepciones mas comunes: INVALID_CURSOR: se genera cuando se realiza una operacin ilegal sobre un cursor, como cerrar dos veces un cursor. CURSOR_ALREADY_OPEN: intentando abrir un cursor ya abierto. NO_DATA_FOUND: Se genera en dos casos: 1. Una SELECT INTO no devuelve ninguna fila. 2. Cuando se hace referencia a un elemento de array inexistente. TOO_MANY_ROWS: cuando una orden SELECT INTO devuelve varias filas. INVALID_NUMBER: cuando falla un intento de conversin de una cadena de caracteres a un numrico en una sentencia SQL. VALUE_ERROR: se genera cuando se produce un error aritmtico o de conversin o de truncamiento o de restriccin en una orden procedimental o en una sentencia SELECT INTO al asignar valor a la variable del programa. DUP_VAL_ON_INDEX: es ante una violacin de una restriccin de unicidad. GENERACIN DE EXCEPCIONES.

13.2.

Cuando se produce un eror asociado a una excepcin se genera la excepcin. Las excepciones definidas por el usuario se generan de forma explcita, mediante la sentencia RAISE. La sintaxis es: RAISE nombre_excepcion;. Las excepciones predefinidas se generan de forma implcita cuando se produce el error ORACLE asociado,, aunque tambin pueden ser generadas de manera explcita mediante la sentencia RAISE si as se desea. Cuando se genera una excepcin el control se pasa inmediatamente a la seccin de excepciones del bloque. Si esta excepcion no est definida o no se contempla esa excepcin se propaga a la seccin de excepciones del bloque de nivel superior.

35

Una vez que se pasa el control al gestor de excepciones no hay forma de volver a la secin procedimental. 13.3. TRATAMIENTO DE EXCEPCIONES.

Cuando se genera un error el control del programa pasa a la seccin de excepciones del bloque, que est compuesta por gestores para las distintas excepciones. Un gestor de excepcin contiene el cdigo que se ejecutar cuando ocurra el error asociado a la excepcin. La sintaxis de la seccin de excepciones es: EXCEPTION WHEN nombre_excepcin [,nombre_excepcin]... THEN secuencia_de_rdenes WHEN nombre:excepcin[,nombre_excepcin]... THEN secuencia_de_rdenes [WHEN OTHERS THEN secuencia_de_rdenes;] END[nombre_bloque]; La clusula WHEN identifica la excepcin correspondiente a cada gestor. Un mismo gestor se puede utilizar para ms de una excepcin separando en la clusula WHEN los nombres de excepciones con comas. El gestor OTHERS se ejecutar para todas las excepciones generadas para las que no se haya declarado previamente un gestor. Es una buena prctica de programacin definir un gestor OTHERS en el bloque de mayor nivel superior para que no quede ningn error sin tratar. Las funciones predefinidas SQLCODE y SQLERRM permiten determinar cual es el error producido dentro del gestor OTHERS. SQLCODE devuelve el cdigo del error producido. SQLERRM devuelve el mensaje de error del sistema asociado al error, con una longitud mxima de 512 caracteres. Estas dos funciones, si se invocan sin argumento, devuelven los valores del error producido; sin embargo, si se les pasa parmetros devuelven el mensaje asociado al error que pasamos como parmetro. Los cdigos de error son: 0: indica ejecucin normal. 100: est asociado a la excepcin NO DATA FOUND. RESTO DE ERRORES: son todos negativos.

El error 1403 y el error 100 estn asociados al mismo error.

36

APNDICES

37

38

1. Teniendo en cueta el valor de las variables viusualizar los sig mensajes, sila edad es <4 visualizar bebe si es menor de 12 nio, si es menor de 14 visualizar v, si es menor de 18 adolescente, si esta entre 18 y 65 adulto activo, si es mayor de 65 adulto jubilado. 1 declare 2 v_edad number(2):=7; 3 v_mensaje varchar2(30); 4 BEGIN 5 IF v_edad < 4 then 6 v_mensaje:='BEBE'; 7 ELSIF v_edad <12 then 8 v_mensaje:='NIO'; 9 ELSIF v_edad <14 then 10 v_mensaje:='PUBERTAD'; 11 ELSIF v_edad <18 then 12 v_mensaje:='ADOLESCENTE'; 13 ELSIF v_edad <65 then 14 v_mensaje:='ADULTO'; 15 ELSE v_mensaje:='MAYOR'; 16 END IF; 17 DBMS_OUTPUT.PUT_LINE (v_mensaje); 18* END; 2. El mismo de antes xo con while 1 DECLARE 2 v_acumulador number(2); 3 LIMITE constant number(2):=51; 4 BEGIN 5 v_acumulador:=1; 6 WHILE v_acumulador<LIMITE LOOP 7 INSERT INTO TEMP VALUES(v_acumulador); 8 v_acumulador:=v_acumulador+1; 9 END LOOP; 10* END; 3. Escribir un bloque PL/SQL annimo que obtenga los 20 primeros nmero primos (1 es primo). DECLARE cte_limite constant number(2):=20; v_numero number(2):=0; v_divisor number(2); v_contador number(2):=0; v_primo boole an; BEGIN WHILE v_contador<cte_limite LOOP v_numero:=v_numero+1; 39

v_divisor:=v_numero-1; v_primo:=TRUE; WHILE v_divisor>1 LOOP IF mod(v_numero,v_divisor)=0 THEN v_primo:=FALSE; EXIT; --optimizar procesamiento END IF; v_divisor:=v_divisor-1; END LOOP; IF v_primo=TRUE THEN DBMS_OUTPUT.PUT_LINE(v_numero); v_contador:=v_contador+1; END IF; END LOOP; END; 4. Escribir un bloque PL/SQL annimo q pase el contenido de una cadena de caracteres d euna variable alfanumerica a una segunda variable alfanumerica invirtiendo la cadena y visualice ambas variables. Para dar valor a la primera cadena se utilizara una variable de sustitucin (&) Con FOR: DECLARE v_cadena varchar2(40) v_reves varchar2(40) v_longitud number; BEGIN v_longitud:=LENGTH(v_cadena); FOR v_contador IN REVERSE 1..v_longitud LOOP v_reves:=v_reves||SUBSTR(v_cadena,v_contador,1); END LOOP; DBMS_OUTPUT.PUT_LINE ('v_cadena: '|| v_cadena); DBMS_OUTPUT.PUT_LINE ('v_reves: '|| v_reves); END; Con WHILE: DECLARE v_cadena varchar2(40):='&cadena'; v_reves varchar2(40); v_indice number; BEGIN v_indice:=LENGTH(v_cadena); WHILE v_indice>=1 LOOP v_reves:=v_reves||SUBSTR(v_cadena,v_indice,1); v_indice:=v_indice-1; 40

END LOOP; DBMS_OUTPUT.PUT_LINE ('v_cadena: '|| v_cadena); DBMS_OUTPUT.PUT_LINE ('v_reves: '|| v_reves); END; 5. Escribir un bloque annimo que visualice los treinta primeros numeros de la SERIE DE FIBONALLI (Base: los 2 primeros son el 0 y el 1 y cada numero se obtiene a partir de la suma de los dos anteriores numeros: ejem: 0,1,1,2,3,5,8). Con FOR: DECLARE v_siguiente number; v_penultimo number:=0; v_ultimo number:=1; BEGIN DBMS_OUTPUT.PUT_LINE (0); DBMS_OUTPUT.PUT_LINE (1); for v_i in 1..28 loop v_siguiente:=v_penultimo+v_ultimo; DBMS_OUTPUT.PUT_LINE (v_siguiente); v_penultimo:=v_ultimo; v_ultimo:=v_siguiente; end loop; end; Con WHILE: DECLARE v_penultimo number:=0; v_ultimo number:=1; v_siguiente number; v_i number:=1; BEGIN DBMS_OUTPUT.PUT_LINE(v_penultimo); DBMS_OUTPUT.PUT_LINE(v_ultimo); while v_i<=28 loop v_siguiente:=v_penultimo+v_ultimo; DBMS_OUTPUT.PUT_LINE(v_siguiente); v_penultimo:=v_ultimo; v_ultimo:=v_siguiente; v_i:=v_i+1; end loop; end;

41

6. Dada una cadena de caracteres a la q se le da valor mediante una variable de sustitucin determinar si es un palndromo. 1 DECLARE 2 v_cadena varchar2(50):='&cadena'; 3 v_f number; 4 v_comp boolean; 5 BEGIN 6 v_f:=length(v_cadena); 7 for v_i in 1..v_f/2 loop 8 v_comp:=TRUE; 9 if substr(v_cadena,v_i,1)<>substr(v_cadena,v_f,1) THEN 10 v_comp:=FALSE; 11 EXIT; 12 END IF; 13 v_f:=v_f-1; 14 end loop; 15 if v_comp=TRUE then 16 dbms_output.put_line (v_cadena || ' si es un palindromo'); 17 else 18 dbms_output.put_line (v_cadena || ' no es un palindromo'); 19 end if; 20* end; Otra manera: DECLARE v_cadena varchar2(40):='&cadena'; v_reves varchar2(40); v_longitud number; BEGIN v_longitud:=LENGTH(v_cadena); FOR v_contador IN REVERSE 1..v_longitud LOOP v_reves:=v_reves||SUBSTR(v_cadena,v_contador,1); END LOOP; if v_reves=v_cadena then DBMS_OUTPUT.PUT_LINE (v_cadena || ' Es palindromo'); else DBMS_OUTPUT.PUT_LINE (v_cadena || ' No es palidromo'); end if; end;

42

7. Obtener el factorial de los numeros comprendidos entre el 1 y el 20. DECLARE v_resul integer; CTE_LIMITE constant number(2):=20; BEGIN for v_i in 1..CTE_LIMITE loop v_resul:=1; for v_fac in 1..v_i loop v_resul:=v_resul*v_fac; end loop; dbms_output.put_line ('El factorial de ' || v_i || ' es ' || v_resul); end loop; end; 8. Obtener el nombre y salario de un empleado cuyo codigo se obtiene mediante una variable de sustitucin. DECLARE v_codigo emple.codem%TYPE:=&codigo; v_nombre emple.nomem%TYPE; v_salario emple.salar%TYPE; BEGIN SELECT nomem,salar INTO v_nombre, v_salario FROM emple WHERE codem=v_codigo; DBMS_OUTPUT.PUT_LINE (v_nombre || ' ' || v_salario); END; 9. Visualizar todos los datos de la asignatura CASE. DECLARE v_nombre emple.nomem%TYPE; v_codde emple.codde%TYPE:=&CODIGO; BEGIN SELECT NOMEM INTO v_nombre from emple where codde=some(select codde from depto where codde=v_codde) AND salar>=some(select salar from emple); DBMS_OUTPUT.PUT_LINE(v_nombre); end; DECLARE v_asignatura asigna%ROWTYPE; BEGIN SELECT * 43

INTO v_asignatura FROM asigna WHERE codasigna=CASE; DBMS_OUTPUT.PUT_LINE (v_asignatura.codasigna|| ' ' || v_asignatura.denasigna|| ' ' || v_asignatura.codrama|| ' ' || v_asignatura.curso|| ' ' || v_asignatura.coddepar); END; 10. Obtener el nombre del empleado que tenga el sueldo mximo de aquel departamento cuyo cdigo se introduce mediante una variable de sustitucin DECLARE v_nombre emple.nomem%TYPE; v_codde emple.codem%TYPE:=&CODIGO; v_nomde depto.nomde%TYPE; BEGIN SELECT NOMEM, nomde INTO v_nombre,v_nomde from emple, depto where emple.codde=depto.codde and emple.codde=v_codde and salar+nvl(comis,0)>=all(select salar+nvl(comis,0) from emple where codde=v_codde); dbms_output.put_line (v_nombre || ' ' || v_nomde); end; 10. Cursor para obtener todos los datos de los alumnos del DAI DECLARE CURSOR c_alumno_DAI IS SELECT * FROM alumno WHERE codrama=DAI; v_alumno alumno%ROWTYPE; BEGIN OPEN c_alumno_DAI; FETCH c_alumno_DAI INTO v_alumno; WHILE c_alumno_DAI%FOUND LOOP DBMS_OUPUT.PUT_LINE (v_alumno.dni || || v_alumno.nomalumno || || v_alumno.codrama || || v_alumno.codgrupo); FETCH c_alumno DAI INTO v_alumno; END LOOP; CLOSE c_alumno_DAI; 11. Cursor para obtener el DNI y el nombre de todos los alumnos del DAI con variable de programa DECLARE v_codrama rama.codrama%TYPE; CURSOR c_alumno_codrama IS SELECT dni,nomalumno FROM alumno WHERE codrama:=v_codrama; 44

v_dni alumno.dni%TYPE; v_nombre alumno.nomalumno%TYPE; BEGIN v_codrama:=DAI; OPEN c_alumno_codrama; FETCH c_alumno_codrama INTO v_dni, v_nombre; WHILE c_alumno_codrama%FOUND LOOP DBMS_OUPUT.PUT_LINE (v_dni || || v_nombre); FETCH c_alumno_codrama DAI INTO v_dni, v_nombre; END LOOP; CLOSE c_alumno_codrama;

12. Obtener los nombres de los empleados y los nombres de los departamentos en que trabajan de los empleados que trabajan en la calle alcala. Presentarlos ordenados por departamentos y dentro de departamentos por orden alfabetico de nombre. DECLARE CURSOR c_depto_alcala IS SELECT nomde, nomem from depto,emple where emple.codde=depto.codde and codce=some (select codce from centro where dirce like 'C/ALCALA%'); v_nomde depto.nomde%TYPE; v_nomem emple.nomem%TYPE; BEGIN OPEN c_depto_alcala; FETCH c_depto_alcala INTO v_nomde,v_nomem; while c_depto_alcala%FOUND LOOP DBMS_OUTPUT.PUT_LINE (v_nomde || ' ' || v_nomem); FETCH c_depto_alcala INTO v_nomde,v_nomem; end loop; close c_depto_alcala; end; 13. Obtener los nombres de los empleados y los nombres de los departamentos en que trabajan de los empleados que trabajan en la calle alcala y que cobren ms que su jefe. Presentarlos ordenados por departamentos y dentro de departamentos por orden alfabetico de nombre. DECLARE CURSOR c_depto_alcala IS SELECT nomde, nomem from depto,emple where emple.codde=depto.codde and codce=some (select codce from centro where dirce like 'C/ALCALA%') and

45

salar>(select salar from emple where codem=depto.codjefe) order by nomde, nomem; v_nomde depto.nomde%TYPE; v_nomem emple.nomem%TYPE; BEGIN OPEN c_depto_alcala; FETCH c_depto_alcala INTO v_nomde,v_nomem; while c_depto_alcala%FOUND LOOP DBMS_OUTPUT.PUT_LINE (v_nomde || ' ' || v_nomem); FETCH c_depto_alcala INTO v_nomde,v_nomem; end loop; close c_depto_alcala; end; 14. Desarrollar un algoritmo que muestre los nombres de los departamentos de la empresa y cuantos empleados trabajan en cada uno de los departamentos. DECLARE CURSOR c_nomde IS SELECT NOMDE,COUNT(*) FROM DEPTO,EMPLE WHERE DEPTO.CODDE=EMPLE.CODDE GROUP BY NOMDE; V_NUMEROEMPLE NUMBER; V_NOMDE DEPTO.NOMDE%TYPE; BEGIN OPEN c_nomde; FETCH c_nomde into V_NOMDE,V_NUMEROEMPLE; WHILE C_NOMDE%FOUND LOOP DBMS_OUTPUT.PUT_LINE(V_NUMEROEMPLE||V_NOMDE); FETCH c_nomde into v_nomde,v_numeroemple; end loop; CLOSE c_nomde; end; 15. Dado un codigo de empleado cuyo valor se obtiene mediante una variable de sustitucin determinar si es jefe en funciones, en cuyo caso se visualizar un mensaje indicando que si es jefe y se retirar la comisin a todos los empleados del departamento del que es jefe y se visualizar un mensaje indicando a cuantos empleados se ha retirado la comisin, en el caso contrario se pondr un mensaje de que no se ha retirado. DECLARE v_codigo emple.codem%TYPE:=&kike; CURSOR c_jefe IS SELECT CODJEFE FROM DEPTO WHERE TIDIR='F'; v_emple emple.codem%TYPE; 46

v_contador number; v_valor boolean; BEGIN OPEN c_jefe; FETCH c_jefe INTO v_emple; WHILE c_jefe%FOUND LOOP IF v_codigo=v_emple then v_valor:=TRUE; EXIT; ELSE v_valor:=FALSE; END IF; FETCH c_jefe INTO v_emple; END LOOP; if v_valor=TRUE then SELECT COUNT(*) into v_contador FROM EMPLE WHERE CODDE=SOME(select codde from depto WHERE codjefe=v_codigo and tidir='F') AND COMIS IS NOT NULL; UPDATE emple SET comis=NULL WHERE codde=SOME(select codde from depto WHERE codjefe=v_codigo and tidir='F'); DBMS_OUTPUT.PUT_LINE('ESTE EMPLEADO ES JEFE EN FUNCIONES.'||'SE HAN ACTUALIZADO '|| v_contador); else DBMS_OUTPUT.PUT_LINE('NO SE HA RETIRADO'); end if; CLOSE c_jefe; end; DECLARE v_codigo emple.codem%TYPE:=&codigo; CURSOR c_depto IS select codde from depto where codjefe=v_codigo AND tidir='F'; v_depto depto.codde%TYPE; v_cuantos number; v_cuantos_total number:=0; BEGIN OPEN c_depto; FETCH c_depto INTO v_depto; IF c_depto%FOUND THEN 47

DBMS_OUTPUT.PUT_LINE ('El emp leado es jefe de departamento'); WHILE c_depto%FOUND LOOP SELECT count(*) INTO v_cuantos from emple where codde=v_depto and comis is not null; update emple set comis=NULL where codde=v_depto and comis is not null; v_cuantos_total:=v_cuantos_total+v_cuantos; FETCH c_depto INTO v_depto; END LOOP; DBMS_OUTPUT.PUT_LINE ('Se ha retirado la comision a '||v_cuantos||' empleados'); ELSE DBMS_OUTPUT.PUT_LINE ('EL empleado no es jefe en funciones'); END IF; CLOSE c_depto; END; 16. Obtener el nombre y salario de los 5 empleados que tienen los salarios ms altos de la empresa. DECLARE CURSOR c_salario is select nomem,salar from emple order by salar desc; v_nomem c_salario%rowtype; BEGIN open c_salario; fetch c_salario into v_nomem; FOR i IN 1..5 LOOP dbms_output.put_line(v_nomem.nomem||' '||v_nomem.salar); fetch c_salario into v_nomem; END LOOP; close c_salario; END;

17. Obtener los nombres de los empleados que cobren los 5 salarios ms altos de la empresa. DECLARE CURSOR c_emple IS SELECT nomem, salar FROM emple ORDER BY salar DESC; v_emple c_emple%rowtype; v_contador number:=0; v_salar_ant emple.salar%type; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; WHILE v_contador<5 and c_emple%FOUND LOOP v_salar_ant:=v_emple.salar; WHILE v_emple.salar=v_salar_ant and c_emple%FOUND LOOP DBMS_OUTPUT.PUT_LINE (v_emple.nomem|| ||v_emple.salar); 48

FETCH c_emple INTO v_emple; END LOOP; v_contador:=v_contador+1; END LOOP; CLOSE c_emple; END; 18. Obtener los nombres y salario de los dos empleados que menos ganen de cada categora laboral, especificando el nombre de la categora laboral. DECLARE CURSOR c_emple IS SELECT nomcat, nomem, salar FROM emple e, catego c WHERE c.codcat=e.codcat ORDER BY nomcat, salar; v_emple c_emple%rowtype; v_contador number; v_nomcat_ant catego.nomcat%type; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; WHILE c_emple%FOUND LOOP DBMS_OUTPUT.PUT_LINE (v_emple.nomcat); v_nomcat_ant:=v_emple.nomcat; v_contador:=0; WHILE v_emple.nomcat=v_nomcat_ant and c_emple%FOUND and v_contador<2 LOOP DBMS_OUTPUT.PUT_LINE(v_emple.nomem||' '||v_emple.salar); v_contador:=v_contador+1; FETCH c_emple into v_emple; END LOOP; WHILE v_emple.nomcat=v_nomcat_ant and c_emple%FOUND LOOP FETCH c_emple INTO v_emple; END LOOP; END LOOP; CLOSE c_emple; END;

49

19. Realizar un programa que visualice de cada categora laboral los empleados con los dos salarios ms bajos. (dos salarios mas bajos, no dos empleados...) DECLARE CURSOR c_emple IS SELECT nomcat, nomem, salar FROM emple e, catego c WHERE c.codcat=e.codcat ORDER BY nomcat, salar; v_emple c_emple%rowtype; v_contador number; v_nomcat_ant catego.nomcat%type; v_salar_ant emple.salar%type; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; WHILE c_emp le%found LOOP DBMS_OUTPUT.PUT_LINE (v_emple.nomcat); v_nomcat_ant:=v_emple.nomcat; v_contador:=0; v_salar_ant:=v_emple.salar; WHILE v_emple.nomcat=v_nomcat_ant and c_emple%FOUND and v_contador<2 LOOP WHILE v_emple.salar=v_salar_ant and v_emple.nomcat=v_nomcat_ant and c_emple%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_emple.nomem||' '||v_emple.salar); FETCH c_emple INTO v_emple; END LOOP; v_contador:=v_contador+1; v_salar_ant:=v_salar.emple; END LOOP; WHILE v_emple.nomcat=v_nomcat_ant and c_emple%FOUND LOOP FETCH c_emple INTO v_emple; END LOOP; END LOOP; CLOSE c_emple; END; 20. Aquellos alumnos matriculados en la asignatura GRAF que tengan en la primera evaluacin entre un 45 y un 49 y han obtenido en la segunda evaluacin 7 o ms de 7 asignarles en la primera evaluacin un 5, visualizando el mensaje: El alumno con DNI: xxxxxxxxx-x se le ha asignado 5 en la primera evaluacin al tener xx en la primera evaluacin y xx en la segunda evaluacin. DECLARE CURSOR c_alumno IS select dni, nota1, nota2 from estudia where codasigna='GRAF' FOR UPDATE OF nota1; v_alumno c_alumno%rowtype; v_nota_ant estudia.nota1%type; 50

BEGIN OPEN c_alumno; FETCH c_alumno INTO v_alumno; WHILE c_alumno%found LOOP IF v_alumno.nota1>=3.5 and v_alumno.nota1<=4.9 and v_alumno.nota2>=5 THEN v_nota_ant:=v_alumno.nota1; UPDATE estudia SET nota1=5; DBMS_OUTPUT.PUT_LINE ('El alumno con dni '||v_alumno.dni||' se le ha asignado un 5 en la primera evaluacin al tener un '||v_nota_ant||' en la primera evaluacin y un '||v_alumno.nota2||' en la segunda'); END IF; FETCH c_alumno INTO v_alumno; END LOOP; CLOSE c_alumno;

END;

21. Los salarios de los empleados van a ser actualizados con los siguientes criterios: A los empleados sin comisin se les incrementa el salario un 5%. A los empleados con comisin superior a 500 se incrementa el salario en un 4% y la comisin en un 3%. A los empleados con comisin inferior o igual a 500 se les incrementa el salario en un 5% y la comisin un 4%.

Adems, previamente se ha declarado una tabla llamada empleados con los siguientes campos: CODEM NOMEM SALARANT SALARNUE COMISANT COMISNUE

y se ha de insertar una fila por cada empleado de la tabla emple con los anteriores y nuevos salarios y comisiones. DECLARE CURSOR c_actualiza IS SELECT codem, nomem, salar, comis FROM emple for update of salar, comis; v_actualiza c_actualiza%rowtype; BEGIN OPEN c_actualiza; FETCH c_actualiza INTO v_actualiza; WHILE c_actualiza%found LOOP IF v_actualiza.comis is null THEN INSERT INTO empleados VALUES (v_actualiza.codem,v_actualiza.nomem, v_actualiza.salar,v_actualiza.salar*1.05,v_actualiza.comis, v_actualiza.comis); 51

UPDATE emple SET salar=salar*1.05; END IF; IF_actualiza.comis>500 THEN INSERT INTO empleados values (v_actualiza.codem,v_actualiza.nomem, v_actualiza.salar,v_actualiza.salar*1.04,v_actualiza.comis, v_actualiza.comis*1.05); UPDATE emple SET salar=salar*1.04 ,comis=comis*1.05; END IF; IF v_actualiza.comis<=500 THEN INSERT INTO empleados values (v_actualiza.codem,v_actualiza.nomem, v_actualiza.salar, v_actualiza.salar*1.05,v_actualiza.comis, v_actualiza.comis*1.04); UPDATE emple set salar=salar*1.05 ,comis=comis*1.04; END IF; FETCH c_actualiza INTO v_actualiza; END LOOP; CLOSE c_actualiza; END; 22. Actualizar la nota final de los alumno de DAI, adems dar de baja en la asignatura a aquello alumnos que tengan las tres evaluaciones suspensas en la asignatura, almacenando en una tabla llamada alumnosbaja el DNI del alumno, nombre del alumno, codrama que estudia, curso en que esta matriculado y el codigo de asignatura en el que se ha dado de baja. 23. A los empleados incrementarles el salario en un 5 por % si cobran comision y en un 9 % si no cobran comision,de cada departamento. (copiado en papel) 24. Escribir una funcin en la que se pasa un cdigo de asignatura y se visualiza cuantos alumnos hay matriculados en esa asignatura y la nota final ms alta. v.1.0.: La visualizacin se realizar dentro del procedimiento. v.2.0: La visualizacin se realizar en la llamada al procedimiento. CREATE OR REPLACE PROCEDURE alumnos( p_codasigna asigna.codasigna%TYPE) IS v_codasigna asigna.codasigna%TYPE; v_contador number; v_nota estudia.notaf%TYPE; BEGIN SELECT count(*),max(NOTAF) notas into v_contador, v_nota from asigna a, estudia e WHERE e.codasigna=p_codasigna AND e.codasigna=a.codasigna GROUP BY denasigna; DBMS_OUTPUT.PUT_LINE(v_contador||' '||v_nota); END alumnos; v.1.0

52

CREATE OR REPLACE PROCEDURE alumnos( p_codasigna asigna.codasigna%TYPE) IS v.1.1 CURSOR c_alumnos IS SELECT count(*) contador,max(NOTAF) notas from asigna a, estudia e WHERE e.codasigna=p_codasigna AND e.codasigna=a.codasigna GROUP BY denasigna; v_alumnos c_alumnos%ROWTYPE; BEGIN OPEN c_alumnos; FETCH c_alumnos INTO v_alumnos; IF c_alumnos%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_alumnos.contador||' '||v_alumnos.notas); ELSE DBMS_OUTPUT.PUT_LINE(Esa asignatura no se imparte en este centro); END IF; END alumnos; CREATE OR REPLACE PROCEDURE alumnos( v.1.2 p_codasigna asigna.codasigna%TYPE) IS CURSOR c_alumnos IS SELECT count(*) contador,max(NOTAF) notas from asigna a, estudia e WHERE e.codasigna=p_codasigna AND e.codasigna=a.codasigna GROUP BY denasigna; v_alumnos c_alumnos%ROWTYPE; BEGIN OPEN c_alumnos; FETCH c_alumnos INTO v_alumnos; IF c_alumnos%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_alumnos.contador||' '||v_alumnos.notas); ELSE DBMS_OUTPUT.PUT_LINE('Esa asignatura no se imparte en este centro'); END IF; CLOSE c_asigna; CLOSE c_alumnos; END alumnos; CREATE OR REPLACE PROCEDURE alumnos2( p_codasigna in asigna.codasigna%TYPE, p_denasigna out asigna.denasigna%TYPE, p_contador out number, p_nota out estudia.notaf%TYPE) IS BEGIN SELECT denasigna, count(*), max(notaf) INTO v.2.0

53

END alumnos2;

p_denasigna, p_contador, p_nota FROM estudia e, asigna a WHERE a.codasigna=e.codasigna AND e.codasigna=p_codasigna GROUP BY denasigna;

DECLARE v_denasigna asigna.denasigna%TYPE; v_contador number; v_nota estudia.notaf%TYPE; BEGIN alumnos2('FOL',v_denasigna,v_contador,v_nota); DBMS_OUTPUT.PUT_LINE(v_denasigna||' '||v_contador||' '|| v_nota); END; CREATE OR REPLACE PROCEDURE alumnos2( p_codasigna IN asigna.codasigna%TYPE, p_denasigna OUT asigna.denasigna%TYPE, p_cuantos OUT number, p_nota OUT estudia.notaf%TYPE) IS CURSOR c_asigna IS SELECT denasigna FROM asigna WHERE codasig na=p_codasigna; v_denasigna asigna.denasigna%TYPE; CURSOR c_estudia IS SELECT count(*) cuantos, max(notaf) media FROM estudia WHERE codasigna=p_codasigna; v_estudia c_estudia%ROWTYPE; BEGIN OPEN c_asigna; FETCH c_asigna INTO v_denasigna; IF c_asigna%NOTFOUND THEN p_denasigna:=NULL; ELSE p_denasigna:=v_denasigna; OPEN c_estudia; FETCH c_estudia INTO v_estudia; p_cuantos:=v_estudia.cuantos; p_nota:=v_estudia.media; CLOSE c_estudia; END IF; CLOSE c_asigna; END alumnos2; 54

v.2.1

DECLARE v_codasigna asigna.codasigna%TYPE:=&Cdigo; v_denasigna asigna.denasigna%TYPE; v_cuantos number; v_nota estudia.notaf%TYPE; BEGIN alumnos2(v_codasigna,v_denasigna,v_cuantos,v_nota); IF v_denasigna IS NULL THEN DBMS_OUTPUT.PUT_LINE(Cdigo de asig natura inexistente); ELSE DBMS_OUTPUT.PUT_LINE(v_denasigna|| ||v_cuantos|| ||v_nota); END IF; END; 25. Realizar un procedimiento que introduciendo el parametro DNI, visualize el nombre del alumno y el nombre de las asignaturas q cursa. CREATE OR REPLACE PROCEDURE alumno_asignaturas (p_dni in alumno.dni%TYPE) IS CURSOR c_alumno IS SELECT nomalumno FROM alumno WHERE dni=p_dni; v_nomalumno alumno.nomalumno%TYPE; CURSOR c_asigna IS SELECT denasigna from estudia, asigna WHERE dni=p_dni AND estudia.codasigna=asigna.codasigna; v_denasigna asigna.denasigna%TYPE; BEGIN OPEN c_alumno; FETCH c_alumno INTO v_nomalumno; IF c_alumno%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('DNI inexistente'); ELSE DBMS_OUTPUT.PUT_LINE (v_nomalumno); OPEN c_asigna; FETCH c_asigna INTO v_denasigna; IF c_asigna%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('El alumno con DNI '||p_dni||' no esta matriculado de ninguna asignatura); ELSE WHILE c_asigna%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_denasigna); FETCH c_asigna INTO v_denasigna; END LOOP; END IF; 55

CLOSE c_asigna; END IF; CLOSE c_alumno; END alumno_asignaturas; 26. Escribir una funcin que reciba como parmetros u cdigo de empleado y devuelva el nombre de departamento o NULL si el emple no existe. CREATE OR REPLACE FUNCTION empleado_departamento(p_codem emple.codem%TYPE) RETURN depto.nomde%TYPE IS CURSOR c_emple IS SELECT nomde FROM depto WHERE codde=(select codde from emple where codem=p_codem); v_nomde depto.nomde%TYPE ; BEGIN OPEN c_emple ; FETCH c_emple INTO v_nomde; IF c_emple%NOTFOUND THEN RETURN NULL; ELSE RETURN v_nomde; END IF; END empleado_departamento; DECLARE v_nombre depto.nomde%TYPE; BEGIN v_nombre :=empleado_departamento(280) ; IF v_nombre IS NULL THEN DBMS_OUTPUT.PUT_LINE(Cdigo de empleado inexistente); ELSE DBMS_OUTPUT.PUT_LINE(v_nombre); END IF; END; 27. Escribir una funcin en la que se pide un DNI de alumno y un cdigo de asignatura y devuelve la nota media o bien los siguientes cdigos de incidencia: -1 alumno inexistente. -2 asignatura inexistente. -3 alumno no matriculado en esa asignatura. CREATE OR REPLACE FUNCTION nota_media(p_dni alumno.dni%TYPE, p_codasigna asigna.codasigna%TYPE) RETURN estudia.notaf%TYPE IS CURSOR c_alum IS SELECT dni FROM alumno WHERE dni=p_dni; v_alum c_alum%ROWTYPE; 56

CURSOR c_asigna IS SELECT codasigna FROM asigna WHERE codasigna=p_codasigna; v_asigna c_asigna%ROWTYPE; CURSOR c_estudia IS SELECT notaf FROM estudia WHERE dni=p_dni AND codasigna=p_codasigna; v_nota estudia.notaf%TYPE; BEGIN OPEN c_alum; FETCH c_alum INTO v_alum; IF c_alum%NOTFOUND THEN RETURN 1; ELSE OPEN c_asigna; FETCH c_asigna INTO v_asigna; IF c_asigna%NOTFOUND THEN RETURN 2; END IF; END IF; OPEN c_estudia; FETCH c_estudia INTO v_nota; IF c_estudia%NOTFOUND THEN RETURN 3; ELSE RETURN v_nota; END IF; END nota_media; DECLARE v_notamedia estudia.notaf%TYPE; BEGIN v_notamedia:=nota_media(280,CASE); IF v_notamedia= -1 THEN DBMS_OUTPUT.PUT_LINE(DNI de alumno inexistente); ELSIF v_notamedia=-2 THEN DBMS_OUTPUT.PUT_LINE(Cdigo de asignatura inexistente); ELSIF v_notamedia=-3 THEN DBMS_OUTPUT.PUT_LINE(El alumno no est matriculado en la asignatura); ELSE DBMS_OUTPUT.PUT_LINE(La nota media es ||v_notamedia); END IF; END;

57

28. Escribir un procedimiento que suba el salario de un empleado cuyo cdigo se pasa como parmetro si su salario es menos que el salario medio de su categora profesional. La subida ser del 50% de la diferencia entre el salario del empleado y la media de su categora laboral. CREATE OR REPLACE PROCEDURE subida (p_codem emple.codem%TYPE) IS CURSOR c_emple IS SELECT codcat, codem, salar FROM emple WHERE codem=p_codem; v_emple c_emple%ROWTYPE; CURSOR c_catego IS SELECT codcat,avg(salar) media FROM emple where codcat=v_emple.codcat; v_catego c_catego%ROWTYPE; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; IF c_emple%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE(Codem inexistente); ELSE OPEN c_catego; FETCH c_catego INTO v_catego; WHILE v_catego.codcat<>v_emple.codcat LOOP FETCH c_catego INTO v_catego; END LOOP; IF v_emple.salar<v_catego.media THEN UPDATE emple SET salar=(v_catego.media-v_emple.salar)*0.5+v_emple.salar WHERE codem=p_codem; DBMS_OUTPUT.PUT_LINE(v_emple.codem|| ||v_emple.salar|| ||v_catego.media) ; ELSE DBMS_OUTPUT.PUT_LINE(Imposible actualizar); END IF; END IF; END subida; 29. Realizar un procedimiento llamado calculo_trienios que calcule los trienios de un determinado empleado, el programa ha de tener el siguiente diseo. El procedimiento recibe como parmetro un cdigo de empleado, los trienios los debe calcular una funcin llamada nmero_ trienios y el procedimiento visulizara el siguiente mensaje: El empleado nombreempleado tiene XX trienios. CREATE OR REPLACE PROCEDURE calculo_trienios (p_codem emple.codem%TYPE) IS CURSOR c_emple IS SELECT nomem, fecing FROM emple WHERE codem=p_codem; v_emple c_emple%ROWTYPE; v_trienio number(2); BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; IF c_emple%NOTFOUND THEN 58

DBMS_OUTPUT.PUT_LINE(Cdigo de empleado inexistente); v_trienio:=numero_trienios(v_emple.fecing); DBMS_OUTPUT.PUT_LINE(El empleado ||v_emple.nomem|| lleva ||v_trienio|| trienios en la empresa); END IF; END calculo_trienios; CREATE OR REPLACE FUNCTION numero_trienios(p_fecha date) RETURN number AS v_aos number; BEGIN RETURN TRUNC(((sysdate-p_fecha)/365)/3); END numero_trienios; 30. Escribir un procedimiento que suba el salario a todos los empleados que ganen menos que el salario medio de su categora profesional. la subida ser del 50% de la diferencia entre el salario del empleado y la media de su categora laboral. CREATE OR REPLACE PROCEDURE subidon IS CURSOR c_catego IS SELECT codcat, avg(salar) media FROM emple GROUP BY codcat; v_catego c_catego%ROWTYPE; CURSOR c_emple IS SELECT codem,codcat,salar FROM emple WHERE catego=v_catego.codcat; v_emple c_emple%ROWTYPE; BEGIN OPEN c_catego; FETCH c_catego INTO v_catego; WHILE c_catego%FOUND LOOP OPEN c_emple; FETCH c_emple INTO v_emple; WHILE v_emple.codcat=v_catego.codcat AND v_emple.salar<v_catego.media loop UPDATE emple SET salar=(v_catego.mediav_emple.salar)/2+v_emple.salar WHERE codem=v_emple.codem; DBMS_OUTPUT.PUT_LINE(v_emple.codem|| ||v_emple.salar); FETCH c_emple INTO v_emple; END LOOP; FETCH c_catego INTO v_catego; END LOOP; CLOSE c_catego; CLOSE c_emple; END; ************TERMINAR PARA CORRECTO FUNCIONAMIENTO********************* 59 ELSE

CREATE OR REPLACE PROCEDURE subir_salario_a_todos IS CURSOR c_emple IS SELECT codcat,salar from emple FOR UPDATE OF salar; v_emple c_emple%ROWTYPE; v_salar_medio emple.salar%TYPE; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; WHILE c_emple%FOUND LOOP SELECT avg(salar) into v_salar_medio FROM EMPLE WHERE codcat=v_emple.codcat; IF v_emple.salar<v_salar_medio THEN UPDATE emple ST SALAR=SALAR+(v_salar.medio-v_emple.salar)/2 WHERE CURRENT OF c_emple; END IF; FETCH c_emple INTO v_salar; END LOOP; CLOSE c_emple; END; 31. Escribir un procedimie nto que reciba como parmetros cdigo de departamento, importe y porcentaje y que suba el salario a todos los empleados del departamento. La subida ser el porcentaje sobre su salario o bien el importe si es ms beneficioso. CREATE OR REPLACE PROCEDURE subida_porcentaje(p_codde depto.codde%TYPE, p_importe number, p_porcentaje number) IS CURSOR c_emple IS SELECT codem, salar FROM emple WHERE codde=p_codde FOR UPDATE OF salar; v_emple c_emple%ROWTYPE; BEGIN OPEN c_emple; FETCH c_emple INTO v_emple; WHILE c_emple%FOUND LOOP IF p_importe>(salar*(p_porcentaje/100)) THEN UPDATE emple SET salar=salar+p_importe WHERE CURRENT OF c_emple; DBMS_OUTPUT.PUT_LINE(v_emple.codem|| ||v_emple.salar); ELSE UPDATE emple SET salar=salar+(salar*p_porcentaje/100) WHERE CURRENT OF c_emple; DBMS_OUTPUT.PUT_LINE(v_emple.codem|| ||v_emple.salar); END IF; FETCH c_emple INTO v_emple; END LOOP; CLOSE c_emple; 60

END subida_porcentaje; 32. Escribir un procedimiento que reciba como parmetro un cdigo de rama y obtenga por cada grupo de esa rama el nombre del alumno que tiene la mayor nota global. CREATE OR REPLACE PROCEDURE nota_global(p_codrama rama.codrama%TYPE) IS CURSOR c_nota IS SELECT nomalumno, a.codgrupo, avg(notaf) notaza FROM estudia e, alumno a WHERE e.dni=a.dni AND a.codrama=p_codrama GROUP BY a.codgrupo, nomalumno ORDER BY a.codgrupo,notaza DESC; v_nota c_nota%ROWTYPE; v_codgrupo grupo.codgrupo%TYPE; BEGIN OPEN c_nota; FETCH c_nota INTO v_nota; WHILE c_nota%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_nota.nomalumno||' '||v_nota.codgrupo||' '||v_nota.notaza); v_codgrupo:=v_nota.codgrupo; WHILE v_nota.codgrupo=v_codgrupo AND c_nota%FOUND LOOP FETCH c_nota INTO v_nota; END LOOP; END LOOP; CLOSE c_nota; END nota_global; ************************************************************************* 33. Escribir un procedimiento q reciba como parmetro un codigo de rama y obtenga por cada grupo de esa rama el nombre del alumno q tiene la mayor nota global (avg(notaf) de todas las asignaturas) CREATE OR REPLACE PROCEDURE nota_medi( p_codrama alumno.codrama%TYPE) IS CURSOR c_alumno IS SELECT nomalumno,avg((nota1+nota2+nota3)/3) media,codgrupo FROM alumno,estudia where alumno.dni=estudia.dni and codrama=p_codrama group by nomalumno,codgrupo order by 3,2 desc; v_alumno c_alumno%ROWTYPE; v_codgrupo alumno.codgrupo%TYPE:='0'; BEGIN OPEN c_alumno; FETCH c_alumno INTO v_alumno; WHILE c_alumno%FOUND LOOP 61

IF v_alumno.codgrupo<>v_codgrupo THEN DBMS_OUTPUT.PUT_LINE ('Alumno: '||v_alumno.nomalumno||' del grupo: '||v_alumno.codgrupo||'con nota media: '||v_alumno.media); v_codgrupo:=v_alumno.codgrupo; END IF; FETCH c_alumno INTO v_alumno; END LOOP; END nota_medi; 34. Construir el paquete alumnopaquete que contenga los siguientes subprogramas: 1. Listado asignatura alumnos (proc listadoasignaturaalumnos), que recibe como parmetro un codigo de asignatura y visualiza la denominacion de la asignatura y los nombres de los alumnos matriculados en ella en orden alfabtico. 2. Listadoalumnoasignaturas, que recibe como parametro un dni de alumno y visualiza el nombre del alumno y los nombres de las asignaturas en que est matriculado con sus calificaciones. 3. Funcin: matricularalumnoasignatura, que recibe como parmetros un dni de alumno y un codigo de asignatura, y ha de matricular al alumno en la asignatura, realizando el siguiente control de situaciones: - Comprobar si el alumno est matriculado en el centro. Si no devolver 1. - Comprobar que el codigo de asignatura existe. Si no devolver 2. - Comprobar que el alumno no est ya matriculado en esa asignatura. Si no devolver 3. - En caso que se pueda matricular el alumo en la asignatura devolver 0. 4. Procedimiento: estadisticamatriculas. Se da un curso o un turno y visualiza el siguiente mensaje: En el curso xx hay xx alumnos En el turno xx hay xx alumnos. 5. Procedimiento: listadoalumnos. Donde puede recibir como parmetro o bien un cdigo de rama, en cuyo caso se presentan los alumnos de la rama clasificados por turnos, grupos y en orden alfabtico, o bien recibir un codigo de rama y un turno, en cuyo caso visualiza los alumnos de la rama y turno especificados clasificados por grupos, y dentro de ellos, en orden alfabtico. CREATE OR REPLACE PACKAGE alumnopaquete IS PROCEDURE listadoasignaturaalumnos(p_codasigna asigna.codasigna%TYPE); PROCEDURE listadoalumnoasignaturas(p_dni alumno.dni%TYPE); FUNCTION matricularalumnoasignatura(p_dni alumno.dni%TYPE, p_codasigna asigna.codasigna%TYPE) RETURN number; PROCEDURE estadisticamatriculas(p_turno grupo.turno%TYPE); PROCEDURE estadisticamatriculas (p_curso grupo%TYPE); PROCEDURE listadoalumnos(p_codrama rama.codrama%TYPE); PROCEDURE listadoalumnos(p_codrama rama.codrama%TYPE, p_turno grupo.turno%TYPE); v_curso number v_turno char; END alumnopaquete; CREATE OR REPLACE PACKAGE BODY alumnopaquete IS PROCEDURE listadoasignaturaalumnos(p_codasigna asigna.codasigna%TYPE) IS 62

CURSOR c_listado IS SELECT nomalumno FROM alumno al, estudia e WHERE al.dni=e.dni AND e.codasigna=p_codasigna ORDER BY nomalumno; v_listado alumno.nomalumno%TYPE; v_denasigna asigna.denasigna%TYPE; BEGIN SELECT denasigna INTO v_denasigna FROM asigna WHERE codasigna=p_codasigna; DBMS_OUTPUT.PUT_LINE(v_denasigna); OPEN c_listado; FETCH c_listado INTO v_listado; WHILE c_listado%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_listado.nomalumno); FETCH c_listado INTO v_listado; END LOOP; CLOSE c_listado; END listadoasignaturaalumnos; PROCEDURE listadoalumnoasignaturas(p_dni alumno.dni%TYPE) IS CURSOR c_alumno IS SELECT denasigna, notaf FROM asigna a, estudia e WHERE a.codasigna=e.codasigna AND e.dni=p_dni ORDER BY 1; v_alumno c_alumno%ROWTYPE; v_nomalumno alumno.nomalumno%TYPE; BEGIN SELECT nomalumno INTO v_nomalumno FROM alumno WHERE dni=p_dni; DBMS_OUTPUT.PUT_LINE(v_nomalumno); OPEN c_alumno; FETCH c_alumno INTO v_alumno; WHILE c_alumno%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_alumno.denasigna|| ||v_alumno.notaf); FETCH c_alumno INTO v_alumno; END LOOP; CLOSE c_alumno; END listadoalumnoasignaturas; FUNCTION matricularalumnoasignatura (p_dni alumno.dni%TYPE, p_codasigna asigna.codasigna%TYPE) RETURN number IS CURSOR c_dni IS SELECT dni FROM alumno WHERE dni=p_dni; v_dni alumno.dni%TYPE; CURSOR c_codasigna IS SELECT codasigna FROM asigna WHERE codasigna=p_codasigna; 63

v_codasigna asigna.codasigna%TYPE; CURSOR c_existe IS SELECT dni, codasigna FROM estudia WHERE dni=p_dni AND codasigna=p_codasigna; v_existe c_existe%ROWTYPE ; BEGIN OPEN c_existe; FETCH c_existe INTO v_existe; IF c_existe%FOUND THEN RETURN 3; ELSE OPEN c_dni; FETCH c_dni INTO v_dni; IF c_dni%FOUND THEN OPEN c_codasigna; FETCH c_codasigna INTO v_codasigna; IF c_codasigna%FOUND THEN INSERT INTO estudia p_codasigna, null, null, null, null); RETURN 0; ELSE RETURN 2; END IF; CLOSE c_codasigna; ELSE RETURN 1; END IF; CLOSE c_dni; END IF; CLOSE c_existe; END matricularalumnoasignatura;

VALUES(p_dni,

PROCEDURE estadisticamatriculas (p_turno grupo.turno%TYPE) IS v_cuantos_alumnos number; BEGIN SELECT count(*) INTO v_cuantos_alumnos FROM alumno,grupo WHERE alumno.codgrupo=grupo.codgrupo AND curso=p_curso; DBMS_OUTPUT.PUT_LINE('En el curso '||p_curso||' hay '||v_cuantos_alumnos); END EstadisticaMatriculas; PROCEDURE EstadisticaMatriculas(p_turno asigna.curso%TYPE)IS v_cuantos_alumnos number; BEGIN SELECT count(*) INTO v_cuantos_alumnos FROM alumno,grupo WHERE alumno.codgrupo=grupo.codgrupo AND turno=p_turno; 64

DBMS_OUTPUT.PUT_LINE('En el turno '||p_turno||' hay '||v_cuantos_alumnos); END EstadisticaMatriculas; PROCEDURE listadoalumnos(p_codrama rama.codrama%TYPE) IS CURSOR c_alum IS SELECT turno, al.codgrupo codigogrupo, nomalumno FROM alumno al, grupo g WHERE al.codgrupo=g.codgrupo AND al.codrama=p_codrama ORDER BY 1,2,3; BEGIN FOR v_alum In c_alum LOOP DBMS_OUTPUT.PUT_LINE(v_alum.turno|| ||v_alum.codigogrupo|| ||v_alum.nomalum); END LOOP; END listadoalumnos; PROCEDURE listadoalumnos(p_codrama rama.codrama%TYPE, p_turno grupo.turno%TYPE) IS CURSOR c_alum IS SELECT al.codgrupo codigogrupo, nomalumno FROM alumno al, grupo g WHERE al.codgrupo=g.codgrupo AND al.codrama=p_codrama AND turno=p_turno ORDER BY 1,2; BEGIN FOR v_alum IN c_alum LOOP DBMS_OUTPUT.PUT_LINE(v_alum.codigogrupo|| ||v_alum.nomalumno); END LOOP; END listadoalumnos; END alumnopaquete; **************CORREGIR ERRORES********************* 35. Crear un trigger ControlAsignaturaRama que controle que un alumno slo se pueda matricular de asignaturas que sean de la rama en la que est matriculado. >CREATE OR REPLACE TRIGGER ControlAsignaturaRama BEFORE INSERT ON estudia FOR EACH ROW DECLARE v_rama_alumno rama.codrama%TYPE; v_rama_asignatura rama.codrama%TYPE; BEGIN SELECT codrama INTO v_rama_alumno FROM alumno 65

WHERE dni=:new.dni; SELECT codrama INTO v_rama_asignatura FROM asigna WHERE codasigna=:new.codasigna; IF v_rama_alumno<>v_rama_asignatura THEN RAISE_APPLICATION_ERROR(-20001,No se puede matricular un alumno en una asignatura que no sea de la rama en la que est matriculado... Meln!!); END IF; END ControlAsignaturaRama; 36. Crear un trigger ControlAsignaturaMatricula que controle que un alumno slo se pueda matricular de asignaturas de la rama en la que el est matriculado, y de asignaturas del curso anterior al que est matriculado. Adems, disear el trigger de manera que las constraints que haya definidas sobre la tabla estudia se accionen por si mismas y no sean interceptadas dentro del trigger. CREATE OR REPLACE TRIGGER ControlAsignaturaMatricula BEFORE INSERT ON estudia FOR EACH ROW DECLARE CURSOR c_rama IS SELECT codrama FROM alumno WHERE dni=:new.dni; CURSOR c_curso IS SELECT curso FROM grupo g, alumno al WHERE g.codgrupo=al.codgrupo AND dni=:new.dni; CURSOR c_asigna IS SELECT codrama, curso FROM asigna WHERE codasigna=:new.codasigna; v_rama c_rama%ROWTYPE; v_curso c_curso%ROWTYPE; v_asigna c_asigna%ROWTYPE; BEGIN OPEN c_asigna; FETCH c_asigna INTO v_asigna; OPEN c_rama; FETCH c_rama INTO v_rama; OPEN c_curso; FETCH c_curso INTO v_curso; IF c_curso%FOUND AND c_asigna%FOUND AND c_rama%FOUND THEN IF v_rama.codrama<> v_asigna.codrama THEN RAISE_APPLICATION_ERROR(-20002,'No se puede matricular a un alumno en un a asignatura que no es de su rama'); END IF; IF v_asigna.curso<>v_curso.curso AND v_asigna.curso<>v_curso.curso-1 THEN RAISE_APPLICATION_ERROR(-20003,'No se puede matricular a un alumo de una asignatura que no es de su curso o curso anterior'); END IF; END IF; CLOSE c_asigna; 66

CLOSE c_rama; CLOSE c_curso; END ControlAsignaturaMatricula; 37. Crear un trigger CaseClausus que controle que en un grupo no puede haber ms de 23 alumnos matriculados en CASE. CREATE OR REPLACE TRIGGER CaseClausus BEFORE INSERT ON estudia FOR EACH ROW WHEN(new.codasigna=CASE) DECLARE CURSOR c_grupo IS SELECT codgrupo from alumno WHERE dni=:new.dni; v_grupo c_grupo%ROWTYPE; v_case number; BEGIN OPEN c_grupo; FETCH c_grupo INTO v_grupo; IF c_grupo%FOUND THEN SELECT count(*) INTO v_case FROM estudia,alumno WHERE codasigna='CASE' and alumno.codgrupo=v_grupo.codgrupo AND estudia.dni=alumno.dni; IF v_case>=23 THEN RAISE_APPLICATION_ERROR(-20001,'No pueden haber ms de 23 alumnos matriculados en CASE'); END IF; END IF; CLOSE c_grupo; END CaseClausus; 38. Realizar el disparador AsignaturaClausus que controle que en cualquier asignatura, en un grupo no puede haber matriculados ms de 23 alumnos. CREATE OR REPLACE TRIGGER AsignaturaClausus BEFORE INSERT ON estudia FOR EACH ROW DECLARE CURSOR c_grupo IS SELECT codgrupo FROM alumno WHERE dni=:new.dni; CURSOR c_asigna IS SELECT codasigna FROM asigna WHERE codasigna=:new.codasigna; v_grupo c_grupo%ROWTYPE; v_asigna c_asigna%ROWTYPE; v_case number; BEGIN OPEN c_grupo; FETCH c_grupo INTO v_grupo; 67

OPEN c_asigna; FETCH c_asigna INTO v_asigna; IF c_grupo%FOUND THEN IF c_asigna%FOUND THEN SELECT count(*) INTO v_case FROM estudia,alumno WHERE codasigna=v_asigna.codasigna and alumno.codgrupo=v_grupo.codgrupo AND estudia.dni=alumno.dni; IF v_case>=23 THEN RAISE_APPLICATION_ERROR(-20001,'No pueden haber ms de 23 alumnos matriculados en '||v_asigna.codasigna|| en el mismo grupo); END IF; END IF; END IF; CLOSE c_grupo; CLOSE c_asigna; END AsignaturaClausus; 39. En la BD_PERSONAL implementar las siguientes restricciones semnticas que no han podido ser implementadas mediante constraints: - Un departamento no puede depender de si mismo. - Si un departamento A depende de un departamento B el departamento B no puede depender del A. - Un empleado puede ser jefe en propiedad de un nico departamento en cuyo caso sera el departamento al que est asignado y puede ser en funciones de varios departamentos - Los salarios, comisiones y nmero de hijos no pueden tener valores negativos. CREATE OR REPLACE TRIGGER ControlDependenciaDepto BEFORE INSERT ON depto FOR EACH ROW DECLARE CURSOR c_depto IS SELECT depde FROM depto WHERE codde=:new.depde; v_depto c_depto%ROWTYPE; BEGIN OPEN c_depto; FETCH c_depto INTO v_depto; IF c_depto%FOUND THEN IF :new.codde=:new.depde THEN RAISE_APPLICA TION_ERROR(-20001,Un depender de si mismo); END IF; IF :new.codde=v_depto.depde THEN RAISE_APPLICATION_ERROR(-20002,No dependencia entre departamentos); END IF; END IF;

departamento

no

puede

puede

haber

ciclos

de

68

CLOSE c_depto; END ControlDependenciaDepartamentos;

CREATE OR REPLACE TRIGGER ControlJefesDepto BEFORE INSERT ON DEPTO FOR EACH ROW WHEN (new.tidir=P) DECLARE CURSOR c_emple IS SELECT codde FROM emple WHERE codem=:new.codjefe; v_codde_empleado emple.codde%TYPE; BEGIN OPEN c_emple; FETCH c_emple INTO v_codde_empleado; IF c_emple%FOUND THEN IF v_codde_empleado<>:new.codde THEN RAISE_APPLICATION_ERROR(-20002,Un empleado no puede ser jefe en propiedad de un departamento al que no est asignado); END IF; END IF; CLOSE c_emple; END ControlJefesDepto; 40. En el procedimiento AadirAlumno crear una excepcin e_DemasiadosAlumnos que se generecuando en un grupo se intenta matricular a ms de 30 alumnos. CREATE OR REPLACE PROCEDURE AadirAlumno(p_dni alumno.dni%TYPE, p_nomalumno alumno.nomalumno%type, p_codrama rama.codrama%type, p_codgrupo grupo.codgrupo%type) IS v_cuantos number; e_DemasiadosAlumnos EXCEPTION; BEGIN SELECT count(*) INTO v_cuantos FROM alumnos WHERE codgrupo=p_codgrupo; IF v_cuantos>=30 THEN RAISE e_DemasiadosAlumnos; ELSE INSERT INTO alumno VALUES(p_dni, p_nomalumno, p_codrama, p_codgrupo); END IF; END;

69

You might also like