Descargar - Universidad de Antioquia

Anuncio
Procedimientos
almacenados en PL/SQL
Jonathan Medina Gómez
Facultad de Ingeniería
Universidad de Antioquia
Ventajas...
● Los bloques se pueden almacenar en forma
permanente mediante subprogramas
(funciones y procedimientos) para usarlos
repetidamente.
● Los subprogramas pueden llevar
argumentos (parámetros).
Sea la tabla:
CREATE TABLE registro(
id_usuario VARCHAR2(10),
fecha DATE,
estacion VARCHAR2(15)
);
CREATE OR REPLACE PROCEDURE registrarse IS
BEGIN
INSERT INTO registro
VALUES (USER, SYSDATE, USERENV('TERMINAL'));
END;
/
Para ejecutarlo en SQL*Plus:
EXECUTE registrarse;
Procedimientos
Sintaxis:
CREATE [OR REPLACE] PROCEDURE nombre_procedimiento
[( arg1 [modo] tipo [, arg2 [modo] tipo...])]
IS | AS
[<<Declaración de variables>>]
BEGIN
<<Instrucciones>>
[EXCEPTION]
<<Manejador de excepciones>>
END;
A tener en cuenta...
● Se debe especificar la opción REPLACE cuando ya exista
el procedimiento y se desee reemplazar.
● Se puede usar AS o IS (son equivalentes).
● El bloque PL/SQL empieza, ya sea con la palabra BEGIN,
o con la declaración de las variables locales (sin usar la
palabra DECLARE).
● Para ver los errores de compilación se puede usar el
comando SHOW ERRORS en SQL*Plus.
● No se puede especificar tamaño para los parámetros en lo
que respecta al tipo de datos.
A tener en cuenta...
● El "modo" especifica el tipo de argumento, el cual puede
ser :
○ IN (el predeterminado): Parámetro de entrada al
subprograma.
○ OUT: Parámetro de salida. El subprograma devuelve
un valor en el parámetro.
○ IN OUT: Parámetro de entrada y salida. El
subprograma devolverá un valor posiblemente
diferente al enviado originalmente.
Ejemplo
CREATE OR REPLACE PROCEDURE consulta_emp (v_nro IN emp.cod%TYPE)
IS
v_nom emp.nom%TYPE;
BEGIN
SELECT nom INTO v_nom FROM emp
WHERE cod = v_nro;
DBMS_OUTPUT.PUT_LINE(v_nom);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Empleado no existe');
END;
/
Parámetros de salida
Si el procedimiento retorna un parámetro de salida se debe
declarar una variable en SQL*Plus para verlo, así:
● VAR nom_var TIPO;
● Invocar el subprograma con los parámetros
correspondientes (las vbles de SQL*PLUS se preceden
con ‘:’ ).
● Imprimir: PRINT nom_var; (no se requiere ‘:’ para
imprimir).
● Los parámetros de salida usualmente son recibidos por
otros subprogramas que los invocan.
Ejemplo
CREATE OR REPLACE PROCEDURE consulta_emp
(v_nro IN emp.cod%TYPE, v_nom OUT emp.nom%TYPE)
IS
BEGIN
SELECT nom INTO v_nom -- Se llena el parámetro de salida
FROM emp
WHERE cod = v_nro;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Empleado no existe');
END;
/
Invocación desde SQL*Plus:
SQL> VAR a VARCHAR2(10);
SQL> EXECUTE consulta_emp(15, :a);
SQL> PRINT a;
Invocación desde un subprograma
CREATE OR REPLACE PROCEDURE
invoca_consulta(v_nro IN emp.cod%TYPE)
IS
nombre emp.nom%TYPE;
Retornará la variable nombre
BEGIN
consulta_emp(v_nro, nombre);
DBMS_OUTPUT.PUT_LINE('El nombre del empleado es: '|| nombre);
END;
/
Para ejecutar:
EXECUTE invoca_consulta(15);
Ejemplo
● Sea el modelo:
Instrucciones (DDL)
Sean las tablas:
CREATE TABLE cliente( ced NUMBER(8) PRIMARY KEY,
nom VARCHAR2(10) NOT NULL);
CREATE TABLE empleo( ced NUMBER(8) REFERENCES cliente,
nit_empresa INTEGER,
valor_mensual NUMBER(6) NOT NULL,
PRIMARY KEY (ced, nit_empresa));
CREATE TABLE gasto( cod_gasto NUMBER(8) PRIMARY KEY,
ced NUMBER(8) REFERENCES cliente,
valor_mensual NUMBER(6),
desc_gasto VARCHAR2(10));
Realizar...
Los subprogramas necesarios para imprimir la
cédula de cada cliente y la diferencia entre
todo lo que devenga y todo lo que se gasta.
Soluciones
Versión 1:
Total gastos
CREATE OR REPLACE PROCEDURE
totalg(
codigo cliente.ced%TYPE,
total OUT NUMBER) IS
BEGIN
SELECT NVL(SUM(valor_mensual),0)
INTO total
FROM gasto WHERE ced = codigo;
END;
/
Total ingresos
CREATE OR REPLACE PROCEDURE
totali(
codigo cliente.ced%TYPE,
total OUT NUMBER) IS
BEGIN
SELECT NVL(SUM(valor_mensual),0)
INTO total
FROM empleo WHERE ced = codigo;
END;
/
Soluciones
Versión 2:
CREATE OR REPLACE PROCEDURE total(
codigo cliente.ced%TYPE,
tabla VARCHAR,
total OUT NUMBER) IS
BEGIN
EXECUTE IMMEDIATE 'SELECT NVL(SUM(valor_mensual),0)
FROM ' || tabla || ' WHERE ced = ' || codigo
INTO total;
END;
/
* Compacto pero puede ser peligroso en rendimiento...
Solución completa
Por lo tanto la solución completa usando el procedimiento total anterior es:
CREATE OR REPLACE PROCEDURE neto IS
totalg NUMBER(8);
totali NUMBER(8);
BEGIN
FOR mis_emp IN (SELECT * FROM cliente) LOOP
total(mis_emp.ced,'empleo',totali);
total(mis_emp.ced,'gasto',totalg);
DBMS_OUTPUT.PUT_LINE(mis_emp.ced ||' Total neto:
'|| (totali - totalg));
END LOOP;
END;
/
¿Cómo hubiera sido la respuesta del
problema usando sólo SQL puro?
●
Mediante subconsultas correlacionadas:
SELECT ced,((SELECT NVL(SUM(valor_mensual),0)
FROM empleo
WHERE ced = c.ced) ( SELECT NVL(SUM(valor_mensual),0)
FROM gasto
WHERE ced = c.ced)) AS total
FROM cliente c;
Pero las subconsultas correlacionadas son en general costosas...
¿Cómo hubiera sido la respuesta del
problema usando sólo SQL puro?
Mediante OUTER JOINS:
SELECT ced, (NVL(sumae,0) - NVL(sumag,0)) AS total
FROM
( cliente NATURAL LEFT OUTER JOIN
(SELECT ced, SUM(valor_mensual) AS sumae
FROM empleo
GROUP BY ced))
NATURAL LEFT OUTER JOIN
(SELECT ced, SUM(valor_mensual) AS sumag
FROM gasto
GROUP BY ced
);
Ejercicio en clase
● Realice un procedimiento almacenado que reciba como
parámetros el nombre y la cédula de un cliente, el nit de la
empresa en la cual trabaja y el salario que gana, tenga en
cuenta lo siguiente:
○ Si el registro (cliente, empleo) no existe se debe crear.
○ Si el registro ya existe se debe modificar.
○ Se debe mostrar un mensaje al usuario para que sepa
cuál de las operaciones fue la que se realizó.
○ Tenga en cuenta las posibles excepciones!!!
Descargar