Académique Documents
Professionnel Documents
Culture Documents
Introduccin
Qu es el lenguaje PL/SQL y primeras explicaciones para saber cmo funciona este gestor PL/SQL. Nos encontramos ante un gestor que va incorporado en Oracle para solventar las grandes limitaciones que tenamos con los editores SQL, en cuanto al control de las secuencias de ejecucin de instrucciones, el uso de variables, la gestin de posibles errores, etc. Este lenguaje esta basado en ADA, por lo que incluye todas las caractersticas de los lenguajes de tercera generacin. Esto nos permite manejar las variables, tener una estructura modular (procedimientos y funciones) y controlar las excepciones. Adems incorpora un completo soporte para la programacin orientada a objetos (POO). Los programas creados con PL/SQL los podemos almacenar en nuestra base de datos como cualquier otro objeto quedando disponibles para los usuarios. El uso del lenguaje PL/SQL es imprescindible para construir disparadores de bases de datos (triggers). PL/SQL esta incluido en el servidor y en algunas herramientas de cliente. Soporta todos los comandos de consulta y manipulacin de datos, aportando al lenguaje SQL las estructuras de control y otros elementos propios de los lenguajes de programacin de 3 generacin. La unidad de trabajo en PL/SQL es el bloque, constituido por un conjunto de declaraciones, instrucciones y mecanismos de gestin de errores y excepciones.
Bloques
Con PL/SQL se pueden construir distintos tipos de programas: procedimientos, funciones y bloques annimos, paquetes, etc. Todos ellos tienen en comn una estructura bsica denominada bloque. Un bloque tiene 3 zonas: Zona de declaraciones: donde se declaran objectos locales. Suele ir precedida por la clusula declare (o is o as en los procedimientos y funciones). Un conjunto de instrucciones precedido por la clusula BEGIN Zona de excepciones: es una zona opcional precedida por la clusula EXCEPTION, donde se gestionan las excepciones. El formato genrico de un bloque es el siguiente:
[ DECLARE <declaraciones>] BEGIN <instrucciones> [EXCEPTION <gestin de excepciones>] END; Las nicas clusulas obligatorias son BEGIN y END Antes de hacer ningn bloque tenemos que ejecutar el siguiente comando en nuestra ventana de Sql *PLUS set serveroutput on; Aunque ahora no entendis mucho os dejo un ejemplo de un bloque para que os vayis familiarizando con la sintaxis. DECLARE v_num_empleados number(2); BEGIN insert into depart values(99,'provisional',null); update emple set dept_no=99 where dept_no=20; v_num_empleados:=SQL%ROWCOUNT; delete from depart where dept_no=20 DBMS_OUTPUT.PUT_LINE (v_num_empleados || 'Empleados cambiados a provisional'); Ya en prximos artculos nos meteremos de lleno en la construccin de bloque y en entender las lineas arriba escritas.
Estructuras de control
Las estructuras de control son bsicamente las mismas que podemos utilizar en cualquier lenguaje de programacin. La vamos a dividir en estructuras de control alternativas (IF) y estructuras de control repetitivas (WHILE, FOR, LOOP) La estructura del IF seria la siguiente: IF <condicin> THEN instrucciones ELSEIF <condicin> THEN instrucciones .... ELSE instrucciones END IF; La estructura del WHILE seria la siguiente: WHILE <condicin> LOOP instrucciones END LOOP; La estructura del FOR se escribira as: FOR <variable> IN <mnimo> <mximo> LOOP instrucciones END LOOP Si queremos que nos vaya contando al revs, es decir de 5 hasta 0 por ejemplo, la sintaxis seria la siguiente: FOR <variable> IN REVERSE <final>.......<inicio> LOOP instrucciones ..... END LOOP; Y la estructura del LOOP seria de esta forma: LOOP instrucciones .... EXIT WHEN <condicin> instrucciones
Cursores implicitos
Es importante saber que en nuestros bloques PL/SQL es bastante prctico el uso de cursores. En este lenguaje el resultado de una consulta no va directamente al terminal del usuario, sino que se guarda en un rea de memoria a la que se accede mediante los nombrados cursores. Para realizar una consulta en PL/SQL tenemos que guardar el resultado en cursores. Esto es muy sencillo y basta con meter un INTO en las consultas. Un ejemplo seria este: select <columna/s> INTO <variable/s> from <tabla> [where] select count(*) INTO vnum from ventas; La variable que sigue al INTO recibe el valor de la columna. Por este motivo es importante que el tipo de dato de la variable coincida con el tipo de dato de la columna.
Gestin de excepciones
Las excepciones sirven para tratar los errores y mensajes. Oracle tiene una serie de excepciones que son las ms frecuentes y con las que la mayora de la gente trabaja. Unas de las ms usadas son: NO_DATA_FOUND (cuando una orden tipo select no ha devuelto ningn valor) TOO_MANY_ROWS (cuando una orden tipo select ha devuelto mas de una fila) OTHERS THEN RAISE_APPLICATION_ERROR (para cualquier otro tipo de error desconocido) Un ejemplo seria el siguiente: DECLARE vapellido varchar(10); voficio varchar(20); BEGIN select apellido,oficio INTO vape,voficio from emple where emp=15; DBMS_OUTPUT.PUT_LINE (vape||: - || voficio); EXCEPTION WHEN NO_DATA_FOUND THEN insert into temp values('No hay datos'); WHEN TOO_MANY_ROWS THEN insert into temp values ('Demasiados datos'); WHEN OTHER THEN RAISE_APPLICATION_ERROR(-2000,'Error en aplicacin'); END;
Estructura modular
En PL/SQL podemos distinguir 3 tipos de programas o bloques. Bloques annimos: Son los que no tienen nombre y comienzan con el DECLARE, es decir los que hemos ido viendo hasta ahora. Procedimientos: Se trata del programa ms utilizado en PL/SQL y su formato es el siguiente: PROCEDURE <nombre_procedimiento> [(<lista de parmetros>)] IS [<declaraciones>] BEGIN <instrucciones>; [EXCEPTIONS <excepciones>;] END;
En el formato distinguimos dos partes claramente, la cabecera donde esta el nombre del procedimiento y los parmetros y el cuerpo donde se situ un bloque annimo. Funciones: similares a los procedimientos pero con la funcin aadida de que pueden devolver valores. Si subs varias lineas y veis el ejemplo de control de excepciones, podis ver que hemos utilizado un atributo como DBMS_OUTPUT. Bien pues esto lo que nos permite es visualizar en pantalla los resultados, tanto excepciones como mensajes. Lo utilizamos porque PL/SQL no dispone de ordenes o sentencias que capturen datos introducidos por teclado, ni tampoco para visualizar datos en pantalla. DBMS_OUTPUT.PUT_LINE nos permite visualizar en pantalla, pero para que funcione correctamente tenemos que poner el SET SERVEROUTPUT a ON Si queremos que un bloque nos pida algn dato tenemos que anteponer el smbolo & delante de la variable, de esta forma cuando el bloque llegue a ese punto nos pedir por pantalla el valor. Otra sentencia importante es la que nos permite visualizar los errores que hemos podido tener al crear el bloque o procedimiento. Esta sentencia es: show errors Podemos agregar comentarios a nuestros bloques anteponiendo al comentario /* <comentario> */ Si queremos que el bloque annimo se ejecute directamente cuando terminamos de crearlo debemos poner el smbolo / que, ademas de guardar el bloque en el buffer, lo ejecuta. Tambin podemos guardar los bloques annimos en ficheros para poderlos ejecutar posteriormente. Para ello ejecutamos la siguiente sentencia: save nombrefichero Y para ejecutarlo primero tenemos que cargar el fichero en el buffer y para ello tenemos que ejecutar la siguiente sentencia: get nombrefichero Una vez cargado el fichero ejecutamos el bloque con la sentencia run nombrefichero. O podemos hacer los dos pasos con una sola sentencia: start nombrefichero Sin embargo para los procedimientos es totalmente distinto ya que al tener nombre se almacena automticamente en la base de datos y para ejecutarlo tan solo tenemos que realizar la siguiente operacin: execute nombre_procedimiento(parmetros);
En el siguiente capitulo revisaremos todo lo que hemos visto en la introduccin del PL/SQL, pero de una forma mas exhaustiva.
DECLARE Vnom clientes.nombre%TYPE; BEGIN select nombre into Vnom from clientes where NIF= '&V_nif'; DBMS_OUTPUT.PUT_LINE (Vnom); END; . Como veis es bastante sencillo, pero no tienen tanta funcionalidad como los procedimientos o funciones.
Al tener almacenado el procedimiento en la base de datos, este puede ser llamado por cualquier usuario que tenga los permisos oportunos. Para invocar un procedimiento utilizamos la orden EXECUTE Para invocar al procedimiento que hemos creado antes tendramos que ejecutar la siguiente orden: EXECUTE ver_usuario('Luis'); Pero tambin podemos invocarlo desde un bloque PL/SQL de ls siguiente forma: BEGIN ver_usuario('Luis'); END; . Como en cualquier lenguaje, podemos agregar comentarios a nuestros procedimientos de la siguiente forma: - - para comentar en una sola linea /* <comentario>*/ para varias lineas.
Tipos de datos
Este lenguaje dispone de los mismo tipos de datos que podemos encontrar en SQL, pero adems se han incorporado algunos nuevos:
char(n): almacena una cantidad fija de caracteres varchar2(n): almacena cadenas de caracteres de longitudes variables long(n): almacena cadenas de longitud variable number(p,e): almacena numero de precisin p y escala e boolean: almacena valores lgicos, true, false o NULL date: almacena fechas completas, incluida la hora raw(n): almacena datos binarios de longitud fija long raw : almacena datos binarios de longitud variable rowid: almacena identificadores de direcciones de fila etc.
Adems es importante sealar que el programador puede definir sus propios tipos de datos a partir de los ya definidos.
Identificadores
Se utilizan para nombrar los objetos que intervienen en los programas PL/SQL como son las variables, constantes, cursores, excepciones, procedimientos, etc.
Pueden tener como mximo 30 caracteres empezando siempre por una letra, que puede ir seguida por otras letras, numeros, $, # _. Es importante destacar que PL/SQL no diferencia entre maysculas y minsculas. Tambin debemos sealar que no pueden contener espacios ni signos de puntuacin.
Variables
Como doy por sentado que todos sabemos lo que son las variables, pasaremos directamente a comentar como se declara una variable en PL/SQL.
No podemos indicar una lista de variables del mismo tipo y luego declarar el tipo, tenemos que hacerlo una a una. Uno ejemplo de declaracin de variables seria el siguiente: DECLARE importe NUMBER (8,2); contador NUMBER(2'0); nombre char(5) NOT NULL :="Sara"; ... Uso de los atributos %TYPE y %ROWTYPE %TYPE: declara una variable del mismo tipo que otra, o que una columna de una tabla %ROWTYPE : crea una variable registro cuyos campos se corresponden con las columnas de una tabla o vista. Por ejemplo si tenemos una variable definida previamente llamada cantidad podemos definir otra de la siguiente forma: total cantidad%TYPE; De esta forma la variable total tendr las mismas caractersticas que la variable cantidad. Otro ejemplo seria declarar una variable que fuera del mismo tipo que la columna nombre de la tabla profesor. nombre_alta nombre%ROWTYPE; mbito y visibilidad de variables La variable ser local para el bloque en el que ha sido declarada y global para los bloque hijos de ste, mientras que las variables declaradas en los bloque hijos no son globales a los bloques padre.
Constantes
Cmo en la mayora de los lenguajes, en este tambin podemos declaras constantes, de la siguiente forma:
Operadores
Asignacin :=
Lgicos
AND OR NOT
Concatenacin || Is null = != <> < > <= >= between...and like in y sus correspondientes negaciones + - * / **
Comparacin
Aritmticos
Funciones predefinidas
En PL/SQL tenemos las mismas funciones predefinidas que en SQL (AVG, MIN, MAX, COUNT, SUM, etc), pero tenemos que tener dos cosas muy claras a la hora de utilizarlas y son: 1. La funcin no modifica el valor de las variables o expresiones que se pasan como argumentos, sino que devuelve un valor a partir de dicho argumento. 2. Si a una funcin se le pasa un valor nulo en la llamada, posiblemente devolver un valor nulo.
Etiquetas
Podemos utilizar etiquetas para poder irnos a cualquier parte del programa utilizando la sentencia GOTO siempre y cuando se cumplan las siguientes reglas: No pueden haber etiquetas con los mismos nombres en un mismo programa. La etiqueta debe preceder a un bloque o a un conjunto de ordenes ejecutables la etiqueta no puede estar dentro de estructuras de control (IF, LOOP)
Subprogramas PL/SQL
Los subprogramas son los bloques PL/SQL con nombre que pueden recibir y devolver valores. En cualquier subprograma podemos distinguir:
La cabecera, compuesta por el nombre del subprograma, los parmetros y el tipo de valor de retorno. El cuerpo, compuesto por las declaraciones, las instrucciones y el manejo de excepciones.
Podemos distinguir entre dos tipos de subprogramas, como ya hemos comentado en artculos anteriores:
Procedimientos
Los procedimientos ya los hemos visto en el articulo Bloques annimos y procedimientos PL/SQL por lo que pasamos directamente a las funciones.
Funciones
Las funciones son muy similares a los procedimiento con la diferencia que stas siempre devolvern un valor. Su estructura es la siguiente:
CREATE [OR REPLACE] FUNCTION NombreFuncion [(parmetros)] RETURN tipo IS [parte declarativa] BEGIN instrucciones RETURN <valor o expresin>; [EXCEPTION excepciones] END; La clusula RETURN de la cabecera nos especifica el tipo de valor que nos va a devolver la funcin.
Parmetros
Todos los subprogramas utilizan parmetros para pasar y recibir informacin. Dentro de los parmetros podemos distinguir dos tipos:
Parmetros actuales: son variables indicadas en la llamada a un subprograma. Parmetros formales: son variables declaradas en la especificacin del subprograma.
Adems podemos hacer el paso de parmetros de un tipo a otro. Generalmente si los tipos son compatibles PL/SQL lo hace automticamente. En cualquier caso, podemos hacerlo de forma manual utilizando las siguientes notaciones:
Posicional: el compilador asocia los parmetros actuales a los formales, basndose en suposicin. Nominal: el smbolo => despus del parmetro actual y antes del nombre del formal, indica al compilador correspondencia. Mixta: nos permite usar las dos notaciones anteriores.
Para que esto quede ms claro pasamos a escribir un ejemplo de paso de parmetros y conversin de tipos.
Tenemos la especificacin de un procedimiento como esta: PROCEDURE departamento( n_departamento INTEGER, localidad VARCHAR2 IS... Desde el siguiente bloque se podrn realizar las llamadas indicadas: DECLARE num_departamento INTEGER; aula VARCHAR(30) BEGIN ... - - posicional departamento(num_departamento, aula); - - nominal departamento(num_departamento => n_departamento, aula =>localidad); ... END; Esto nos pasara los parmetros num_departamento al mismo tipo que n_departamento y localidad al mismo tipo que aula. Los parmetros que soporta PL/SQL pueden ser de entrada, salida o entrada/salida
Nos permite pasar valores a un subprograma. Dentro del subprograma, el parmetro actu como una constante. Puede ser una variable, constante, literal o expresin.
IN
OUT Permite devolver valores al bloque que llam al subprograma. Dentro del subprograma, el parmetro acta como una variable no inicializada. Solo puede
ser una variable. Permite pasar un valor inicial y devolver un valor actualizado. Dentro del IN subprograma, el parmetro actu como variable inicializada. Puede intervenir OUT otras expresiones. El valor actual debe ser una variable.
<nombrevariable> [IN | OUT | IN OUT] <tipodato> [ { := | DEFAULT} <valor>] Adems es importante recordar que al especificar parmetros debemos indicar el tipo, pero no el tamao.
Para volver a compilar un subprograma almacenado utilizaremos la orden ALTER en vez del CREATE y su formato es el siguiente: ALTER {PROCEDURE | FUNCITON} nombresubprograma COMPILE; Para ver el cdigo de un subprograma almacenado podemos ejecutar una sentencia como esta; select LINE, SUBSTR(TEXT,1,60) from USER_SOURCE where name = 'nombresubprograma'; Para borrar un subprograma almacenado utilizaremos la orden DROP de la siguiente forma: DROP {PROCEDURE | FUNCTION} nombresubprograma;
Nota: PL/SQL implementa la recursividad en los subprogramas, esto quiere decir, que un programa puede llamarse a si mismo.
Hasta ahora hemos utilizado cursores implcitos, cuando devolvamos el resultado de una select mediante la clausula into a una variable. Pero esto es un problema cuando el resultado de una subconsulta nos devolva varias filas, porque esto nos daria un error al ejecutar la consulta Para que no nos salte un error en estos casos debemos utilizar los cursores explcitos.
Cursores explcitos
Los cursores explcitos los utilizamos cuando tenemos consultas que nos devuelven ms de una fila. Tenemos 4 operaciones bsicas para trabajar con un cursor explcito. 1. Declaracin del cursor: lo tenemos que declarar en la zona de declaraciones, con el siguiente formato: CURSOR <nombrecursor> IS <sentencia SELECT>; 2. Apertura del cursor: Deber colocarse en la zona de instrucciones, con el siguiente formato: OPEN <nombrecursor>; Al hacerlo se ejecuta automticamente la sentencia select y sus resultados se almacenan en las estructuras internas de memoria manejadas por el cursor. 3. Recogida de informacin: Para recuperar la informacin anteriormente guardada en las estructuras de memoria interna tenemos que usar el siguiente formato: FETCH <nombrecursor> INTO {<variable> | <listavariables>}; Si tenemos una nica variable que recoge los datos de todas las columnas, el formato de la variable seria el siguiente: <variable> <nombrecursor>%ROWTYPE; Si tenemos una lista de variables, cada una recoger la columna correspondiente de la clusula select, por lo que sern del mismo tipo que las columnas. 4. - Cierre del cursor: CLOSE <nombrecursor>; Ahora, veamos un ejemplo de utilizacin de cursores explcitos:
Vnom VARCHAR2(12); Vape VARCHAR2(20); BEGIN OPEN C1; LOOP FETCH C1 INTO Vnom, Vape; EXIT WHEN C1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(Vnom || '' || Vapen); END LOOP; CLOSE C1; END; Si nos fijamos, en la declaracin de los cursores explcitos no utilizamos la clusula INTO, que s se utilizaba en los cursores implcitos. Ademas podis ver que despus del FETCH hemos comprobado si nos devuelve valores con la linea del EXIT. Es algo importante ya que si no nos devuelve nada el LOOP se interrumpir.
%FOUND: devuelve verdadero di el ultimo FETCH ha recuperado algn valor; en caso contrario devuelve falso; si el cursor no esta abierto nos devuelve error. %NOTFOUND: hace justamente lo contrario al anterior. %ROWCOUNT: nos devuelve el nmero de filas recuperadas hasta el momento. %ISOPEN: devuelve verdadero si el cursor esta abierto.
Veamos ahora un ejemplo de utilizacin de %ROWCOUNT: DECLARE CURSOR C1 IS SELECT nombre from futbolista WHERE Cod='e1'; Vnom VARCHAR2(15); BEGIN OPEN C1; LOOP FETCH C1 INTO Vnom; EXIT WHEN C1%NOTFOUND; DBMS_OUTPUT.PUT_LINE (C1%ROWCOUNT || Vnom); END LOOP; CLOSE C1; END;
En el ejemplo siguiente podemos observar que en la clusula WHERE se incluye una variable que se debera haber declarado previamente. Este tipo de variables reciben el nombre de variables de acoplamiento. El programa la sustituir por su valor en el momento en que se abre el cursor, y se seleccionarn las filas segn dicho valor. Aunque ese valor cambie durante la recuperacin de los datos con FETCH, el conjunto de filas que contiene el cursor no variar.
El ejemplo nos muestra los futbolistas de un equipo cualquiera. CREATE OR REPLACE PROCEDURE ver_futbolistas_por_equipos(codeq VARCHAR2) IS Vequi VARCHAR2(3); CURSOR C1 IS SELECT nombre from futbolista where codeq=Vequi; Vnom VARCHAR(15); BEGIN vequi:=codeq; OPEN C1; FETCH C1 INTO vnom; WHILE C1%FOUND LOOP DBMS_OUTPUT.PUT_LINE(Vnom); FETCH C1 INTO Vnom; END LOOP; CLOSE C1; END;
Variables de acoplamiento
Si os fijis en el siguiente ejemplo veris que en la clusula where se incluye una variable que se deber declarar previamente. Este tipo de variables recibe el nombre de variables de acoplamiento. El programa la sustituir por su valor en el momento en que se abre el cursor, y se seleccionarn las filas segn dicho valor.
Create or replace procedure ver_jugador(codeq varchar2) IS vequi varchar2(3); cursor c1 is select nombre from jugador where cod=vequi; vnom varchar2(15); BEGIN vequi:=codeq; OPEN c1; FETCH c1 INTO vnom; WHILE c1%found LOOP DBMS_OUTPUT.PUT_LINE(vnom); FETCH c1 INTO vnom; END LOOP; CLOSE c1; END;
Para resumir todas esas tareas, tenemos una estructura cursor FOR...LOOP que hace todas estas cosas de forma implcita, todas menos la declaracin del cursor. El formato y el uso de esta estructura es:
1. se declara la informacin cursor en la seccin correspondiente 2. Se presenta el cursor utilizando el siguiente formato: FOR nombreVarReg IN nombreCursor LOOP . END LOOP; Al entrar en el bucle se abre el cursor de manera automtica, se declara implcitamente la variable nombreVarReg de tipo nombrecursor%ROWTYPE y se ejecuta el primer fetch cuyo resultado quedarn en nombreVarReg. A continuacin se realizaran las acciones que correspondas hasta llegar al END LOOP. Este es un ejemplo del LOOP . END LOOP:
DECLARE cursor c2 is select nombre, peso, estatura from jugador where salario>1200; BEGIN FOR vreg IN c2 LOOP DBMS_OUTPUT.PUT_LINE (vreg.nombre || '-' ||vreg.peso || '-' || vreg.estatura); END LOOP; END;
CURSOR nombrecursor [(parmetros)] IS SELECT <sentencia select en la que intervendrn los parmetros>; Los parmetros formales indicados despus del nombre del cursor tienen la siguiente sintaxis: nombreCursor [IN] tipodato [{:=|DEFAULT} valor] Todos los parmetros formales de un cursor son parmetros de entrada y su mbito es local al cursor por eso slo pueden ser referenciados dentro de la consulta. Un ejemplo seria el siguiente: DECLARE ... CURSOR C1 (vpeso number, vestatura number DEFAULT 170) is select nficha, nombre FROM emple WHERE estatura=vestatura AND peso=vpeso; Para abrir un cursor con parmetros lo haremos de la siguiente forma: OPEN nombrecursor [(parmetros)];
SQL%NOTFOUND: nos dice si el ltimo insert, update,delete o select into no han afectado a ninguna fila. SQL%FOUND: nos dice si el ltimo insert, update,delete o select into ha afectado a una o mas filas SQL%ROWCOUNT: devuelve el nmero de filas afectadas por el ltimo insert, update, delete o select into SQL%ISOPEN: Nos dice si el cursor esta cerrado, por lo que en teora siempre nos dar falso ya que Oracle cierra automticamente el cursor despus de cada orden SQL.
Si se trata de un select into tenemos que tener en cuenta que solo puede devolver una nica fila de lo contrario nos levantar automticamente una de estas dos excepciones:
NO_DATA_FOUND: si la consulta no devuelve ninguna fila TOO_MANY_ROWS: si la consulta devuelve ms de una fila Cuando un select into hace referencia a una funcin de grupo nuca se levantar la excepcin NO_DATA_FOUND y SQL%FOUND siempre ser verdadero. Esto se explica porque las funciones de grupo siempre devuelven algn valor (NULL se considera un valor).
CURSOR nombre_cursor <declaraciones> FOR UPDATE Esto indica que las filas seleccionadas por el cursor van a ser actualizadas o borradas. Una vez declarado un cursor FOR UPDATE, se incluir el especificador CURRENT OF nombre_cursor en la clusula WHERE para actualizar o borrar la ltima fila recuperada mediante la orden FETCH.
{UPDATE|DELETE}... WHERE CURRENT OF nombre_cursor. Os pongo un ejemplo para que quede claro: Subir el salario a todos los empleados del departamento indicado en la llamada. El porcentaje se indicar tambin en la llamada. CREATE OR REPLACE PROCEDURE subir_salario (num_dept NUMBER, incre NUMBER) IS CURSOR c IS SELECT oficio, salario FROM empleados WHERE cod_dept=num_dept FOR UPDATE; reg c%ROWTYPE; inc NUMBER (8); BEGIN OPEN c; FETCH c INTO reg; WHILE c%FOUND LOOP inc :=(reg.salario/100 )* inc; UPDATE empleados SET salario=salario+inc WHERE CURRENT OF c FETCH c INTO reg; END LOOP; END; Tambin podemos usar ROWID en lugar de FOR UPDATE. ROWID nos indicar la fila que se va a actualizar. Para ello, al declarar el cursor en la clusula SELECT indicaremos que seleccione tambin el identificador de fila: CURSOR nombre_cursor IS SELECT columna1,columna2,...ROWID FROM tabla; Al ejecutarse el FETCH se guardar el nmero de fila en una variable y despus ese nmero se podr usar en la clusula WHERE de la actualizacin: {UPDATE |DELETE } ... WHERE ROWID = variable_rowid El ejemplo anterior utilizando ROWID quedara de la siguiente manera: CREATE OR REPLACE PROCEDURE subir_salario (num_dept NUMBER, incre NUMBER) IS CURSOR c IS SELECT oficio, salario,ROWID FROM empleados WHERE cod_dept=num_dept FOR UPDATE; reg c%ROWTYPE; inc NUMBER (8);
BEGIN OPEN c; FETCH c INTO reg; WHILE c%FOUND LOOP inc :=(reg.salario/100 )* inc; UPDATE empleados SET salario=salario+inc WHERE ROWID = reg.ROWID; FETCH c INTO reg; END LOOP; END; Con este artculo damos por terminado todo lo referente a cursores y empezamos a tratar las excepciones en el siguiente artculo.
Por ltimo, de paso que vemos cosas acerca del tratamiento de errores en PL/SQL, explicaremos el RAISE_APPLICATION_ERROR, un componente del sistema gestor de base de datos Oracle que ayuda a gestionar errores y sus mensajes de error.
El formato sera el siguiente: BEGIN ......... ...... ...... EXCEPTION WHEN <nombre_excepcin> THEN <instrucciones>; ...... [WHEN OTHERS THEN <instrucciones>;] END;
Excepciones predefinidas
Son aquellas que se disparan automticamente al producirse determinados errores. Estas son las ms comunes:
too_many_rows: Se produce cuando select into devuelve ms de una fila. no_data_found: se produce cuando un select . into no devuelve ninguna fila. login_denied: error cuando intentamos conectarnos a Oracle con un login y clave no validos.
not_logged_on: se produce cuando intentamos acceder a la base de datos sin estar conectados. program_error: se produce cuando hay un problema interno en la ejecucin del programa. value_error: se produce cuando hay un error aritmtico o de conversin. zero_divide: se puede cuando hay una divisin entre 0. dupval_on_index: se crea cuando se intenta almacenar un valor que creara duplicados en la clave primaria o en una columna con restriccin UNIQUE. invalid_number: se produce cuando se intenta convertir una cadena a un valor numrico. Hay alguna ms pero estas son las ms utilizadas y tenemos que tener en cuenta que no es necesario declararlas en la seccin DECLARE.
DECLARE ... Importe_mal EXCEPTION; ... BEGIN ... IF precio NOT BETWEEN mnimo and mximo THEN RAISE importe_mal; END IF; ... EXCEPTION WHEN importe_mal THEN DBMS_OUTPUT.PUT_LINE("Importe incorrecto"); ... END;
Otras excepciones
Existen otros errores internos de Oracle que no tienen asignada una excepcin, sino un cdigo de error y un mensaje, a los que se accede mediante funciones SQLCODE y SQLERRM. Cuando se produce un error de estos se trasfiere directamente el control a la seccin EXCEPTION donde se tratara el error en la clausula WHEN OTHERS de la siguiente forma:
Utilizacin de RAISE_APPLICATION_ERROR
En el paquete DBMS_STANDARD se incluye un procedimiento llamado RAISE_APPLICATION_ERROR que nos sirve para levantar errores y definir mensajes de error. Su formato es el siguiente:
RAISE_APPLICATION_ERROR(numero_error,mensaje_error); Es importante saber que el numero de error esta comprendido entre -20000 y -20999 y el mensaje es una cadena de caracteres de hasta 512 bytes. Este procedimiento crea una excepcin que solo puede ser tratada en WHEN OTHERS. Ponemos un ejemplo para que nos quede ms claro. CREATE or REPLACE PROCEDURE subir_horas (emple NUMBER, horas_subir NUMBER) IS horas_actuales NUMBER; BEGIN Select horas into horas_actuales from empleados where id_empleado=emple; if horas_actuales is NULL then RAISE_APPLICATION_ERROR(-20010,'No tiene horas'); else update empleados set horas=horas_actuales + horas_subir where id_empleado=emple; end if; End subir_horas;
Control de transacciones
Control de transacciones en Oracle. Una transaccin se define como un conjunto de operaciones sobre la base de datos. En Oracle si se ejecuta un conjunto de operaciones y una de ellas falla se aborta la transaccin entera. En este artculo veremos todo lo que debemos saber sobre transacciones y algunos ejemplos interesantes. Est englogado dentro del Manual de Oracle que venimos publicando en DesarrolloWeb.com. La transaccin finaliza cuando se ejecuta un comando de control de transacciones como rollback o commit work (se puede abreviar poniendo simplemente commit). Un ejemplo: BEGIN .... update alumnos set edad=20 where n_alumno=109; update nuevos set apellido='perez' where n_alumno=200; commit work; ... EXCEPTION WHEN OTHERS THEN rollback work; END;
savepoint numero; Rollback implicito Este comando se ejecuta cuando un programa almacenado (procedimiento o funcin) falla y no se controla la excepcin que produjo el fallo. Pero si en el programa tenemos un commit estos cambios no sern deshechos. Rollback to Deshace el trabajo realizado despus del punto indicado. Pero no se confirma el trabajo hecho hasta el savepoint. La transaccin no finaliza hasta que se ejecuta un comando de control de transacciones o hasta que finaliza la sesin. Os dejo a continuacin un ejemplo bastante completo de lo que seria el control de transacciones: create or replace procedure prueba (nfilas number) as begin savepoint ninguna; insert into tmp values ('primera fila'); savepoint una; insert into tmp values ('segunda fila'); savepoint dos; if nfilas=1 then rollback to una; else if nfilas=2 then rollback to dos; else rollback to ninguna; end if; commit; exception when other then rollback end prueba; Con este artculo terminamos la parte bsica sobre oracle, PL/SQL y pasamos a lo que podemos denominar programacin avanzada de sql. Empezaremos con triggers en el siguiente artculo.
En el presente artculo vamos a estudiar acerca de los triggers, donde veremos qu son y como se construyen, comenzando con los trigger de tablas y ms tarde veremos los trigger de sustitucin y los de sistema. Para ello lo primero que tenemos que ver es su definicin.
Trigger a tablas
Un trigger es un bloque de cdigo PL/SQL que se almacenan en la base de datos. Los bloques de cdigo de los triggers estn asociados a una tabla y se ejecutan automticamente cuando se producen ciertos eventos asociados a la tabla.
Se suelen utilizar para prevenir transacciones errneas y nos sirven tambin para implementar restricciones de integridad o seguridad. Su formato bsico es el siguiente: create or replace trigger nombre_trigger {before | after} {delete | insert | update[of lista_columnas]} [or {before | after} {delete|insert|update [of lista_columnas]}] on nombre_tabla [for each {row | statement | when (condicin)}] /* comienza el trigger */ [declare] <declaraciones> begin <instrucciones> [exception] <excepciones> end;
Elementos de un trigger
before / after: elemento que dispara el trigger nombre: nombre del trigger que tiene que ser nico. for each: nivel del disparo del trigger que por defecto es statement que significa que se dispara
una sola vez por cada operacin independientemente del nmero de filas afectadas. for each row: salta por cada fila afectada.
Variables posibles para update: la primera es :old que hace referencia a los valores anteriores y :new que hace referencia a los nuevos valores de actualizacin de la fila. Tenemos que tener en cuanta unos cuantos aspectos:
Cuando el evento que dispara el trigger es un delete haremos referencia al valor :old porque el valor :new es nulo Cuando el evento que dispara el trigger es un insert haremos referencia al valor :new porque el :old es nulo. Cuando el evento es un update tiene sentido hacer referencia a los dos valores. Slo se pueden utilizar cuando el trigger es a nivel de fila (for each row).
Vamos a a crear un trigger que se dispar automticamente despus de la modificacin del salario de la tabla empleado y pase un comentario a la tabla auditar.
Create or replace trigger auditar_salario after update of salario on empleado for each row begin insert into auditar values ('se ha modificado el salario' || :old.num_empleado); end;
Ejemplo:
create or replace trigger nombre_trigger before insert or delete begin if insert then ..... elseif deleting then .... elseif updating then ... end if .... end; En el siguiente articulo veremos los triggers de sustitucin y los del sistema.
Disparadores de sustitucin
Podemos crear triggers que no se ejecutan antes ni despus de una instruccin sino en lugar de (instead of). Solo podemos utilizar estos triggers si estn asociados a vistas, adems actan siempre a nivel de fila. La sintaxis de este tipo de trigger es la siguiente: create [or replace] trigger nombre_trigger instead of { insert | delete | update [of columnas]} [ or { insert | delete | update}] on nombre vista [ for each row] [declare] declaraciones begin instrucciones [execption] excepciones end; Sobre una vista podemos hacer un select pero si hacemos un insert, delete o update puede darnos problemas y no dejar ejecutarse la orden correctamente. Los trigger sobre vistas vas a sustituir a estas operaciones por las correspondientes en las tablas que forman la vista. Un ejemplo de trigger de sustitucin seria el siguiente: create or replace trigger t_borrado_emp instead of delete on empleado for each row begin delete from emple where emp_num=:old.cod end;
Disparadores de sistema
Estos trigger se disparan cuando se arranca o para la base de datos, entra o sale un usuario, cuando se crea, modifica o elimina un objeto, etc.
En Oracle para crear este tipo de trigger tenemos que tener privilegios de Administer database trigger. La sintaxis de este trigger seria la siguiente: create [or replace] trigger nombre_trigger { before | after } { <lista eventos de definicin> | <lista eventos del sistema>} on { database | schema} [when (condicin)] <cuerpo del trigger (bloque PL/SQL)> Donde la lista de eventos de definicin puede tener uno o ms eventos DDL separados por or y la lista de eventos del sistema igualmente separados por or. Al asociar un disparador a un evento debemos indicar el momento en que se dispare. A continuacin os dejo una tabla de evento, momento y cuando se disparara para dejarlo todo mas o menos claro. Evento STARTUP SHUTDOWN LOGON LOGOFF Momento AFTER BEFORE AFTER BEFORE BEFORE | AFTER BEFORE | AFTER BEFORE | AFTER BEFORE | AFTER BEFORE | AFTER BEFORE | AFTER BEFORE | AFTER Se disparan: Despus de arrancar la instancia Antes de apagar la istancia Despus de que el usuario se conecte a la base de datos. Antes de la desconexin de un usuario Cuando ocurre un error en el servidor Antes o despus de crear un objeto en el esquema Antes o despus de borrar un objeto en el esquema Antes o despus de cambiar un objeto en el esquema Antes o despus de ejecutar un comando truncate Antes o despus de ejecutar un comando grant Antes o despus de ejecutar un comando revoke Antes o despus de ejecutar cualquier comando de definicin de datos
Oracle tiene algunas funciones que permiten acceder a los atributos del evento del disparo ORA_YSEVENT, ORA_LOGIN, etc. Estas funciones pueden usarse en la clausula WHEN
o en el cuerpo del disparador. En el manual de Oracle podis encontrar el listado de todas estas funciones. Un ejemplo seria un trigger que nos guarda los datos de un usuario al hacer login en la base de datos: create or replace trigger control after logon on database begin insert into control_conexion (usuario, momento, evento) values {ORA_LOGIN_USER, SYSTIMESTAMP, ORA_SYSEVENT); end;