8 Unidad8 PLSQL
8 Unidad8 PLSQL
Por ello, todas las bases de datos incorporan algún lenguaje de tipo procedimental que
permite manipular de forma más avanzada los datos de la base de datos.
El lenguaje PL/SQL viene incorporado en el servidor de base de datos Oracle y forma parte
de la suite de herramientas que ofrece Oracle.
Se trata de un lenguaje creado para dar a SQL nuevas posibilidades. Esas posibilidades
permiten utilizar condiciones y bucles al estilo de los lenguajes de tercera generación (como
Basic, Cobol, C++, Java, etc.).
En otros sistemas gestores de bases de datos existen otros lenguajes procedimentales: SQL
Server utiliza Transact SQL, Informix usa Informix 4GL,...
PL/SQL es una tecnología que emplea Oracle Server y ciertas herramientas de Oracle. Los
bloques PL/SQL se transfieren a un motor PL/SQL que los procesa y que puede residir en
una herramienta o en el Servidor de Oracle.
2
Cuando se ejecutan bloques PL/SQL desde un precompilador de Oracle como, por ejemplo,
programas Pro*C o Pro*Cobol, SQL*Plus o Server Manager, el motor PL/SQL de Oracle
Server los procesa. Se encarga de separar las sentencias SQL y las envía individualmente al
ejecutor de sentencias SQL. Sólo es necesaria una transferencia para enviar el bloque desde
la aplicación a Oracle Server, mejorando así el rendimiento, especialmente en las redes
cliente-servidor. Además, el código PL/SQL se puede almacenar en Oracle Server como
subprogramas a los que puede hacer referencia cualquier número de aplicaciones
conectadas a la base de datos.
2. Ventajas de PL/SQL
Rendimiento:
PL/SQL puede mejorar el rendimiento de una aplicación. Las ventajas varían en función de
cada entorno de ejecución.
PL/SQL se puede utilizar para agrupar sentencias SQL en un solo bloque y enviar el bloque
completo al servidor con una sola llamada, reduciendo así el tráfico de red. Sin PL/SQL, las
sentencias SQL se enviarían a Oracle Server de una en una. Cada sentencia SQL origina una
llamada a Oracle Server y, por lo tanto, la sobrecarga de rendimiento es mayor. En un
3
entorno de red, esta sobrecarga puede ser importante. Tal y como ilustra la imagen, si la
aplicación utiliza SQL de manera intensiva, se pueden usar subprogramas y bloques PL/SQL
para agrupar sentencias SQL antes de enviarlas a Oracle Server para que las ejecute.
Nota: Los procedimientos y las funciones que se declaran como parte de una aplicación
Oracle Forms o Reports Developer son distintas de las almacenadas en la base de datos,
aunque su estructura general sea la misma. Los subprogramas almacenados son objetos de
base de datos y están almacenados en el diccionario de datos. Cualquier aplicación puede
acceder a ellos, incluidas las aplicaciones Oracle Forms o Reports Developer.
Portabilidad:
Dado que PL/SQL es un lenguaje nativo de Oracle Server, se pueden desplazar los
programas a cualquier entorno host (sistema operativo o plataforma) que sea compatible
con Oracle Server y PL/SQL. Dicho de otro modo, los programas PL/SQL funcionan allí donde
funcione Oracle Server, por lo que no es necesario adaptarlos a cada nuevo entorno.
• Procesar individualmente las filas que devuelve una consulta de múltiples filas con un
cursor explícito.
Manejo de Errores:
La funcionalidad del manejo de errores de PL/SQL permite hacer lo siguiente:
4
3. Estructura de Bloques PL/SQL
PL/SQL es un lenguaje estructurado en bloques, lo que significa que los programas se
pueden dividir en bloques lógicos. Los bloques PL/SQL constan de hasta tres secciones: la
sección declarativa (opcional), la sección ejecutable (necesaria) y la sección de
manejo de excepciones (opcional).
• Manejo de excepciones. Para indicar las acciones a realizar en caso de error. Van
precedidas por la palabra EXCEPTION.
5
4. Normas de escritura de instrucciones PL/SQL
La mayor parte de las normas de escritura en PL/SQL proceden de SQL, por ejemplo:
• Colocar un punto y coma (;) al final de las sentencias SQL o las sentencias de control
PL/SQL.
• Las sentencias END y el resto de las sentencias PL/SQL necesitan un punto y coma
para terminar la sentencia.
• Los comentarios de línea simple utilizan los signos -- (doble guión). El texto a la
derecha de los guiones se considera comentario (el de la izquierda no)
DECLARE
v NUMBER := 17;
BEGIN
DBMS_OUTPUT.PUT_LINE(v) -- escribe 34
END;
6
5. Algunos tipos de bloques
Un bloque PL/SQL se puede representar como sigue:
Los programas PL/SQL constan de uno o varios bloques. Estos bloques pueden estar
completamente separados entre sí o anidados unos en otros.
Bloques Anónimos:
Los bloques anónimos son bloques sin nombre. Se declaran en el punto de la aplicación en el
que tienen que ser ejecutados y se transfieren al motor PL/SQL para que los ejecute en
tiempo de ejecución.
Los sub-programas son bloques PL/SQL con nombre que aceptan parámetros y se pueden
llamar. Se pueden declarar como procedimientos o como funciones.
Generalmente, los procedimientos se utilizan para realizar una acción y las funciones, para
calcular un valor.
Las funciones son similares a los procedimientos, excepto en que las funciones deben
devolver un valor.
7
6. Uso de variables
Con PL/SQL, es posible declarar variables y utilizarlas en sentencias procedimentales y SQL
en cualquier lugar en el que se pueda utilizar una expresión. Las variables se utilizan para lo
siguiente:
Sintaxis:
Donde:
8
Ejemplo:
DECLARE
v_hiredate DATE;
v_deptno NUMBER(2) NOT NULL := 10;
v_location VARCHAR2(13) := 'Atlanta';
c_comm CONSTANT NUMBER := 1400;
v_mgr NUMBER(6) DEFAULT 100;
• Asigne un nombre al identificador utilizando las mismas reglas que se aplican a los
objetos SQL.
• Si declara un solo identificador por línea, el código será más fácil de leer y de
mantener.
• Dos objetos pueden tener el mismo nombre, siempre y cuando estén definidos en
bloques diferentes. Si coexisten, sólo se puede utilizar el objeto declarado en el
bloque actual.
9
• El nombre (identificador) de una variable no debería ser el mismo que el nombre de
las columnas de la tabla utilizadas en el bloque. Si en las sentencias SQL hay
variables PL/SQL que tienen el mismo nombre que una columna, Oracle Server
asume que se está haciendo referencia a la columna.
El tipo de dato escalar guarda un solo valor y no tiene componentes internos. Los tipos de
dato escalares se pueden clasificar en cuatro categorías: número, carácter, fecha y
booleano. Los tipos de dato de carácter y número tienen subtipos que asocian un tipo base a
una restricción. Por ejemplo, INTEGER y POSITIVE son subtipos del tipo base NUMBER.
10
Las bases de datos Oracle permiten utilizar tres tipos de datos diferentes para trabajar con
fechas y momentos de tiempo:
• DATE: este tipo de dato permite almacenar una fecha y un tiempo hasta el nivel de
segundos. No incluye información sobre la zona horaria. Es el tipo de dato que más
se utiliza para trabajar con fechas dentro de cualquier aplicación Oracle.
• TIMESTAMP: se trata de un tipo de dato similar al DATE pero con dos diferencias
clave, permiten almacenar y manipular momentos de tiempo hasta la mil millonésima
de segundo (con una precisión de 9 decimales), y también es posible asociarle una
zona horaria de tal manera que la base de datos Oracle tendrá en cuenta dicha zona
horaria cuando manipulemos y realicemos cálculos utilizando este tipo de dato.
• INTERVAL: mientras que los tipos DATE y TIMESTAMP indican un momento específico
de tiempo, INTERVAL almacena y permite trabajar con duraciones de tiempo, siendo
posible definir intervalos de tiempo en términos de años y meses, o de días y
segundos.
DECLARE
v_job VARCHAR2(9);
/*variable alfanumérica de 9 posiciones maximo*/
v_count BINARY_INTEGER := 0;
v_count2 PLS_INTEGER := 0
/*entero con signo usado como contador y valor inicial 0*/
v_total_sal NUMBER(9,2) := 0;
/*variable numérica con decimales y valor inicial 0 */
11
6.4. Atributo %TYPE
Cuando se declaran variables PL/SQL para guardar valores de columnas, debemos
asegurarnos de que la variable es del tipo de dato y precisión correctos. Si no es así, se
producirá un error PL/SQL durante la ejecución.
Ejemplos:
v_balance NUMBER(7,2);
v_min_balance v_balance%TYPE := 10;
12
6.5. DBMS_OUTPUT.PUT_LINE
Una opción para ver la información de un bloque PL/SQL es DBMS_OUTPUT.PUT_LINE.
DBMS_OUTPUT es un paquete de Oracle y PUT_LINE es una función contenida en ese
paquete.
DECLARE
a NUMBER := 17;
BEGIN
DBMS_OUTPUT.PUT_LINE(a);
END;
13
7. Operadores de PL/SQL
En PL/SQL se permiten utilizar todos los operadores de SQL: los operadores
aritméticos (+ - * /), condicionales (> < != <> >= <= OR AND NOT) y de cadena
(||).
14
Las operaciones de una expresión se realizan en un orden en particular que
depende de su precedencia (prioridad). La siguiente tabla muestra el orden que
tienen por defecto las operaciones, desde la prioridad más alta hasta la más baja.
Ejemplos:
v_cont := v_count + 1;
v_equal := (v_n1=v_n2);
8. Funciones en PL/SQL
La mayoría de las funciones disponibles en SQL también son válidas en las expresiones
PL/SQL.
Hay que tener en cuenta que las funciones de grupo: AVG, MIN, MAX, COUNT, SUM,
STDDEV y VARIANCE se aplican a los grupos de filas de una tabla y, por lo tanto, sólo están
disponibles en las sentencias SQL de un bloque PL/SQL.
15
9. Estructuras de control
Para cambiar el flujo lógico de las sentencias en el interior de los bloques PL/SQL, podemos
utilizar una serie de estructuras de control. Esta lección explica tres tipos de estructuras de
control PL/SQL: construcciones condicionales con la sentencia IF, expresiones CASE y
estructuras de control LOOP.
9.1. Sentencias IF
La estructura de la sentencia IF de PL/SQL es similar a la estructura de las sentencias IF de
otros lenguajes procedimentales. Permite a PL/SQL realizar acciones selectivamente
basándose en condiciones.
IF condición THEN
sentencias;
[ELSE
sentencias;]
END IF;
En la sintaxis:
16
Ejemplos:
DECLARE
v_fecha_alta DATE := '12/12/90';
v_five_years BOOLEAN;
BEGIN
. . .
IF MONTHS_BETWEEN(SYSDATE,v_fecha_alta)/12 > 5 THEN
v_five_years := TRUE;
ELSE
v_five_years := FALSE;
END IF;
IF v_deptno = 10 THEN
v_bonus := 5000;
ELSIF v_deptno = 80 THEN
v_bonus := 7500;
ELSE
v_bonus := 2000;
END IF;
17
9.2. Expresiones CASE
La expresión CASE selecciona un resultado y lo devuelve. Para seleccionar el resultado, la
expresión CASE utiliza un selector, que es una expresión cuyo valor sirve para seleccionar
una de varias alternativas. El selector va seguido de una o varias cláusulas WHEN, que se
comprueban secuencialmente. El valor del selector determina qué cláusula se ejecuta. Si el
valor del selector es igual al valor de una expresión de cláusula WHEN, se ejecuta esa
cláusula WHEN.
CASE selector
WHEN condición1_búsqueda THEN resultado1;
WHEN condición2_búsqueda THEN resultado2;
...
WHEN condiciónN_búsqueda THEN resultadoN;
[ELSE resultadoN+1;]
END CASE;
Ejemplos:
texto:= CASE actitud
WHEN 'A' THEN 'Muy buena';
WHEN 'B' THEN 'Buena';
WHEN 'C' THEN 'Normal';
WHEN 'D' THEN 'Mala';
ELSE 'Desconocida';
END CASE;
aprobado:= CASE
WHEN actitud='A' AND nota>=4 THEN TRUE;
WHEN nota>=5 AND (actitud='B' OR actitud='C')
THEN TRUE;
WHEN nota>=7 THEN TRUE;
ELSE FALSE
END CASE;
• Un bucle básico LOOP que realiza acciones repetitivas sin condiciones globales.
18
• Bucles FOR LOOP. PL/SQL ofrece dos tipos de FOR LOOP: Numéricos y cursores.
El tipo más sencillo de sentencia LOOP es el bucle básico (o infinito), que encierra una
secuencia de sentencias entre las palabras clave LOOP y END LOOP. Cada vez que el flujo de
ejecución llega a la sentencia END LOOP, el control se devuelve a la sentencia LOOP
correspondiente por encima de ella. Un bucle básico permite la ejecución de su sentencia al
menos una vez, aunque la condición ya se haya cumplido antes de entrar en el bucle. Sin la
sentencia EXIT, el bucle sería infinito.
La Sentencia EXIT
Utilice la sentencia EXIT para finalizar los bucles. El control pasa a la sentencia que hay
después de la sentencia END LOOP. Puede emitir EXIT como una acción en el interior de una
sentencia IF, o como una sentencia autónoma en el interior del bucle. La sentencia EXIT se
debe colocar en el interior de un bucle. En este caso, puede añadir una cláusula WHEN para
permitir que el bucle termine de manera condicional. Cuando se encuentra con la sentencia
EXIT, la condición de la cláusula WHEN se evalúa. Si esta condición devuelve TRUE, el bucle
finaliza y el control pasa a la siguiente sentencia que hay después del bucle. Un bucle básico
puede contener múltiples sentencias EXIT.
LOOP
sentencia1;
. . .
EXIT [WHEN condición];
END LOOP;
19
Ejemplo:
DECLARE
v_pais_id lugares.pais_id%TYPE := 'CA';
v_lugar_id lugares.lugar_id%TYPE;
v_cont NUMBER(2) := 1;
v_ciudad lugares.ciudad%TYPE := 'Montreal';
BEGIN
SELECT MAX(lugar_id) INTO v_lugar_id FROM lugares
WHERE ciudad_id = v_ciudad_id;
LOOP
INSERT INTO lugares(lugar_id, ciudad, pais_id)
VALUES((v_lugar_id + v_cont),v_ciudad, v_pais_id);
v_cont := v_cont + 1;
EXIT WHEN v_cont > 3;
END LOOP;
END;
20
Bucles WHILE
El bucle WHILE sirve para repetir una secuencia de sentencias hasta que la condición de
control ya no sea TRUE. La condición se evalúa al principio de cada iteración. El bucle
termina cuando la condición es FALSE. Si la condición es FALSE al principio del bucle, no se
realiza ninguna iteración más.
WHILE condición LOOP
sentencia1;
sentencia2;
. . .
END LOOP;
En la sintaxis:
Si las variables que se utilizan en las condiciones no cambian a lo largo del cuerpo del bucle,
la condición sigue siendo TRUE y el bucle no termina.
Ejemplo:
DECLARE
v_pais_id lugares.pais_id%TYPE := 'CA';
v_lugar_id lugares.lugar_id%TYPE;
v_cont NUMBER(1) := 1;
v_ciudad lugares.ciudad%TYPE := 'Montreal';
BEGIN
SELECT MAX(lugar_id) INTO v_lugar_id FROM lugares
WHERE ciudad_id = v_ciudad_id;
21
Bucles FOR LOOP
Los bucles FOR tienen la misma estructura general que los bucles básicos. Pero, además,
poseen una sentencia de control antes de la palabra clave LOOP para determinar el número
de iteraciones que realiza PL/SQL. En la sintaxis:
donde:
Nota: La secuencia de sentencias se ejecuta cada vez que aumenta el contador, tal y como
determinan los dos límites. El límite inferior y el límite superior del rango del bucle pueden
ser literales, variables o expresiones, pero se deben evaluar como enteros.
DECLARE
v_lower NUMBER := 1;
v_upper NUMBER := 100;
BEGIN
FOR i IN v_lower..v_upper LOOP
...
END LOOP;
END;
El límite inferior y el límite superior están incluidos en el rango del bucle. Si el límite inferior
del rango del bucle se evalúa como un entero mayor que el límite superior, la secuencia de
sentencias no se ejecutará, siempre y cuando no se haya utilizado REVERSE.
22
Por ejemplo, la siguiente sentencia sólo se ejecuta una vez:
Ejemplo:
Insertar tres nuevos identificadores de lugares para el
código de país de CA y la ciudad de Montreal. Estos tres
nuevos identificadores tienen que ser consecutivos al lugar
de id más alto.
DECLARE
v_pais_id lugares.pais_id%TYPE := 'CA';
v_lugar_id lugares.lugar_id%TYPE;
v_ciudad lugares.ciudad%TYPE := 'Montreal';
BEGIN
SELECT MAX(lugar_id) INTO v_lugar_id
FROM lugares
WHERE ciudad_id = v_ciudad_id;
FOR i IN 1..3 LOOP
INSERT INTO lugares(lugar_id, ciudad, pais_id)
VALUES((v_lugar_id + v_cont),v_ciudad, v_pais_id);
END LOOP;
END;
La palabra clave END señala el final de un bloque PL/SQL, no el final de una transacción. Al
igual que un bloque puede abarcar múltiples transacciones, una transacción puede abarcar
múltiples bloques.
PL/SQL no admite directamente sentencias DDL (Lenguaje de Definición de Datos) como, por
ejemplo, CREATE TABLE, ALTER TABLE o DROP TABLE.
23
PL/SQL no admite sentencias DCL (Lenguaje de Control de Datos) como, por ejemplo,
GRANT o REVOKE.
Siempre que se emite una sentencia SQL, Oracle abre un área de memoria en la que se
analiza y se ejecuta el comando. Esta área se denomina cursor. Por tanto, un cursor es un
área de trabajo SQL privada. Existen dos tipos de cursores:
• Cursores implícitos: Declarados para todas las sentencias DML y SELECT de PL/SQL
• Cursores explícitos: El programador los declara y les asigna un nombre. Los veremos
mas adelante.
Los cursores SQL poseen una serie de atributos. Los atributos de los cursores implícitos, que
pueden utilizarse dentro de los bloques PL/SQL para probar el resultado de las sentencias
SQL, son:
• SQL%ROWCOUNT Número de filas afectadas por la sentencia SQL más reciente
(un valor entero)
• SQL%FOUND Atributo booleano que se evalúa como TRUE si la sentencia SQL
más reciente afecta a una o varias filas
• SQL%NOTFOUND Atributo booleano que se evalúa como TRUE si la sentencia SQL
más reciente no afecta a ninguna fila
• SQL%ISOPEN Siempre se evalúa como FALSE porque PL/SQL cierra los
cursores implícitos inmediatamente después de ejecutarlos
24
• La cláusula WHERE es opcional y se puede utilizar para especificar variables de
entrada, constantes, literales o expresiones PL/SQL.
• Utilice funciones de grupos como, por ejemplo, SUM, en las sentencias SQL, porque
este tipo de funciones se aplica a los grupos de filas de una tabla.
• Las consultas deben devolver sólo una fila. Si las consultas devuelven más de una fila
o ninguna, se generará un error.
• Para gestionar estos errores, PL/SQL emite excepciones estándar, que se pueden
interrumpir en la sección de excepciones del bloque con las excepciones
NO_DATA_FOUND y TOO_MANY_ROWS .
DECLARE
v_salario empleados.salario%TYPE;
v_nombre empleados.nombre%TYPE;
BEGIN
SELECT salario,nombre INTO v_salario, v_nombre
FROM empleados
WHERE id_empleado=12344;
Ejemplos:
BEGIN
INSERT INTO emple(emp_no, apellido, oficio,fecha_alt,
salario) VALUES (12345, 'Cores', 'vendedor', sysdate,
4000);
END;
25
Aumente el sueldo a todos los empleados que tengan el
puesto de administrativos.
DECLARE
v_sal_incr emple.salario%TYPE := 800;
BEGIN
UPDATE emple
SET salario = salario + v_sal_incr
WHERE oficio = 'administrativo';
END;
DECLARE
v_deptno emple.dept_no%TYPE := 10;
BEGIN
DELETE FROM emple
WHERE dept_no = v_deptno;
END;
26
La opción REPLACE indica que, si el procedimiento existe, se borrará y se sustituirá por la
nueva versión que ha creado la sentencia.
27
Nota: Si hay algún error de compilación y se realizan cambios posteriores en la
sentencia CREATE PROCEDURE, primero debe utilizar DROP en el procedimiento o la
sintaxis OR REPLACE.
Los parámetros reales son variables o expresiones a las que se hace referencia en la lista
de parámetros de la llamada de un subprograma. Por ejemplo, en la llamada
incrementar_salario(1122, 20) al procedimiento incrementar_salario , las variables 1122 y
20 son parámetros reales.
Los parámetros reales se evalúan y los resultados se asignan a parámetros formales durante
la llamada del subprograma. Los parámetros reales también pueden ser expresiones .
Es conveniente utilizar diferentes nombres para los parámetros formales y los reales. En
este curso, los parámetros formales tienen el prefijo p_.
Los parámetros formales y reales deberían tener tipos de datos compatibles. Si es necesario,
antes de asignar el valor, PL/SQL convierte el tipo de dato del valor del parámetro real en el
del parámetro formal.
28
El modo de parámetro IN es el modo por defecto. Es decir, si no se especifica ningún modo
con un parámetro, se considera que el parámetro es IN. Los modos OUT e IN OUT se deben
especificar explícitamente delante de dichos parámetros.
Hay que asignar un valor a los parámetros OUT o IN OUT antes de que regresen al entorno
de llamada.
A los parámetros IN se les puede asignar un valor por defecto en la lista de parámetros.
A los parámetros OUT e IN OUT no se les pueden asignar valores por defecto.
Por defecto, los parámetros IN se pasan por referencia y los parámetros OUT e IN OUT por
valor.
29
aumento de sueldo del 10 por ciento.
30
siguiente forma:
EXECUTE
consultar_empleado(1712,:nombre, :salario, :comision);
31
valor que se devuelve es el original, un valor que no ha
cambiado o un nuevo valor definido en el interior del
procedimiento.
BEGIN
:g_tfno_no := '8006330575';
END;
/
PRINT g_tfno_no
EXECUTE formatear_ tfno (:g_tfno_no)
PRINT g_tfno_no
Para eliminar un procedimiento del servidor utilizando SQL*Plus, hay que ejecutar el
comando SQL DROP PROCEDURE nombre_procedimiento.
32
Las funciones se pueden almacenar en la base de datos como objetos de esquema para
repetir su ejecución. Las funciones que están almacenadas en la base de datos se
denominan funciones almacenadas.
La función puede formar parte de una expresión SQL o parte de una expresión PL/SQL. En
una expresión SQL, la función debe obedecer una serie de reglas específicas para controlar
los efectos secundarios. En una expresión PL/SQL, el identificador de la función actúa como
una variable cuyo valor depende de los parámetros que se le transfieran.
Ejemplo:
33
WHERE empno = p_id;
RETURN v_salary;
END obtener_salario;
Desde SQL*PLUS:
PRINT g_salario
Ejemplo:
34
multiplicar el valor del parámetro por 0.08.
Las funciones PL/SQL definidas por el usuario se pueden llamar desde cualquier expresión
SQL desde la que se pueda llamar a una función incorporada.
• Debe poseer o tener el privilegio EXECUTE sobre la función para poder llamarla desde
una sentencia SQL.
• Las funciones deben devolver tipos de dato SQL válidos. No pueden ser tipos de dato
específicos de PL/SQL como, por ejemplo, BOOLEAN, RECORD o TABLE. Esta
misma restricción se aplica a los parámetros de la función.
• La función no puede invocar a otra función que se salte alguna de las reglas
anteriores.
35
Ejemplo:
Ejemplo:
36
12.2. Eliminación de funciones
Para eliminar una función almacenada utilizando SQL*Plus, hay que ejecutar el comando
SQL DROP FUNCTION nombre_función
Uso de USER_OBJECTS
Para obtener el nombre de todos los objetos PL/SQL almacenados en un esquema, consulte
la vista de diccionario de datos USER_OBJECTS.
También puede examinar las vistas ALL_OBJECTS y DBA_OBJECTS, cada una de las cuales
contiene la columna OWNER adicional para el propietario del objeto.
Ejemplo:
37
Uso de USER_SOURCE
Examine también las vistas ALL_SOURCE y DBA_SOURCE, cada una de las cuales contiene
la columna OWNER adicional para el propietario del objeto.
Ejemplo:
SELECT text
FROM USER_SOURCE
WHERE name = 'QUERY_EMPLOYEE'
ORDER BY line;
Para obtener el texto de los errores de compilación, utilice la vista de diccionario de datos
USER_ERRORS o el comando SHOW ERRORS de SQL*Plus.
Examine también las vistas ALL_ERRORS y DBA_ERRORS, cada una de las cuales contiene
la columna OWNER adicional para el propietario del objeto.
38
Ejemplo:
Utilice SHOW ERRORS sin ningún argumento en el prompt de SQL para obtener los errores de
compilación del último objeto que haya compilado.
Ejemplo:
DESCRIBE query_employee
DESCRIBE add_dept
DESCRIBE tax
39
14. Excepciones
Se llama excepción a todo hecho que le sucede a un programa que causa que la ejecución
del mismo finalice. Lógicamente eso causa que el programa termine de forma anormal.
Los bloques terminan siempre cuando PL/SQL produce una excepción, pero se puede
especificar un manejador de excepciones para realizar las acciones finales.
Las excepciones se pueden capturar a fin de que el programa controle mejor la existencia de
las mismas.
40
14.2. Tipos de excepciones
Existen tres tipos de excepciones:
EXCEPTION
WHEN exception1 [OR exception2 . . .] THEN
statement1;
statement2;
. . .
[WHEN exception3 [OR exception4 . . .] THEN
statement1;
statement2;
. . . ]
[WHEN OTHERS THEN
statement1;
statement2;
. . . ]
En la sintaxis:
excepción es el nombre estándar de una excepción predefinida o el nombre de una
excepción definida por el usuario que se ha declarado en la sección.
Sentencia es una o varias sentencias PL/SQL o SQL.
OTHERS es una clausula de manejo de excepciones opcional que interrumpe las
excepciones no especificadas.
41
de excepciones OTHERS. Este manejador interrumpe cualquier excepción que todavía no se
haya manejado. Por esta razón, OTHERS es el último manejador de excepciones que se
define.
42
Veamos un ejemplo,
DECLARE
x NUMBER :=0;
y NUMBER := 3;
res NUMBER;
BEGIN
res:=y/x;
DBMS_OUTPUT.PUT_LINE(res);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('No se puede dividir por cero') ;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error inesperado') ;
END;
Cuando en nuestro bloque tengamos una sentencia SELECT, sin función de grupo y que
devuelva una sola fila, es conveniente manejar siempre las excepciones NO_DATA_FOUND y
TOO_MANY_ROWS
Otra posibilidad es asignar un nombre al número. Ese número es el que aparece cuando
Oracle muestra el mensaje de error tras la palabra ORA. Por ejemplo en un error por
restricción de integridad Oracle lanza un mensaje encabezado por el texto: ORA-02292. Por
lo tanto el error de integridad referencia es el —02292.
43
Ejemplo:
Si en un departamento hay empleados, imprima un mensaje que
informe al usuario de que el departamento no se puede
eliminar.
DECLARE
e_integridad EXCEPTION;
PRAGMA EXCEPTION_INIT(e_integridad, -2292);
BEGIN
DELETE FROM depart WHERE dept_no=20;
COMMIT;
EXCEPTION
WHEN e_integridad THEN
DBMS_OUTPUT.PUT_LINE(‘No se puede borrar ese
departamento porque tiene empleados relacionados’);
END;
44
Ejemplo:
DECLARE
v_error_code NUMBER;
v_error_message VARCHAR2(255);
BEGIN
...
EXCEPTION
...
WHEN OTHERS THEN
ROLLBACK;
v_error_code := SQLCODE ;
v_error_message := SQLERRM ;
INSERT INTO errors VALUES(v_error_code,v_error_message);
END;
Ejemplo:
EXCEPTION
...
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘Ocurrió el error ‘ || SQLCODE
||‘mensaje: ‘ || SQLERRM);
END;
Una forma, el usuario define las excepciones y las provoca usando RAISE
Está última forma, se usa cuando se necesite generar un error específico de la aplicación
desde el servidor (por ejemplo, desde un trigger de la base de datos) y devolver este error
al proceso de la aplicación cliente.
45
Para definir y provocar la excepción,
• Declaradas en la sección declarativa de un bloque PL/SQL.
Ejemplo,
DECLARE
e_invalid_depart EXCEPTION;
BEGIN
UPDATE depart
SET dnombre = '&p_depart_nombre'
WHERE dept_no = &p_depart_no;
IF SQL%NOTFOUND THEN
RAISE e_invalid_department;
END IF;
COMMIT;
EXCEPTION
WHEN e_invalid_department THEN
DBMS_OUTPUT.PUT_LINE('No existe el departamento.');
END;
46
Con RAISE_APPLICATION_ERROR se simplifican los tres pasos anteriores. Sintaxis:
DECLARE
BEGIN
DELETE FROM depart WHERE dept_no=60;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20001,'No existe ese
departamento');
END IF;
END;
15. Cursores
Oracle Server utiliza unas áreas de trabajo, que se denominan áreas SQL privadas, para
ejecutar sentencias SQL y almacenar la información del procesamiento. Se pueden utilizar
cursores PL/SQL para asignar un nombre a un área SQL privada y acceder a la información
que tiene almacenada.
El conjunto de filas que devuelve una consulta de múltiples filas se denomina conjunto
activo. Su tamaño equivale al número de filas que cumplen el criterio de búsqueda. El
diagrama de la siguiente página muestra cómo un cursor explícito “apunta” a la fila actual
del conjunto activo. De esta manera, el programa puede procesar las filas de una en una.
47
Los programas PL/SQL abren un cursor, procesan las filas que devuelve una consulta y luego
cierran el cursor. El cursor marca la posición actual en el conjunto activo.
La sentencia FETCH recupera la fila actual y avanza hasta la siguiente fila hasta que ya no
quedan más filas o hasta que se cumple la condición especificada.
Cierre el cursor cuando se haya procesado la última fila. La sentencia CLOSE desactiva el
cursor.
48
15.3. Declaración del cursor
Utilice la sentencia CURSOR para declarar un cursor explícito. Puede hacer referencia a
variables en la consulta, pero debe declararlas antes que la sentencia CURSOR.
CURSOR nombre_cursor IS
sentencia_select;
En la sintaxis:
nombre_cursor es un identificador PL/SQL.
sentencia_select es una sentencia SELECT sin una cláusula INTO.
Nota
Ejemplo:
El cursor emp_cursor se declara para recuperar las columnas
EMP_NO y APELLIDO de la tabla EMPLE.
DECLARE
v_empno emple.emp_no%TYPE;
v_ape emple.ape%TYPE;
CURSOR emp_cursor IS
SELECT emp_no, apellido
FROM emple;
BEGIN
...
Si la consulta no devuelve ninguna fila cuando el cursor está abierto, PL/SQL no produce
ninguna excepción.
49
15.5. Recuperación de datos desde el cursor
La sentencia FETCH recupera las filas del conjunto activo de una en una. Después de cada
recuperación, el cursor avanza hasta la siguiente fila del conjunto activo.
En la sintaxis:
nombre_cursor es el nombre del cursor declarado previamente.
Instrucciones:
• Incluya tantas variables en la cláusula INTO de la sentencia FETCH como columnas
en la sentencia SELECT, y asegúrese de que los tipos de datos son compatibles.
• Haga coincidir cada variable para que sus posiciones se correspondan con las de las
columnas.
50
15.6. Cierre del cursor
La sentencia CLOSE desactiva el cursor, y el conjunto activo queda indefinido. Cierre el
cursor después de terminar el procesamiento de la sentencia SELECT. Este paso permite
volver a abrir el cursor, si es necesario. Por lo tanto, puede establecer un conjunto activo
varias veces.
CLOSE nombre_cursor;
Aunque se puede terminar el bloque PL/SQL sin cerrar los cursores, debería tener como
costumbre cerrar cualquier cursor que haya declarado explícitamente para liberar recursos.
Ejemplo:
OPEN emp_cursor
LOOP
FETCH emp_cursor INTO v_empno, v_ape;
EXIT WHEN emp_cursor%NOTFOUND;
…...
END LOOP;
CLOSE emp_cursor;
END;
51
Ejemplo:
DECLARE
v_empno emple.emp_no%TYPE;
v_ape emple.apellido%TYPE;
CURSOR emp_cursor IS
SELECT emp_no, apellido
FROM emple;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_empno, v_ape;
52
15.8. Declaración de registros con el atributo %ROWTYPE
Para declarar un registro basándose en una recopilación de columnas de una tabla o una
vista de base de datos, hay que utilizar el atributo %ROWTYPE. Los campos del registro
obtienen sus nombres y tipos de dato de las columnas de la tabla o la vista. El registro
también puede almacenar una fila completa de datos que se ha recuperado desde un cursor
o una variable de cursor.
Ejemplo:
Sintaxis
DECLARE
identificador referencia%ROWTYPE;
donde:
53
En el siguiente ejemplo, un empleado se va a jubilar. La
información acerca de los empleados jubilados se agrega a
una tabla en la que se guarda este tipo de información. El
usuario proporciona el número de empleado. El registro del
empleado especificado por el usuario se recupera de EMPLE y
se almacena en la variable emp_rec, que se declara con el
atributo %ROWTYPE.
DECLARE
emp_rec emple%ROWTYPE;
BEGIN
SELECT * INTO emp_rec
FROM emple
WHERE emp_no = &emple_number;
COMMIT;
END;
/
54
Utilice un cursor para recuperar los números y los nombres
de los empleados y para rellenar una tabla de base de
datos, TEMP_LIST, con esta información.
DECLARE
CURSOR emp_cursor IS
SELECT emp_no, apellido
FROM emple;
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_record;
EXIT WHEN emp_cursor%NOTFOUND;
INSERT INTO temp_list (empid, empape)
VALUES (emp_record.emp_no, emp_record.apellido);
END LOOP;
COMMIT;
CLOSE emp_cursor;
END ;
/
En la sintaxis:
Instrucciones:
55
• No utilice un bucle FOR de cursor cuando haya que gestionar explícitamente las
operaciones del cursor.
Ejemplo:
DECLARE
CURSOR emp_cursor IS
SELECT apellido, dept_no
FROM emple;
BEGIN
FOR emp_record IN emp_cursor LOOP
-- se produce una apertura y una lectura implícita.
IF emp_record.dept_no = 30 THEN
DBMS_OUTPUT.PUT_LINE ('El empleado '||
emp_record.apellido|| ' trabaja en el dpto. ');
END IF;
END LOOP; -- se produce un cierre y una salida del bucle
implícita
END ;
56
15.12. Cursores de actualización
Los cursores de actualización se declaran igual que los cursores explícitos, añadiendo FOR
UPDATE al final de la sentencia SELECT.
CURSOR nombre_cursor
IS instrucción_SELECT FOR UPDATE
Para actualizar los datos del cursor hay que ejecutar una sentencia UPDATE especificando la
cláusula WHERE CURRENT OF .
UPDATE <nombre_tabla> SET <campo_1> = <valor_1>[,<campo_2> = <valor_2>]
WHERE CURRENT OF <cursor_name>
Cuando trabajamos con cursores de actualización debemos tener en cuenta que la sentencia
UPDATE genera bloqueos en la base de datos ( transacciones, disparadores,etc).
DECLARE
CURSOR cpaises IS
select CO_PAIS, DESCRIPCION, CONTINENTE from paises
FOR UPDATE;
co_pais VARCHAR2(3);
descripcion VARCHAR2(50);
continente VARCHAR2(25);
BEGIN
OPEN cpaises;
FETCH cpaises INTO co_pais,descripcion,continente;
WHILE cpaises%found
LOOP
UPDATE PAISES SET CONTINENTE = CONTINENTE || '.'
WHERE CURRENT OF cpaises;
FETCH cpaises INTO co_pais,descripcion,continente;
END LOOP;
CLOSE cpaises;
COMMIT;
END;
Un REF CURSOR es una variable, definida como un tipo de cursor, que apuntará o hará
referencia a un resultado de un cursor.
57
REF CURSOR no es un cursor, sino una variable que apunta a un cursor. Antes de asignar
una variable cursor, debe definirse un tipo cursor.
Nota: Los Strong REF Cursor resultan menos flexible pero a la vez menos propensos a
errores de programación que los Weak REF Cursor. Esto es porque con los Strong REF
Cursor el compilador PL/SQL verifica(en tiempo de compilación) que la cláusula FETCH tenga
una variable/record acorde con el tipo de retorno del cursor.
Sintaxis:
TYPE <NOMBRE_TIPO> IS REF CURSOR [RETURN xxx];
VARIABLE <NOMBRE_TIPO>;
Así para definir una variable REF CURSOR con la misma “forma” que la tabla depart:
TYPE cursor_fuerte_depart IS REF CURSOR RETURN depart%ROWTYPE;
p_cursor cursor_fuerte_depart;
58
Ejemplo:
DECLARE
TYPE T_CURSOR IS REF CURSOR;
MICURSOR T_CURSOR;
V_APELLIDO HR.EMPLOYEES.LAST_NAME%TYPE;
BEGIN
OPEN MICURSOR FOR 'SELECT LAST_NAME FROM HR.EMPLOYEES';
LOOP
FETCH MICURSOR INTO V_APELLIDO;
EXIT WHEN MICURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('APELLIDO = '||V_APELLIDO);
END LOOP;
CLOSE MICURSOR;
END;
59
El uso de OR REPLACE permite sobrescribir un trigger existente. Si se omite, y el trigger
existe, se producirá, un error.
Borrar un Disparador
DROP TRIGGER <NombreT>
Diccionario de Datos
Todos los datos de un TRIGGER están almacenados en la vista USER_TRIGGERS.
60
• No se violen las reglas de integridad, es decir no se pueden modificar claves
primarias, ni actualizar claves foráneas.
61
Sólo se puede definir un trigger de cada tipo por tabla. Esto da doce posibilidades:
BEFORE UPDATE row AFTER UPDATE row
BEFORE DELETE row AFTER DELETE row
BEFORE INSERT row AFTER INSERT row
BEFORE UPDATE statement AFTER UPDATE statement
BEFORE DELETE statement AFTER DELETE statement
BEFORE INSERT statement AFTER INSERT statement
Así si la sentencia siguiente, por ejemplo afecta a 3 filas, el trigger sólo se ejecutará 1 vez.
Es decir se emite un sólo mensaje
62
Para usarlos en una clausula WHEN se le quitan los dos puntos.
Tabla resumen:
Orden de
:old :new
Disparo
No definido; todos los campos Valores que serán insertados
INSERT
toman el valor NULL. cuando se complete la orden
Valores originales de la fila, antes Nuevos valores que serán escritos
UPDATE
de la actualización. cuando se complete la orden.
Valores originales, antes del No definido; todos los campos
DELETE
borrado de la fila. toman el valor NULL.
Ejemplo:
Programar un disparador que calcule el campo código de alumno (cod_al) cada vez que se
inserte un nuevo alumno:
CREATE OR REPLACE TRIGGER NuevoAlumno
BEFORE INSERT ON alumno FOR EACH ROW
BEGIN
-- Establecer el nuevo número de alumno:
SELECT MAX(cod_al)+1 INTO :new.cod_al FROM Alumno;
IF :new.cod_al IS NULL THEN
:new.cod_al := 1;
END IF;
END NuevoAlumno;
Modificar Pseudo-Registros:
• No puede modificarse el Pseudo-Registro :new en un disparador AFTER a nivel de fila.
• El Pseudo-Registro :old nunca se modificará: Sólo se leerá.
Sólo es válida en disparadores a nivel de fila y siempre es opcional. Si existe, el cuerpo del
disparador se ejecutará sólo para las filas que cumplan la condición de disparo especificada.
En la condición de disparo pueden usarse los Pseudo-Registros :old y :new, pero en ese caso
no se escribirán los dos puntos (:), los cuales son obligatorios en el cuerpo del disparador.
Ejemplo: Deseamos que los precios grandes no tengan más de 1 decimal. Si tiene 2 ó más
decimales, redondearemos ese precio: si el Precio>200, redondearlos a un decimal:
63
• Se puede escribir ese disparador sin la cláusula WHEN, usando un IF:
BEGIN
IF :new.Precio > 200 THEN
:new.Precio := ROUND(:new.Precio,1);
END IF;
Otro ejemplo:
Así cuando se crea un trigger para más de una operación DML, se puede utilizar un
predicado condicional en las sentencias que componen el trigger que indique que tipo de
operación o sentencia ha disparado el trigger.
64
– Inserting: Retorna true cuando el trigger ha sido disparado por un INSERT
Ejemplo:
Creamos un trigger de actualización a nivel de fila sobre la tabla "libros". Ante cualquier
modificación de los registros de "libros", se debe ingresar en la tabla "control", el nombre del
usuario que realizó la actualización y la fecha; pero, controlamos que NO se permita
modificar el campo "codigo", en caso de suceder, la acción no debe realizarse y debe
mostrarse un mensaje de error indicándolo:
Cuando se intenta modificar una vista esta modificación puede no ser posible debido al
formato de esa vista. Características:
◦ Sólo pueden definirse sobre Vistas.
◦ Se activan en lugar de la Orden DML que provoca el disparo, o sea, la orden
disparadora no se ejecutará nunca.
◦ Deben tener Nivel de Filas.
◦ Se declaran usando INSTEAD OF en vez de BEFORE/AFTER.
65
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.
Este tipo de trigger combina los cuatro eventos de activación en una sola pieza de código.
Además de la eficacia de la codificación, que aborda algunos problemas como:
• Error en Tablas mutantes (ORA-04091)
• Cuando se usa Trigger con EACH ROW que por cada fila hacer alguna transacción en
alguna otra tabla utilizando nuevos valores.
Si hacemos esto a la hora de programar el trigger, cuando este trigger se ejecute (dispare)
se nos producirá el error MUTATING TABLE ERROR
66
Ejemplo, supongamos el siguiente esquema relacional
Para almacenar los valores se puede utilizar un paquete u opcionalmente, podríamos utilizar
una tabla.
Para nuestro ejemplo creamos un paquete con las variables. Así tendremos un paquete con
2 variables: una para poder guardar el DNI y otra para el nombre del profesor:
CREATE OR REPLACE PACKAGE pck_profesores
AS
v_DNI_profesor Profesores.DNI%TYPE;
v_nombre_profesor varchar2(50);
END;:
67
• TRIGGER a nivel de fila: –necesitamos 2 variables globales
68
CREATE OR REPLACE TRIGGER compound_trigger_name
FOR INSERT | DELETE UPDATE OF column ON table COMPOUND TRIGGER
-- Sección declarativa (opcional.
END compound_trigger_name;
69
• cuerpo (no obligatoria).
En el cuerpo del paquete se implementa la especificación del mismo. El cuerpo
contiene los detalles de implementación y declaraciones privadas, manteniéndose
todo esto oculto a las aplicaciones externas, siguiendo el conocido concepto de “caja
negra”. Sólo las declaraciones hechas en la especificación del paquete son visibles y
accesibles desde fuera del paquete (por otras aplicaciones o procedimientos
almacenados) quedando los detalles de implementación del cuerpo del paquete
totalmente ocultos e inaccesibles para el exterior.
Para acceder a los elementos declarados en un paquete basta con anteceder el nombre del
objeto referenciado con el nombre del paquete donde está declarado y un punto, de esta
manera: Paquete.Objeto, donde Objeto puede ser un tipo, una variable, un cursor, un
procedimiento o una función declarados dentro del paquete.
Para referenciar objetos desde adentro del mismo paquete donde han sido declarados no es
necesario especificar el nombre del paquete y pueden (deberían) ser referenciados
directamente por su nombre.
Como hemos dicho anteriormente, la creación de un paquete pasa por dos fases:
1. Crear la cabecera del paquete donde se definen que procedimientos, funciones,
variables, cursores, etc. Disponibles para su uso posterior fuera del paquete. En esta
parte solo se declaran los objetos, no se implementa el código.
Hay que tener en cuenta que toda declaración de función o procedimiento debe estar dentro
del cuerpo del paquete, y que todo bloque de código contenido dentro del cuerpo debe estar
declarado dentro de la cabecera de paquete.
70
Ejemplo:
CREATE OR REPLACE PACKAGE PK1 IS
PROCEDURE xLis (xfamilia IN Articulos.cArtFml%TYPE);
END;
71
cambio en la definición de estas rutinas internas afectará sólo al paquete donde se
encuentran, simplificando el mantenimiento y protegiendo la integridad del conjunto.
Valores simples
Supongamos un procedimiento usa la sentencia SELECT COUNT(*) para contar registros. El
resultado se puede guardar en nuestra variable temporal vTotal, o se puede guardar en el
parámetro de salida pTotal, el cual podremos leer desde Java.
Conjunto de valores
Tenemos que usar cursores para retornar conjuntos de datos a Java (por medio de un
ResultSet).
72
Si queremos hacer una consulta y devolver sus resultados en un ResultSet, tenemos que
utilizar el parámetro de salida de tipo CURSOR como medio de transporte. Esto se hace:
Ejemplo:
create or replace Procedure ProGetPrueba2(prfCursor out Sys_Refcursor, pcodigo
out Number, pnNumber In Number) Is
V_EMPNO emple.emp_no%TYPE;
V_APELLIDO emple.apellido%TYPE;
Begin
--Valor 1: funciona OK, Valor diferente a 1 retorna error.
if pnNumber = 1 then
Select 1 into pcodigo
From DUAL;
Open prfCursor For 'SELECT emp_no,apellido FROM emple';
else
RAISE_APPLICATION_ERROR (-20000, 'El valor no puede ser distinto de
uno');
end if;
End ProGetPrueba2;
Recordar que se pueden implementar cursores genéricos los cuales permiten crear el cursor
y en tiempo de ejecución se asignará la consulta que el cursor debe manejar. La sintaxis de
implementación del cursor genérico:
OPEN <VARIABLE> FOR
SELECT campo FROM tabla [WHERE campo = otroDato];
73
El cuerpo del procedimiento es:
74
75