Está en la página 1de 73

PL/SQL

Ejercicios de clase
Tema 1 - Introduccin

Ejercicios simples de comprensin de estructura y
flujo de bloques, declaracin de variables y tipos
Tema 2 Sentencias ejecutables
Edicin y ejecucin de bloques con
SQL*Plus
Identificadores, tipos
Bloques anidados y mbito de variables
Ejercicios simples de comprensin
Cree y ejecute un bloque PL/SQL que:

Acepte dos nmeros por medio de variables de
sustitucin de SQL*Plus (ACCEPT, &).
Se debera dividir el primer nmero entre el segundo,
y despus aadir el segundo nmero al resultado.
El resultado debera escribirse en una variable de
PL/SQL.
E imprimirse en la pantalla a travs de una variable
de SQL*Plus (se define con VARIABLE y se imprime
con PRINT).
ACCEPT p_num1 NUMBER PROMPT 'Introduzca un nmero: '
ACCEPT p_num2 NUMBER PROMPT 'Introduzca otro nmero: '
VARIABLE p_resultado NUMBER

DECLARE
v_num1 NUMBER(3) := &p_num1;
v_num2 NUMBER(3) := &p_num2;
v_result NUMBER(3);

BEGIN

v_result := v_num1/v_num2 + v_num2;
:p_resultado := v_result;

END;
/

PRINT p_resultado
Genere un bloque PL/SQL que calcule la comisin total
para un ao.

El salario anual y el porcentaje anual de bonificaciones se
pasarn al bloque PL/SQL a travs de variables de sustitucin
de SQL*Plus.
El importe de las bonificaciones tendr que ser convertido de un
nmero entero a un decimal (por ejemplo 15 a 0.15)
Si el salario es nulo, asgnele cero antes de calcular la
compensacin total (NVL).
Compruebe el funcionamiento con diversos casos (para
introducir un NULL desde el prompt tenemos que escribir la
palabra completa, dar a intro provoca un error)

ACCEPT p_salanual NUMBER PROMPT 'Introduzca Salario Anual: '
ACCEPT p_porcentaje NUMBER PROMPT 'Introduzca el porcentaje de comisin: '

VARIABLE p_total NUMBER

DECLARE

v_salanual NUMBER(7) := &p_salanual;
v_porcentaje NUMBER(3) := &p_porcentaje;


BEGIN

:p_total := NVL(v_salanual,0) *(1+NVL(v_porcentaje,0)/100);
END;
/

PRINT p_total
Tema 3 Interaccin con Servidor
Definiendo y modificando datos
SELECT
INSERT UPDATE - DELETE
Ejercicios simples de comprensin
Ejercicios
Hacer un bloque PL/SQL que muestre por
pantalla el salario de SCOTT. (3.1.a)

Introduce mediante ACCEPT un nombre de
empleado y muestra por pantalla su salario.
Prubalo con varios nombres (3.1.b)

Hacer un bloque PL/SQL que muestre por
pantalla en nmero de empleados del
departamento 10. (3.1.c)




/* Servidor 3.a */

DECLARE
v_sal emp.sal%TYPE;
BEGIN
SELECT sal
INTO v_sal
FROM emp
WHERE ename='SCOTT';
dbms_output.put_line('El salario es: '||v_sal);
END;
/

/* servidor 3.1b */

ACCEPT p_emp CHAR PROMPT 'Introduce el nombre del
empleado:

DECLARE
v_sal emp.sal%TYPE;

BEGIN
SELECT sal
INTO v_sal
FROM emp
WHERE ename='&p_emp';
dbms_output.put_line('El salario es: '||v_sal);
END;
/

/* servidor 3.1c */
DECLARE
v_num Number(3);

BEGIN
SELECT count(*)
INTO v_num
FROM emp
WHERE deptno=10;
dbms_output.put_line('El departamento 10 tiene:
'||v_num||' empleados.');
END;
/

Ejercicios
Realiza bloques PL/SQL que hagan lo siguiente:

Adete como nuevo empleado a la tabla.
Para asegurarte de que el empno no est repetido realiza antes
un SELECT que te saque el mayor nmero de empleado y
smale 1. Cul sera la manera ms correcta de saber el
nmero de empleado que toca? (3.2.a)

Incrementa en una cantidad introducida mediante
ACCEPT el salario de todos los empleados que trabajan
como ANALYST (3.2.b)

Borra a todos los empleados del departamento
introducido mediante ACCEPT (3.2.c)
/* servidor 3.2.a */

DECLARE
v_num emp.empno%TYPE;
BEGIN
SELECT max(empno)
INTO v_num
FROM emp;
INSERT INTO emp VALUES
(v_num+1,'JAVI','ANALYST',NULL,'04/10/2009',6000,
1000,10);
END;
/

/* servidor 3.2.b */

ACCEPT p_subida PROMPT 'Introduce cantidad a
incrementar el salario: '

BEGIN
UPDATE emp
SET SAL = SAL + &p_subida
WHERE job='ANALYST';
END;
/


/* servidor 3.2.c */

ACCEPT p_dept PROMPT 'Introduce un
nmero de departamento: '

BEGIN
DELETE emp
WHERE deptno=&p_dept;
END;
/




Atributos del cursor SQL. Ejercicios
Realice un bloque PL/SQL que suprima todos
los empleados del departamento 10. Y muestre
un mensaje con el nmero de filas eliminadas.

Realice un bloque PL/SQL que incremente en
un 10% los salarios de todos los empleados que
trabajan como ANALYST. Indique el nmero de
empleados afectados.
/* servidor 3.3.a */

BEGIN
DELETE emp
WHERE deptno=10;

dbms_output.put_line('Hay '||SQL%ROWCOUNT||'
filas borradas.');
END;
/


/* servidor 3.3.a */

BEGIN
UPDATE emp
SET sal = sal*1.1
WHERE job = 'ANALYST';
dbms_output.put_line('Hay '||SQL%ROWCOUNT||'
empleados actualizados.');
END;
/

Tema 4 Estructuras de control
Simples y anidadas

Condicionales IF THEN ELSE
ELSEIF CASE

Bucles LOOP FOR - WHILE

Ejercicios
Introduce un nmero de empleado mediante
ACCEPT y si gana ms de 3000 dlares
muestra el mensaje Gana ms de 3000 dlares
Amplia el ejercicio anterior para que en caso de
que gane menos ponga Gana menos de 3000
dlares
Continua aadiendo condiciones para cada
intervalo de 1000 dlares entre 1000 y 6000.
/* control 4.1.a */
ACCEPT p_numemp NUMBER PROMPT 'Introduce un nmero de
empleado: '

DECLARE
v_sal emp.sal%TYPE;
BEGIN
SELECT sal
INTO v_sal
FROM emp
WHERE empno = &p_numemp;
IF v_sal>3000 THEN
dbms_output.put_line('Gana mas de 3000 Euros: '||V_SAL);
ELSE
dbms_output.put_line('Gana menos de 3000 Euros: '||V_SAL);
END IF;
END;
/


Ejercicio
Realiza la tabla de multiplicar del 5 (muestrala
por pantalla con dbms_output. put_line).

Repite el ejercicio 3 veces, una con cada tipo de
bucle.


Generaliza el ejercicio anterior a todas las tablas
de multiplicar
/* ejemplo simple */

DECLARE
v_cuenta_emp NUMBER;
i NUMBER;
BEGIN
SELECT count(*)
INTO v_cuenta_emp
FROM emp;
FOR i IN 1 .. v_cuenta_emp LOOP
dbms_output.put_line(Empleado ||v_cuenta_emp);
END LOOP;
END;
/

Tema 5 Tipos compuestos
Registros y tablas PL/SQL

RECORD

TABLE
Ejercicio
Crea un tipo de registro para contener el
nmero de empleado, el departamento, el
salario y la comisin de un empleado

Declara una variable de dicho tipo

Introduce dentro de la misma los datos de
SCOTT
TYPE miemp IS RECORD
(emplenum emp.empno%TYPE,
deptnum emp.deptno%TYPE,
salario emp.sal%TYPE,
comision emp.comm%TYPE);
V_EMP miemp;
BEGIN
SELECT empno, sal, comm, deptno
INTO v_emp.emplenum, v_emp.salario,
v_emp.comision, v_emp.deptnum
FROM emp
WHERE ename = 'SCOTT';
dbms_output.put_line('El empleado Scott ..);
END;
/

Ejercicio
Declare una variable utilizando
%ROWTYPE con la misma estructura que
la tabla empleados.
Rellnela con los datos del empleado
7934
DECLARE
miemple emp%rowtype;
BEGIN
SELECT empno, ename, job, mgr,hiredate, sal,
comm, deptno
INTO miemple
FROM emp
WHERE empno= 7934;
dbms_output.put_line('El empleado
'||miemple.empno||'del departamento
'||miemple.deptno||' gana '||miemple.sal);
END;
/

Ejercicios
Crea una tabla de nmeros y rellena sus
posiciones del 1 al 10, pon en cada una el
cuadrado de su nmero de posicin.
Crea un registro con tres campos, nm1, num2
y resultado. Crea una tabla de este tipo de
registros y rellenal con el contenido de las
tablas multiplicar (los factores estarn en num1
y num2, y el producto en resultado).
TYPE tipo_tabla_num IS TABLE OF NUMBER
INDEX BY BINARY INTEGER;

v_tabla tipo_tabla_num;
BEGIN
FOR cuenta IN 1..10 LOOP
v_tabla[cuenta]:= cuenta*cuenta;
END;
/
ACCEPT p_tabladel NUMBER PROMPT 'Introduce la
tabla que desee: '

TYPE mireg IS RECORD
(num1 INTEGER,
num2 INTEGER,
resultado INTEGER);

DECLARE
v_tablamul IS TABLE OF mireg INDEX BY BINARY
INTEGER;
BEGIN
FOR tab IN 1..10 LOOP
FOR cuenta IN 1..10 LOOP
v_tablamul[cuenta].num1 := tab;
v_tablamul[cuenta].num2 := cuenta;
v_tablamul[cuenta].resultado := cuenta*tab;
END LOOP;
END LOOP;
END;
/

Segunda Parte Ejercicios
significativos
PL/SQL
Cursores
Cursores

Tipos de cursores
mplicitos: declarados automticamente para todas
las sentencias DML y SELECT PL/SQL.
Explcitos: Declarados y nombrados por el
programador.
Control de cursores explcitos
Puntero
Recupere una fila del cursor.
Continue hasta que quede vaco.
Puntero
Puntero





















Cursor
Cursor
Cursor
Abra el cursor.
Declaracin del cursor
No se debe incluir la clusula INTO en la declaracin del cursor.
Se puede usar ORDER BY
CURSOR nombre_cursor IS
sentencia_select;
CURSOR c1 IS
SELECT *
FROM emp
WHERE deptno=10;
Apertura del cursor
Se tiene que abrir el cursor para ejecutar la consulta e
identificar el juego activo.
Si la consulta no devuelve ninguna fila, no se producir
ninguna excepcin.
Utilice los atributos del cursor para comprobar los
resultados producidos tras una recuperacin.
OPEN nombre_cursor;
OPEN c1;
Recuperacin de datos del cursor

Recupera los valores de la fila actual y los introduce en variables de salida.
Debe incluir el mismo nmero de variables que columnas devuelve el
cursor.
Las variables y las columnas estn relacionadas posicionalmente.
Se debe comprobar si el cursor tiene filas.
FETCH nombre_cursor INTO [variable1, variable2, ...]
| nombre_registro];
Recuperacin de datos del cursor (II)
Ejemplo
FETCH c1 INTO v_emp;
...
OPEN c1;
LOOP
FETCH c1 INTO v_emp
EXIT WHEN ...;
...
-- Proceso de recuperar los datos
...
END;
Cierre del cursor
Cierre el cursor una vez completado el procesamiento
de las filas.
Vuelva a abrir el cursor si es necesario
No intente recuperar los datos de un cursor una vez ha
sido cerrado
CLOSE nombre_cursor;
CLOSE c1;
%NOTFOUND Y %ROWCOUNT
Usa %ROWCOUNT para recuperar un nmero exacto de filas

Usa %NOTFOUND para determinar cundo salir del bucle
LOOP
FETCH ename_cursor INTO v_ename,
v_deptno;
IF ename_cursor%ROWCOUNT > 20 THEN
...
EXIT WHEN ename_cursor%NOTFOUND;
...
END LOOP;
Ejercicios
Utiliza un cursor explcito para mostrar los nombres de
los empleados de un departamento (p.ej. El 10) dentro
de un bloque PL/SQL.

Muestra los nombres y el salario de (los 5) empleados
con mayor salario.

DECLARE
CURSOR mi_emp_cursor IS
SELECT ename
FROM emp
WHERE deptno=10;
v_nombre emp.ename%TYPE;
BEGIN
OPEN mi_emp_cursor;
LOOP
FETCH mi_emp_cursor INTO v_nombre;
EXIT WHEN mi_emp_cursor%NOTFOUND;
dbms_output.put_line(v_nombre);
END LOOP;
CLOSE mi_emp_cursor;
END;
/
DECLARE
CURSOR mi_emp_cursor IS
SELECT ename,sal
FROM emp WHERE deptno=10;
v_empleado mi_emp_cursor%ROWTYPE;
BEGIN
OPEN mi_emp_cursor;
LOOP
FETCH mi_emp_cursor INTO v_empleado;
EXIT WHEN mi_emp_cursor%NOTFOUND;
dbms_output.put_line(v_empleado.ename);
END LOOP;
CLOSE mi_emp_cursor;
END;
/
/* Bsqueda de los empleados mejor pagados pej. top 5 */

SQL> CREATE TABLE top_emps
2> (name VARCHAR2(25),
3> salary NUMBER(11,2);
/* creara una nueva tabla en la que almacenarlos */

SET VERIFY OFF
ACCEPT p_num PROMPT Por favor, teclee el nmero de top empleados que
desee listar: ;
DECLARE
v_num NUMBER(3) := &p_num;
v_ename emp.ename%TYPE ;
v_sal emp.sal%TYPE ;
CURSOR emp_cursor IS
SELECT ename, sal
FROM emp
WHERE sal IS NOT NULL
ORDER BY sal DESC;
..
BEGIN
OPEN emp_cursor;
FETCH emp_cursor INTO v_ename, v_sal;
WHILE emp_cursor%ROWCOUNT <= v_num AND
emp_cursor%FOUND LOOP
INSERT INTO top_emps (name, salary)
VALUES (v_name, v_sal);
FETCH emp_cursor INTO v_ename, v_sal;
END LOOP;
CLOSE emp_cursor;
COMMIT;
END;
/
Cursores y registros
Procesa las filas del juego activo de forma conveniente recuperando
valores e introducindolos en una variable de tipo registro.

Ejemplo:

DECLARE
...
CURSOR emp_cursor IS
SELECT empno, sal, hiredate, rowid
FROM emp
WHERE deptno = 20;
emp_record emp_cursor%ROWTYPE;

BEGIN
OPEN emp_cursor;
. . .
FETCH emp_cursor INTO emp_record;

Bucles FOR de cursor
Facilita el procesamiento de cursores explcitos
Apertura, recuperacin y cierre implcitos.
No declarar la variable registro, se declara implcitamente.
FOR nombre_registro IN nombre_cursor LOOP
sentecia1;
sentencia2;
. . .
END LOOP;
Ejercicios
Repite los ejercicios anteriores utilizando un
bucle FOR de cursor.

Introduce los nombres de todos los empleados
en una tabla PL/SQL. Recorre despus la tabla
y muestra los nombres por pantalla.


/* Ejemplo con doble cursor para recuperar todos los
empleados de todos los departamentos */
DECLARE
v_current_deptno dept.deptno%TYPE;
v_emp VARCHAR2(50);
CURSOR dept_cursor IS
SELECT deptno
FROM dept
ORDER BY deptno;
CURSOR emp_cursor (v_deptno NUMBER) IS
SELECT ename || Departamento ||TO_CHAR(deptno)
FROM emp
WHERE deptno = v_deptno;
..

..
BEGIN
OPEN dept_cursor;
LOOP
FETCH dept_cursor INTO v_current_deptno;
EXIT WHEN dept_cursor%NOTFOUND;
IF emp_cursor%ISOPEN THEN
CLOSE emp_cursor;
END IF;
OPEN emp_cursor (v_current_deptno);
LOOP
FETCH emp_cursor INTO v_emp;
EXIT WHEN emp_cursor%NOTFOUND;
dbms_output.put_line (Recuperado empleado || v_emp.ename);
END LOOP;
CLOSE emp_cursor;
END LOOP;
CLOSE dept_cursor;
COMMIT;
END;
/

PL/SQL
Excepciones
Gestin de excepciones
Interrumpir la Excepcin Propagar la Excepcin
Surge la excepcin
Se interrumpe la
excepcin
Surge la excepcin
La excepcin se propaga
al entorno de llamadas
DECLARE
BEGIN
EXCEPTION
END;
DECLARE
BEGIN
EXCEPTION
END;
No se interrumpe
la excepcin
Tipos de excepciones
Predefinidas por el Servidor
Oracle

No predefinida por el
Servidor


Definida por el usuario
Provocada
implcitamente
Provocada
explcitamente
Interrupcin de excepciones
EXCEPTION
WHEN excepcin1 [OR excepcin2 . . .] THEN
sentencia1;
sentencia2;
. . .
[WHEN excepcin3 [OR excepcin4 . . .] THEN
sentencia1;
sentencia2;
. . .]
[WHEN OTHERS THEN
sentencia1;
sentencia2;
. . .]
Interrupcin de Errores
Predefinidos por el Servidor Oracle
Haga referencia al nombre estndar en la rutina
de gestin de excepciones
Ejemplos de excepciones predefinidas
NO_DATA_FOUND
TOO_MANY_ROWS
INVALID_CURSOR
ZERO_DIVIDE
DUP_VAL_ON_INDEX
LOGIN_DENIED
Ejercicio
Del nombre de un empleado introducido durante
ACCEPT crea un bloque PL/SQL que devuelva
su nmero y su salario.

Trata los siguientes casos mostrando el
mensaje correspondiente:
No existe ningn empleado con ese nombre
Existen varios empleados con ese nombre
Se produce otro error distinto
/* Ejemplo - Bsqueda de empleados a partir de sueldo con excepciones */

SET VERIFY OFF

ACCEPT p_sal PROMPT Por favor, especifique el salario por considerar: ;

DECLARE
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE := &p_sal;

BEGIN
SELECT ename
INTO v_ename
FROM emp
WHERE sal = v_sal;
dbms_output.put_line(v_empleado.ename|| gana ||v_sal||Euros anuales);
..





..
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line(No existe ningn empleado que
gane||v_sal||Euros anuales);
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line(Existe ms de un empleado que
gane||v_sal||Euros anuales);
WHEN OTHERS THEN
WHEN no_data_found THEN
dbms_output.put_line(Error inesperado Avise al Servicio de
Informtica);
END;
/

Interrupcin de Excepciones
definidas por el usuario
Declare Reference
Seccin
declarativa
Seccin de gestin
De excepciones
Especifique la
excepcin
Gestione la
excepcin
Raise
Provoque
explcitamente la
excepcin utilizando la
sentencia RAISE
Seccin
Ejecutable
/* Ejemplo ahora con manejador que comunique mensaje a usuario si el
departamento no existe */

SET VERIFY OFF

ACCEPT p_deptno PROMPT Por favor, especifique el departamento: ;
ACCEPT p_loc PROMPT Especifique la localizacion del departamento: ;

DECLARE
e_dept_no_valido EXCEPTION;
v_deptno dept.deptno%TYPE := &p_deptno;

BEGIN
UPDATE dept
SET loc = &&p_loc
WHERE deptno = v_deptno;
..






..
IF SQL%NOTFOUND THEN
RAISE e_dept_no_valido
END IF;
COMMIT;

EXCEPTION
WHEN e_dept_no_valido THEN
dbms_output.put_line(El departamento
||TO_CHAR(v_deptno) ||introducido no es un departamento
vlido);
END;
/

PL/SQL
Disparadores
Creacin de disparadores
Momento:
BEFORE
AFTER

Evento
INSERT
UPDATE
DELETE
Nombre de tabla:
ON table

Tipo de trigger
Fila
Sentencia

WHEN condicin

Cuerpo del trigger
Momento de un disparador
BEFORE
No queremos permitir que se complete la sentencia del
disparador en caso de una excepcin o de no cumplir una
comprobacin realizada en el disparador.
Derivar valores de columna antes de completar una sentencia
INSERT o UPDATE
AFTER
Cuando queremos que la sentencia se complete antes de
ejecutar la accin del disparador.
Para completar acciones del disparador BEFORE.
Para realizar auditoras
Tipos de disparador
Cuntas veces se ejecuta un disparador?

Sentencia
Una nica vez cuando se produce el eventos (incluso si no
afecta a ningn registro)

Fila
Una vez por cada registro de la tabla afectada por el evento.
Cuerpo del disparador
Accin a realizar por el disparador

Se especifica mediante un bloque PL/SQL

Los disparadores tiene acceso a los valores de
columna nuevos y viejos del registro que se est
procesando.
Sintaxis
CREATE [OR REPLACE] TRIGGER
nombre_disparador
Tiempo evento1 [OR evento2 OR evento3]
ON nombre_tabla
BLOQUE PL/SQL;
Ejercicios
Crea un disparador que antes de insertar en la
tabla empleados compruebe que sea una hora
laborable (entre las 8:00 am y las 20:00 pm) y si
no es as que provoque un error.

CREATE OR REPLACE PROCEDURE operacin_segura
IS
BEGIN
IF TO_CHAR(SYSDATE, HH24:MI NOT BETWEEN 08:00 AND
20:00
OR TO_CHAR(SYSDATE, DY) IN (SAT, SUN) THEN
RAISE _APPLICATION_ERROR (-20205, Usted no est
autorizado a acceder fuera de horas normales de trabajo);
END IF;
END operacin_segura;
/

/* creacin de un disparador que llame al procedimiento de arriba */
/* en caso necesario */

CREATE OR REPLACE TRIGGER modo_seguro
BEFORE INSERT OR UPDATE OR DELETE on emp;
BEGIN
modo_seguro;
END modo_seguro;
/