Вы находитесь на странице: 1из 9

Curso de PL/SQL

1.INTRODUCCION PL/SQL (Procedural Language Programming Language, segn distintas fuentes, aunque la oficial es la primera) es el lenguaje utilizado por Oracle como extensin al SQL, y que nos va a permitir introducir los elementos existentes en los dems lenguajes de programacin y dirigir el flujo de un programa mediante bloques de cdigo. El elemento principal del PL/SQL es el bloque, que como acabamos de decir contendr sentencias condicionales, bucles, llamadas a procedimientos y/o funciones, etc... Enlaces relacionados: http://otn.oracle.com/tech/pl_sql/index.html

2.BLOQUES Veremos que hay dos tipos de bloques (annimos y nombrados) y que estos se utilizan en todas las construcciones PL/SQL: Bloques annimos, Disparadores de aplicacin (para Forms, Reports, Graphics, y las herramientas de desarrollo nativas de Oracle), Disparadores de Base de datos, Procedimientos o funciones almacenada en la Base de datos, Procedimientos o funciones de aplicacin (para Forms, Reports, Graphics, y las herramientas de desarrollo nativas de Oracle) y Paquetes de Base de datos (dejaremos aparte los tipos de objetos, ya que no los abordaremos en este curso, slo recordar que Oracle 8 es una base de datos orientada a objetos y estos forman parte de la BD). Cada estructura PL/SQL contiene uno o ms bloques. Los bloques pueden estar separados uno tras otro o anidados unos dentro de otros. 2.1.- BLOQUES ANONIMOS Los bloques annimos son, como su nombre indica, bloques sin nombre. Se ejecutan normalmente como scripts independientes o dentro de una aplicacin. La estructura es la siguiente: DECLARE (opcional) Definicin de variables, tipos y objetos a utilizar en el bloque. BEGIN Sentencias ejecutables EXCEPTION (opcional) Acciones a realizar si ocurre algn error en las sentencias ejecutables. END ; Los elementos BEGIN y END son obligatorios e incluyen las sentencias a ejecutar. La seccin DECLARE es opcional y nos servir para declarar las variables, constantes, cursores y excepciones definidas por el usuario que vamos a usar dentro del bloque. La seccin EXCEPTION es tambin opcional y es la encargada de atrapar los errores, en caso de que se produzcan y realizar las acciones oportunas segn el tipo de excepcin. Esta seccin debe preceder inmediatamente a END. Como podis observar el punto y coma va solamente en el END ya que se considera al bloque como un todo, pero

Pg 1 de 9

Curso de PL/SQL
dentro de la seccin BEGIN, las instrucciones deben ir siempre terminadas por el punto y coma. Como siempre, vamos a ver un ejemplo para aclarar las cosas: DECLARE v_nombre VARCHAR2(30); BEGIN SELECT nombre INTO v_nombre FROM EMPLEADOS WHERE codigo=1 ; END; Veamos: en la seccin DECLARE establecemos la variable v_nombre del mismo tipo y longitud que el campo nombre de la tabla EMPLEADOS para recoger ese dato en el programa. Despus del BEGIN ejecutamos una sentencia SELECT con la particularidad de la clausula INTO que provoca que el campo nombre se guarde en la variable v_nombre. En este caso buscamos el nombre del empleado cuyo cdigo es el 1. Finalmente, terminamos el bloque con END; Vale, todo correcto, pero supongamos que la sentencia SELECT no recupera ningn registro porque no hay registros grabados, o porque no existe el empleado 1, o simplemente porque se produce un error de sistema. Para eso est la seccin EXCEPTION: DECLARE v_nombre VARCHAR2(30); v_error VARCHAR2(30); BEGIN SELECT nombre INTO v_nombre FROM EMPLEADOS WHERE codigo=1 ; EXCEPTION WHEN NO_DATA_FOUND THEN v_error := 'Registro no encontrado.'; WHEN OTHERS THEN v_error := 'Error inesperado.'; END; Si no se encuentra ningn registro que cumpla las condiciones de la sentencia SELECT, se lanza una excepcin NO_DATA_FOUND y la seccin EXCEPTION la capturar, almacenando en la variable v_error el mensaje apropiado. Otra cosa ser lo que hagamos con ello despus. Si se produce cualquier otra excepcin, se capturar por WHEN OTHERS que recoge cualquier excepcin que no se haya especificado antes, por lo que es muy conveniente ponerla siempre. La gestin de excepciones la iremos viendo a medida que avancemos ya que es bastante ilustrativo hacerlo as. Considero que dedicar un captulo a las excepciones puede llegar a ser intil si las vamos explicando paso a paso. Importante indicar que una SELECT dentro de un bloque PL/SQL solo puede devolver un registro. En este caso no nos hemos preocupado del asunto pues el campo codigo es clave primaria de la tabla EMPLEADOS.

Pg 2 de 9

Curso de PL/SQL
2.2.- BLOQUES NOMBRADOS (SUBPROGRAMAS) Los subprogramas son bloques nombrados que pueden tener parmetros y ser llamados por otros bloques. PL/SQL dispone de dos tipos: procedimientos y funciones. Ambos pueden residir en la aplicacin o en la propia base de datos. Su estructura es la siguiente: Cabecera IS Definicin de variables, tipos y objetos a utilizar en el bloque. BEGIN Sentencias ejecutables EXCEPTION (opcional) Acciones a realizar si ocurre algn error en las sentencias ejecutables. END ; Como podis la diferencia con los bloques annimos est en la seccin de declaraciones. Aqu no se usa DECLARE y sin embargo tenemos una Cabecera que consta de: Si se trata de un procedimiento: PROCEDURE nombre_procedimiento(parametros) Si se trata de una funcin: FUNCTION nombre_funcion(parametros) RETURN tipo Los parmetros son opcionales y pueden ser de entrada, de salida o de entrada y salida al mismo tiempo. En el caso de las funciones, el tipo devuelto especifica el tipo de dato que devuelve la funcin mediante la sentencia RETURN expresin. Que se especifica en la seccin ejecutable y en la de EXCEPTION. Es muy importante recordar que una funcin SIEMPRE debe devolver un valor, por lo que hay que tener especial atencin a los errores que se puedan generar, capturarlos y devolver un valor incluso en caso de error. Como siempre, unos ejemplos: PROCEDIMIENTO: PROCEDURE nombre_empleado(P_CODIGO IN NUMBER) IS v_nombre VARCHAR2(30); v_error VARCHAR2(30); BEGIN SELECT nombre INTO v_nombre FROM EMPLEADOS WHERE codigo=P_CODIGO ; EXCEPTION WHEN NO_DATA_FOUND THEN v_error := 'Registro no encontrado.'; WHEN OTHERS THEN v_error := 'Error inesperado.'; END;

Pg 3 de 9

Curso de PL/SQL
Vemos la seccin de cabecera con el nombre del procedimiento y el parmetro pasado (IN = de entrada). Claro, ahora ms de uno dir: y para qu sirve este procedimiento si el nombre del empleado que recuperamos, se queda en la variable v_nombre. Cierto, pero tranquilos, vamos a ver los parmetros de salida y resolvemos el problema. De nuevo escribimos nuestro procedimiento:

PROCEDURE nombre_empleado(P_CODIGO IN NUMBER, P_NOMBRE OUT VARCHAR2) IS v_error VARCHAR2(30); BEGIN P_NOMBRE := NULL; SELECT nombre INTO P_NOMBRE FROM EMPLEADOS WHERE codigo=P_CODIGO ; EXCEPTION WHEN NO_DATA_FOUND THEN v_error := 'Registro no encontrado.'; WHEN OTHERS THEN v_error := 'Error inesperado.'; END; Ahora en el parmetro P_NOMBRE saldr (OUT) el nombre del empleado que hemos recuperado. Como se puede ver, he eliminado la variable v_nombre de la seccin de declaraciones ya que ahora no nos hace falta, y en la SELECT INTO metemos el valor directamente en el parmetro de salida P_NOMBRE. Tambin vemos que justo antes de la SELECT asignamos el valor NULL al parmetro. Esto no es obligatorio, pero es buena costumbre ya que as, en caso de error, el parmetro P_NOMBRE contendr un valor: el valor NULL. FUNCION: El mismo ejemplo pero con una funcin: FUNCTION nombre_empleado(P_CODIGO IN NUMBER) RETURN VARCHAR2 IS v_nombre VARCHAR2(30); v_error VARCHAR2(30); BEGIN SELECT nombre INTO v_nombre FROM EMPLEADOS WHERE codigo=P_CODIGO ; RETURN v_nombre; EXCEPTION WHEN NO_DATA_FOUND THEN v_error := 'Registro no encontrado.'; RETURN v_error ; WHEN OTHERS THEN v_error := 'Error inesperado.'; RETURN v_error ; END; Por supuesto, una funcin puede devolver ms de un valor haciendo uso de los parmetros de salida (OUT).

Pg 4 de 9

Curso de PL/SQL
Observar que aunque se produzca un error hacemos que la funcin devuelva un valor. Esto lo hacemos as ya que cuando se produce una excepcin dentro de un bloque, se ejecuta el cdigo del manejador de la excepcin y se finaliza la ejecucin del cdigo del bloque en cuestin. Es decir, en el caso anterior, si se produce un error, la funcin devolver el valor de v_error y no el de v_nombre ya la sentencia RETURN v_nombre no se llega a ejecutar. Hemos dicho que los bloques nombrados, ya sean procedimientos o funciones pueden estar almacenados en la aplicacin o en la base de datos. Durante el curso nos ceiremos al caso de almacenarlos en la base de datos ya que escapa a este documento el entrar a explicar las herramientas de desarrollo nativas de Oracle (Developer, Discover, etc...). En este sentido, podemos utilizar para almacenar los procedimientos o funciones en la base de datos, las aplicaciones suministradas por Oracle (Procedure Builder) o utilizar aplicaciones de terceros (SQL Navigator, Toad, etc..), pero insisto, vamos a suponer que solo tenemos un editor de textos y la herramienta SQL*Plus para poder ejecutar sentencias o scripts SQL. Dicho esto, ahora os preguntareis: como almaceno anterior en la base de datos entonces?. Vamos a verlo: la funcin

CREATE OR REPLACE FUNCTION nombre_empleado(P_CODIGO IN NUMBER) RETURN VARCHAR2 IS v_nombre VARCHAR2(30); v_error VARCHAR2(30); BEGIN SELECT nombre INTO v_nombre FROM EMPLEADOS WHERE codigo=P_CODIGO ; RETURN v_nombre; EXCEPTION WHEN NO_DATA_FOUND THEN v_error := 'Registro no encontrado.'; RETURN v_error ; WHEN OTHERS THEN v_error := 'Error inesperado.'; RETURN v_error ; END; / As de sencillo. Ponemos delante CREATE OR REPLACE para crearla o sustituirla en caso de que ya exista y de esta forma queda almacenada en la base de datos. Fcil no?. Con un procedimiento lo haramos igual. La clausula OR REPLACE es opcional como habris deducido ya. El lector atento se habr dado cuenta de la barra / al final. S, no se trata de un error. Es para que SQL*Plus ejecute el cdigo directamente. No voy a tratar el SQL*Plus ya que tampoco es necesario para aprender PL/SQL, aunque es la herramienta bsica, al final la mayora usamos otras herramientas ms avanzadas (SQL*Plus es una consola de SQL y poco ms). Y cmo borro lo que acabo de almacenar en la base de datos?. La sentencia para borrar un procedimiento almacenado en la base de datos es:

Pg 5 de 9

Curso de PL/SQL
DROP PROCEDURE nombre_procedimiento ; Y,de forma anloga, para borrar una funcin almacenada: DROP FUNCTION nombre_funcion ; Es importante recordar que no hay posibilidad de ROLLBACK despus de una sentencia DDL, por lo que una instruccin DROP es definitiva (aviso para los gatillos rpidos). Volveremos a los procedimientos y funciones ms adelante. 2.3.- BLOQUES ANIDADOS Ya hemos comentado que los bloques se pueden anidar, ya sea con los bloques annimos como con los nombrados. El concepto es muy sencillo y, bsicamente, consiste en encerrar entre BEGIN EXCEPTION END los bloques de cdigo susceptibles de provocar algn error para as poder capturarlo y proceder en consecuencia. Por ejemplo: supongamos que tenemos un procedimiento que inserta un registro en la tabla DEPARTAMENTOS y actualiza en EMPLEADOS el departamento de un empleado con el nuevo creado. Pero slo debe realizarse la transaccin si se tiene xito en los dos casos. Por supuesto, si no se puede realizar la insercin del departamento, es absurdo intentar actualizar el departamento del empleado ya que no existir (fall la insercin) PROCEDURE INSERTA_DEP_EMP(p_departamento IN NUMBER, p_nom_departamento IN VARCHAR2, p_cod_empleado IN NUMBER p_resultado OUT VARCHAR2) IS BEGIN -- Primer bloque. Anidado dentro del bloque principal BEGIN INSERT INTO DEPARTAMENTOS VALUES(p_departamento, p_nom_departamento); -- Segundo bloque. Anidado dentro del bloque del INSERT -- Se ejecutar si no se produjo error en el anterior. BEGIN UPDATE EMPLEADOS SET departamento=p_departamento WHERE codigo=p_cod_empleado ; -- AQUI LLEGAMOS SI NO SE PRODUJO NINGUNA EXCEPCION -- GRABAMOS LA TRANSACCION. COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK ; p_resultado := 'Error al actualizar el empleado'; END; EXCEPTION WHEN OTHERS THEN p_resultado := 'Error al insertar Departamento'; END; END;

Pg 6 de 9

Curso de PL/SQL
Esta es una forma de hacerlo, sobre todo para poder observar perfectamente el anidamiento de los bloques y el control de las excepciones (errores). Daros cuenta que insertamos el registro y si todo ha ido bien, actualizamos el registro del empleado con el nuevo departamento. Si tambin ha tenido xito el update, hacemos el commit, grabando as la transaccin. Si falla el insert, devolvemos en la variable p_resultado la causa del error y no hemos hecho nada. Si lo que falla es el update posterior, deshacemos la insercin (ROLLBACK) y devolvemos la causa del error. Tambin podramos haber colocado un SAVEPOINT al principio para hacer el ROLLBACK TO SAVEPOINT en lugar del ROLLBACK, asegurndonos as que solo deshacemos el insert y no alguna transaccin pendiente que pudiera haber. El cdigo final sera: PROCEDURE INSERTA_DEP_EMP(p_departamento IN NUMBER, p_nom_departamento IN VARCHAR2, p_cod_empleado IN NUMBER p_resultado OUT VARCHAR2) IS BEGIN -- Primer bloque. Anidado dentro del bloque principal BEGIN SAVEPOINT trans1 INSERT INTO DEPARTAMENTOS VALUES(p_departamento, p_nom_departamento); -- Segundo bloque. Anidado dentro del bloque del INSERT -- Se ejecutar si no se produjo error en el anterior. BEGIN UPDATE EMPLEADOS SET departamento=p_departamento WHERE codigo=p_cod_empleado ; -- AQUI LLEGAMOS SI NO SE PRODUJO NINGUNA EXCEPCION -- GRABAMOS LA TRANSACCION. COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK TO SAVEPOINT trans1; p_resultado := 'Error al actualizar el empleado'; END; EXCEPTION WHEN OTHERS THEN p_resultado := 'Error al insertar Departamento'; END; END; El tema de los bloques anidados es muy til. Otra aplicacin interesante es la de buscar un valor en una tabla y si no existe, proceder a ejecutar una serie de instrucciones. Por ejemplo: en el caso anterior, si el departamento que vamos a insertar NO existe, lo insertamos, si ya existe solo actualizaremos el departamento al empleado. Veamos el cdigo comentado:

Pg 7 de 9

Curso de PL/SQL
PROCEDURE INSERTA_DEP_EMP(p_departamento IN NUMBER, p_nom_departamento IN VARCHAR2, p_cod_empleado IN NUMBER p_resultado OUT VARCHAR2) IS BEGIN SAVEPOINT trans1 -- Primer bloque. Anidado dentro del bloque principal BEGIN -- BUSCAMOS EL DEPARTAMENTO SELECT codigo FROM DEPARTAMENTOS WHERE codigo=p_departamento ; EXCEPTION WHEN NO_DATA_FOUND THEN -- No existe, as que lo insertamos (LO INTENTAMOS) -- OTRO BLOQUE ANIDADO. BEGIN INSERT INTO DEPARTAMENTOS VALUES(p_departamento, p_nom_departamento); EXCEPTION WHEN OTHERS THEN p_resultado := 'Error al insertar Departamento'; END; END; -- Segundo bloque. Anidado dentro del bloque principal -- Tanto si ya exista el departamento, como si se ha -- insertado nuevo, actualizamos el empleado. BEGIN UPDATE EMPLEADOS SET departamento=p_departamento WHERE codigo=p_cod_empleado ; -- AQUI LLEGAMOS SI NO SE PRODUJO NINGUNA EXCEPCION -- GRABAMOS LA TRANSACCION. COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK TO SAVEPOINT trans1; p_resultado := 'Error al actualizar el empleado'; END; END; En este cdigo falta algo, pero volveremos a l cuando veamos las excepciones definidas por usuario y cmo lanzar una excepcin de forma intencionada. En particular falta un RAISE <excepcion> (adems una excepcin especial) para forzar la interrupcin de la ejecucin del cdigo. Daros cuenta que ya dijimos que cuando se produce una excepcin se ejecuta el cdigo del manejador que la gestiona y se finaliza el bloque actual. Pero fijaros en el cdigo anterior. Si el INSERT falla, se carga el texto en la variable p_resultado y se finaliza la ejecucin del bloque, pero nada impide que se ejecute el cdigo del siguiente bloque (el azul). De momento lo controlaremos con una variable lgica que nos indique si tenemos que realizar la actualizacin. De este modo adems, vemos la sentencia IF THEN END IF; de control de flujo como adelanto. Ah va el cdigo correspondiente:

Pg 8 de 9

Curso de PL/SQL
PROCEDURE INSERTA_DEP_EMP(p_departamento IN NUMBER, p_nom_departamento IN VARCHAR2, p_cod_empleado IN NUMBER p_resultado OUT VARCHAR2) IS updateamos BOOLEAN; BEGIN updateamos := TRUE ; SAVEPOINT trans1 -- Primer bloque. Anidado dentro del bloque principal BEGIN -- BUSCAMOS EL DEPARTAMENTO SELECT codigo FROM DEPARTAMENTOS WHERE codigo=p_departamento ; EXCEPTION WHEN NO_DATA_FOUND THEN -- No existe, as que lo insertamos (LO INTENTAMOS) -- OTRO BLOQUE ANIDADO. BEGIN INSERT INTO DEPARTAMENTOS VALUES(p_departamento, p_nom_departamento); EXCEPTION WHEN OTHERS THEN updateamos := FALSE ; p_resultado := 'Error al insertar Departamento'; END; END; -- Segundo bloque. Anidado dentro del bloque principal -- Tanto si ya exista el departamento, como si se ha -- insertado nuevo, actualizamos el empleado. -- Siempre y cuando no haya habido error. IF updateamos THEN BEGIN UPDATE EMPLEADOS SET departamento=p_departamento WHERE codigo=p_cod_empleado ; -- AQUI LLEGAMOS SI NO SE PRODUJO NINGUNA EXCEPCION -- GRABAMOS LA TRANSACCION. COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK TO SAVEPOINT trans1; p_resultado := 'Error al actualizar el empleado'; END; END IF; END; Como podis ver, el tema de bloques anidados da para mucho. Podis empezar a imaginar las muchas posibilidades que tienen y cuando veamos las excepciones predefinidas y las definibles por usuario un poco ms adelante, le sacaremos ms partido an.

Pg 9 de 9

Вам также может понравиться