CURSORES EVENTOS TRIGGERS - APUNTES 2 ASIR

28
RUTINAS/TAREAS ALMACENADAS (continuación) 9.- CURSORES. Normalmente los procedimientos implican la manipulación de BD pudiéndose utilizar cualquier instrucción SQL, es decir, pertenecientes al DDL, DML o DCL. Ejemplo: DELIMITER $$ DROP PROCEDURE IF EXISTS simple_sql$$ CREATE PROCEDURE simple_sql( ) BEGIN DECLARE i INT; SET i = 1; DROP TABLE IF EXISTS testtable; CREATE TABLE testtable ( id INT PRIMARY KEY, dato VARCHAR(30) ); WHILE i <= 10 DO INSERT INTO testtable VALUES (i, CONCAT (“registro “, i) ) ; SET i = i + 1; END WHILE; SET i = 5; UPDATE testtable SET dato = CONCAT (“actualizado “, i) WHERE id = i; DELETE FROM testtable WHERE id > i; END$$ >CALL simple_sql( )$$ SELECT * FROM testtable$$ Podemos usar la sentencia SELECT para enviar valores a variables (usando INTO). Ejemplo: DELIMITER $$ DROP PROCEDURE IF EXISTS simple_sql1$$ CREATE PROCEDURE simple_sql1(job VARCHAR(10)) BEGIN DECLARE emple INT(4); DECLARE apell VARCHAR(30); SELECT emp_no, apellido INTO emple, apell FROM empleados 2º ASIR - Administración de Sistemas Gestores de Bases de Datos 1/28

description

APUNTES Y EJERCICIOS DE BASE DE DATOS

Transcript of CURSORES EVENTOS TRIGGERS - APUNTES 2 ASIR

PROCEDIMIENTOS ALMACENADOS (continuacin)

RUTINAS/TAREAS ALMACENADAS (continuacin)

9.- CURSORES.

Normalmente los procedimientos implican la manipulacin de BD pudindose utilizar cualquier instruccin SQL, es decir, pertenecientes al DDL, DML o DCL.

Ejemplo:

DELIMITER $$

DROP PROCEDURE IF EXISTS simple_sql$$

CREATE PROCEDURE simple_sql( )BEGIN

DECLARE i INT;

SET i = 1;DROP TABLE IF EXISTS testtable;

CREATE TABLE testtable ( id INT PRIMARY KEY, dato VARCHAR(30) );

WHILE i i;

END$$

>CALL simple_sql( )$$

SELECT * FROM testtable$$

Podemos usar la sentencia SELECT para enviar valores a variables (usando INTO).

Ejemplo:DELIMITER $$

DROP PROCEDURE IF EXISTS simple_sql1$$

CREATE PROCEDURE simple_sql1(job VARCHAR(10))BEGIN

DECLARE emple INT(4);

DECLARE apell VARCHAR(30);

SELECT emp_no, apellido INTO emple, apell FROM empleados

WHERE oficio = job;

/* procesamos los datos obtenidos, por ejemplo mostrndolos */

SELECT emple, apell, job;

END$$>CALL simple_sql1(PRESIDENTE)$$

Si queremos recuperar ms de una fila para manipular sus datos, la sentencia anterior no sirve y necesitamos usar los cursores. Conceptualmente los cursores se asocian a un conjunto de filas o una consulta sobre la BD. Nos permite recuperar los datos de una consulta select en un procedimiento almacenado de forma similar a como se recuperan los datos de un archivo con un lenguaje de programacin procedural.

>CALL simple_sql1(VENDEDOR)$$ da ERROR 1172 ..Los cursores se utilizan para procesar individualmente cada una de las filas que hemos obtenido como resultado de una consulta SELECT. En las versiones de MySQL superior a 5.x los cursores tienen las siguientes propiedades:

- Solo lectura: Los valores en el cursor no se pueden actualizar

- No scrollable: Solo se puede recorrer en una direccin y no se pueden dar saltos ni mover hacia delante y hacia atrs en el conjunto de filas.

- Insensible: Se debe evitar hacer actualizaciones en la tabla asociada mientras el cursor est abierto ya que se pueden obtener resultados inesperados.

MySQL facilita las siguientes sentencias para trabajar con cursores:

Lo primero que hay que hacer es declarar el cursor usando la sentencia DECLARE.DECLARE cursor_name CURSOR FOR SELECT_statement;

Despus, se abrir el cursor con la sentencia OPEN. Hay que abrir el cursor para poder trabajar con las filas almacenadas en el cursor.

OPEN cursor_name;

A continuacin, para ir recuperando filas guardadas en el cursor e ir avanzando a travs de las diferentes filas se usa la sentencia FETCH.

FETCH cursor_name INTO variable list;

Por ltimo, hay que cerrar el cursor para desactivarlo y liberar la memoria asignada al cursor. Para cerrar el cursor se usa la sentencia CLOSE:

CLOSE cursor_name;

Algunas funciones tiles para manejar los cursores (tambin disparadores y eventos) son:

FOUND_ROWS( )devuelve el nmero de filas mostradas en la ltima sentencia select sin la clasula limit.

Ejemplo:

SELECT * FROM departamentos;

SELECT FOUND_ROWS( );

ROW_COUNT( )devuelve el nmero de filas afectadas por la ltima sentencia insert, update o delete, es decir, devuelve el nmero de filas insertadas, actualizadas o borradas de una tabla. Si devuelve el valor -1, indica que la ltima sentencia fue una select.

Ejemplo:

SELECT ROW_COUNT( );

INSERT INTO departamentos VALUES (70, MARKETING, SEVILLA);

SELECT ROW_COUNT( );

Veremos un ejemplo de un cursor que muestre todos los cdigos y su descripcin de la tabla productos (usamos la funcin FOUND_ROWS( ) que nos dice el nmero de filas seleccionadas por la ltima instruccin select):

DELIMITER $$

DROP PROCEDURE IF EXISTS mis_productos$$

CREATE PROCEDURE mis_productos( )BEGIN

DECLARE codigo INT(2);

DECLARE descri VARCHAR(50);

DECLARE num_filas INT;

DECLARE cur_prod CURSOR FORSELECT producto_no, descripcion FROM productos;

/* Abrimos el cursor y guardamos su nmero de filas */OPEN cur_prod;SELECT FOUND_ROWS( ) INTO num_filas;

SELECT num_filas;IF num_filas = 0 THEN

SELECT La tabla de productos est vaca ;

END IF;

WHILE (num_filas 0) DO

FETCH cur_prod INTO codigo, descri;

SELECT codigo, descri;/* Mostramos los datos de cada fila */END WHILE;

END$$

>CALL mis_productos( )$$

..ERROR 1329 (02000): No data zero rows fetched, selected, or processedEl procedimiento funciona pero al final da un mensaje de error ya que el bucle intenta seguir leyendo ms all del final del cursor. Lo elegante es evitar que se produzca o usar un manejador de error:

Solucin 1: /* Solo va a leer tantas veces como le indica el nmero de filas */DELIMITER $$

DROP PROCEDURE IF EXISTS mis_productos1$$

CREATE PROCEDURE mis_productos1( )

BEGIN

DECLARE codigo INT(2);

DECLARE descri VARCHAR(50);

DECLARE num_filas INT;

DECLARE cur_prod CURSOR FORSELECT producto_no, descripcion FROM productos;

/* Abrimos el cursor y guardamos su nmero de filas */OPEN cur_prod;SELECT FOUND_ROWS( ) INTO num_filas;

IF num_filas = 0 THEN

SELECT La tabla de productos est vaca ;

END IF;

WHILE (num_filas 0) DO

FETCH cur_prod INTO codigo, descri;

SELECT codigo, descri;/* Mostramos los datos de cada fila */SET num_filas = num_filas -1;END WHILE;

END$$

>CALL mis_productos1( )$$

Solucin 2: /* Utilizamos un manejador de tipo no encuentro ms */DELIMITER $$

DROP PROCEDURE IF EXISTS mis_productos2$$

CREATE PROCEDURE mis_productos2( )BEGINDECLARE no_mas INT DEFAULT 0;DECLARE codigo INT(2);

DECLARE descri VARCHAR(50);

DECLARE cur_prod CURSOR FOR

SELECT producto_no, descripcion FROM productos;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_mas = 1;/* Abrimos el cursor y guardamos su nmero de filas */OPEN cur_prod;A1: LOOP FETCH cur_prod INTO codigo, descri;

IF no_mas = 1 THEN LEAVE A1;END IF;SELECT codigo, descri;/* Mostramos los datos de cada fila */END LOOP A1;

END$$

DELIMITER $$

DROP PROCEDURE IF EXISTS mis_productos2$$

CREATE PROCEDURE mis_productos2( )BEGINDECLARE no_mas INT DEFAULT 0;DECLARE codigo INT(2);

DECLARE descri VARCHAR(50);

DECLARE cur_prod CURSOR FOR

SELECT producto_no, descripcion FROM productos;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_mas = 1;/* Abrimos el cursor y guardamos su nmero de filas */OPEN cur_prod;WHILE no_mas = 0 DO

FETCH cur_prod INTO codigo, descri;

SELECT codigo, descri;/* Mostramos los datos de cada fila */END WHILE;

END$$

>CALL mis_productos2( )$$9.1.- CONDICIONES Y MANEJADORES DE ERROR (HANDLERS)Cuando en un programa se produce un error inesperado hay 2 posibilidades de actuacin:

Dejar que el programa termine inmediatamente para que el SGBD tome el control y lo resuelva a su manera.

Escribir cdigo en el propio programa para que gestione el error.

Se llama condicin o excepcin, al error que se produce en tiempo de ejecucin. Cada excepcin debe tener su manejador correspondiente, que se encargar de procesar el error. Su declaracin es la siguiente:

DECLARE tipo_manejador HANDLER FOR valor_condicin [, ] instruccin;

Tipo_manejador: CONTINUE | EXIT | UNDO

Valor_condicin: SQLSTATE valor | SQLWARNING | NOT FOUND | Instruccin: puede ser nica o varias en un bloque BEGIN END

CONTINUE, contina la rutina actual que provoc el error despus de ejecutar la instruccin de manejador.EXIT, termina la ejecucin.

UNDO, deshace lo ya hecho, aunque de momento no est implementado.

SQLSTATE valor, cdigo de error de SQL.

SQLWARNING, incluye todos los cdigos SQLSTATE que empiezan por 01 (warnings)

NOT FOUND, los que empiezan por 02 (no encontrados)

Cuando se trabaja con cursores lo ms habitual es usar un manejador (handler) para evitar que se produzca un error no data to fetch cuando se llega al final del cursor y ya no quedan ms filas que leer, es decir:

DECLARE CONTINUE HANDLER FOR NOT FOUND instruccin; Ejemplo: Cursor con manejador que recorre el resultado de una SELECT y muestra el total de registros encontrados:DELIMITER $$

DROP PROCEDURE IF EXISTS simple_sql2$$

CREATE PROCEDURE simple_sql2(job VARCHAR(10))BEGIN

DECLARE no_mas INT DEFAULT 0;DECLARE total INT DEFAULT 0;

DECLARE regi VARCHAR(255);DECLARE cur_of CURSOR FOR SELECT apellido FROM empleados WHERE oficio = job;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_mas = 1;OPEN cur_of;B1: LOOP FETCH cur_of INTO regi;

IF no_mas = 1 THEN LEAVE B1;END IF;SET total = total +1;

SELECT regi;END LOOP B1;

CLOSE cur_of;SELECT total;

END$$>CALL simple_sql2(VENDEDOR)$$

Ejemplo: En este procedimiento se define un cursor sobre la tabla PRODUCTOS y se realiza un bucle que comprueba si la cantidad en stock de cada uno de los productos es menor que 25. En caso de que el stock sea menor que 25, se guarda informacin de dicho producto en una tabla temporal. Una vez procesados todos los registros de la tabla PRODUCTOS se visualiza en pantalla la tabla temporal y se elimina.

Inicialmente se han declarado tanto el cursor como el manejador del error NOT FOUND para que no se produzca un error cuando se llegue al finalDELIMITER $$

DROP PROCEDURE IF EXISTS CursorProc$$

CREATE PROCEDURE CursorProc( )

BEGINDECLARE no_mas, cant_stock INT DEFAULT 0;

DECLARE prd_code VARCHAR(255);

DECLARE cur_prod CURSOR FOR SELECT producto_no FROM productos;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_mas = 1;

/* para guardar informacin de log en infologs */

CREATE TABLE infologs (

id INT(11) NOT NULL AUTO_INCREMENT,

msg VARCHAR(255) NOT NULL,

PRIMARY KEY (Id)

);

OPEN cur_prod;

FETCH cur_prod INTO prd_code;REPEAT

SELECT stock INTO cant_stock FROM productos

WHERE producto_no = prd_code;

IF cant_stock < 25 THEN

INSERT INTO infologs(msg) VALUES (prd_code);

END IF;

FETCH cur_prod INTO prd_code;

UNTIL no_mas = 1END REPEAT;CLOSE cur_prod;

SELECT * FROM infologs;

DROP TABLE infologs;

END$$

DELIMITER ;9.2.- EJERCICIOS

1. Hacer un procedimiento sobre la BD ventas que muestre los datos de los empleados que no son vendedores y el nombre de su departamento.

Delimiter //

drop procedure if exists novendedores//

create procedure novendedores()

begin

declare no_mas int default 0;

declare apellido_c varchar(30);

declare oficio_c varchar(10);

declare fecha_alta_c date;

declare dep_no_c int(2);

declare dpto varchar(30);

declare cursor_noven cursor for select apellido, oficio, fecha_alta, dep_no from empleados where oficio"vendedor";

Declare continue handler for not found set no_mas=1;

open cursor_noven;

fetch cursor_noven into apellido_c,oficio_c,fecha_alta_c,dep_no_c;

while no_mas=0 do

select dnombre from departamentos where dep_no=dep_no_c into dpto;

select apellido_c, oficio_c, fecha_alta_c,dpto;

fetch cursor_noven into apellido_c,oficio_c,fecha_alta_c,dep_no_c;

end while;

close cursor_noven;

end//

delimiter ;

drop procedure if exists novendedor7;

delimiter //

create procedure novendedor7()

begin

declare no_mas int default 0;

declare emp_no_c int;

declare apellido_c varchar(30);

declare oficio_c varchar(30);

declare dep_no_c int;

declare dnombre_c varchar(30);

declare cursor40 cursor for select empleados.emp_no,empleados.apellido, empleados.oficio, empleados.dep_no, departamentos.dnombre from empleados, departamentos

where empleados.dep_no=departamentos.dep_no and oficio!="vendedor";

declare continue handler for not found set no_mas=1;

open cursor40;

fetch cursor40 into emp_no_c, apellido_c, oficio_c, dep_no_c, dnombre_c;

while no_mas=0 do

select emp_no_c, apellido_c, oficio_c, dep_no_c, dnombre_c;

fetch cursor40 into emp_no_c, apellido_c, oficio_c, dep_no_c, dnombre_c;

end while;

close cursor40;

end //

delimiter ;

call novendedor7();

2. Al ejercicio anterior, aadir que para los empleados cuyo departamento sea MADRID, le subamos el sueldo un 10%.

Delimiter //

drop procedure if exists novendedorsal//

create procedure novendedorsal()

begin

declare no_mas int default 0;

declare apellido_c varchar(30);

declare oficio_c varchar(10);

declare fecha_alta_c date;

declare dep_no_c int(2);

declare dpto varchar(30);

declare salario_c decimal(6,2);

declare cursor_noven cursor for select apellido, oficio, salario, fecha_alta, dep_no from empleados where oficio"vendedor";

Declare continue handler for not found set no_mas=1;

open cursor_noven;

fetch cursor_noven into apellido_c,oficio_c,salario_c, fecha_alta_c,dep_no_c;

while no_mas=0 do

if dep_no_c=30 then

update empleados set salario=salario+(salario*0.1) where apellido=apellido_C;

select dnombre from departamentos where dep_no=dep_no_c into dpto;

select apellido_c, oficio_c, salario_c,fecha_alta_c,dpto;

select "Salario del Empleado Actualizado.";

end if;

select dnombre from departamentos where dep_no=dep_no_c into dpto;

select apellido_c, oficio_c, salario_c,fecha_alta_c,dpto;

fetch cursor_noven into apellido_c,oficio_c, salario_c,fecha_alta_c,dep_no_c;

end while;

close cursor_noven;

end//

delimiter ;OTRA FORMA:

Delimiter //

drop procedure if exists novendedorsal//

create procedure novendedorsal()

begin

declare no_mas int default 0;

declare apellido_c varchar(30);

declare oficio_c varchar(10);

declare fecha_alta_c date;

declare dep_no_c int(2);

declare dpto varchar(30);

declare salario_c decimal(6,2);

declare cursor_noven cursor for select apellido, oficio, salario, fecha_alta, dep_no from empleados where oficio"vendedor" and dep_no in(select dep_no from departamentos where localidad="madrid");

Declare continue handler for not found set no_mas=1;

open cursor_noven;

fetch cursor_noven into apellido_c,oficio_c,salario_c, fecha_alta_c,dep_no_c;

while no_mas=0 do

update empleados set salario=salario+(salario*0.1) where apellido=apellido_C;

select dnombre from departamentos where dep_no=dep_no_c into dpto;

select apellido_c, oficio_c, salario_c,fecha_alta_c,dpto;

fetch cursor_noven into apellido_c,oficio_c, salario_c,fecha_alta_c,dep_no_c;

end while;

close cursor_noven;

end//

delimiter ;

3. EJERCICIOS DE BAJADA DE SUELDOS:

Con motivo de la crisis econmica se va a bajar el sueldo a todos los empleados un 5%. Implementar en MySQL el proceso que realice la bajada de sueldos (usando cursores).

Delimiter //

drop procedure if exists bajada//

create procedure bajada()

begin

declare dni char(9);

declare salario decimal(8,2);

declare no_mas int default 0;

declare cursor_baja cursor for select nif, sueldo from nominas;

Declare continue handler for not found set no_mas=1;

open cursor_baja;

fetch cursor_baja into dni,salario;

while no_mas=0 do

update nominas set sueldo=sueldo-(sueldo*0.05) where nif=dni;

select nif,sueldo from nominas where nif=dni;

fetch cursor_baja into dni,salario;

end while;

close cursor_baja;

end//

delimiter ;Los datos de los salarios de los empleados se guardan en la tabla nminas

CREATE TABLE nominas (

nif CHAR(9) PRIMARY KEY,

sueldo NUMERIC (8,2) NOT NULL);

En la hilera: DB_Bajada_Sueldos.sql tenemos las siguientes instrucciones SQL para la creacin de las tablas de estos ejercicios

DROP DATABASE IF EXISTS practica_scripts;

CREATE DATABASE practica_scripts;

USE practica_scripts;

DROP TABLE IF EXISTS nominas;

CREATE TABLE nominas (

nif CHAR(9) PRIMARY KEY,

sueldo NUMERIC(8,2) NOT NULL);

INSERT INTO nominas VALUES ("1234678A", 15000.00),

("1234678B", 14000.00),

("1234678C", 25000.00),

("1234678D", 20000.00),

("1234678E", 15000.00),

("1234678F", 12000.00),

("1234678G", 10000.00),

("1234678H", 9000.00),

("1234678I", 11500.00),

("1234678J", 15750.00),

("1234678K", 18000.00);

CREATE TABLE nominas2 (

nif CHAR(9) PRIMARY KEY,

grupo CHAR(1) NOT NULL,

sueldo NUMERIC (8,2) NOT NULL);

INSERT INTO nominas2 VALUES ("1234678A", 'B', 15000.00),

("1234678B", 'B', 14000.00),

("1234678C", 'A', 25000.00),

("1234678D", 'A', 20000.00),

("1234678E", 'B', 15000.00),

("1234678F", 'B', 12000.00),

("1234678G", 'C', 10000.00),

("1234678H", 'D', 9000.00),

("1234678I", 'C', 11500.00),

("1234678J", 'B', 15750.00),

("1234678K", 'A', 18000.00);

4. Implementar una segunda versin del procedimiento de bajada de sueldo. Consiste en bajar el sueldo a todos los empleados en base a un determinado porcentaje en funcin a su escala salarial. Para ello:

a) Crear una nueva tabla nominas2 aadiendo a la tabla nominas un campo que represente la escala salarial. El campo se llamar grupo y ser de tipo CHAR(1)

CREATE TABLE nominas2 (

Nif CHAR(9) PRIMARY KEY,

Grupo CHAR(1) NOT NULL,

Sueldo NUMERIC (8,2) NOT NULL);

b) Crear una funcin llamada dame_porcentaje que acepte como parmetro la escala salarial y devuelva el porcentaje de disminucin a aplicar. Los porcentajes de bajada que devolver sern 10%, 8%, 5% y 3% para los grupos A, B, C y D, respectivamente.

Delimiter //

drop function if exists bajadas//

create function bajadas(clase char(1))

returns INT

begin

declare porcentaje int default 0;

if clase="A" then set porcentaje=10;

elseif clase="B" then set porcentaje=8;

elseif clase="C" then set porcentaje=5;

elseif clase="D" then set porcentaje=3;

else set porcentaje=0;

end if;

return porcentaje;

end//

delimiter ;

c) Crear una nueva versin del procedimiento baja_sueldos para que llame a la funcin dame_porcentaje para obtener el porcentaje de disminucin de salario.

5. Modificar la funcin dame_porcentaje para que extraiga de una tabla los porcentajes de bajada en funcin del grupo. La tabla deber tener dos campos, uno con el grupo (A, B, ) y otro campo con el porcentaje a descontar. Crear una tercera versin del procedimiento baja_sueldos para que use la nueva funcin.

CREATE TABLE porcentaje_grupo (

grupo CHAR(1) PRIMARY KEY,

porcentaje INTEGER(3) NOT NULL);

INSERT INTO porcentaje_grupo

VALUES (A, 10), (B, 7), (C, 5), (D, 3), (E, 0);

6. Modifica el procedimiento almacenado baja_sueldos para que adems guarde en una tabla auxiliar cantidad_reducida (#nif_empleado, cantidad) la cantidad que se va a reducir el sueldo de cada uno de los empleados. Si ya existieran datos sobre el empleado en la tabla cantidad_reducida, el valor calculado sustituir al valor anterior.

CREATE TABLE cantidad_reducida (

Nif_empleado CHAR(9) PRIMARY KEY,

cantidad NUMERIC (6,2) NOT NULL);

10.- TRIGGERS o DISPARADORES.

Un trigger o disparador es un tipo especial de rutina almacenada que se activa o ejecuta cuando en una tabla ocurre un evento de tipo INSERT, DELETE o UPDATE. Es decir implementan una funcionalidad asociada a cualquier cambio en una tabla.

Muchas veces se conocen como reglas ECA (Evento Condicin Accin) ya que para definir un disparador es necasario especificar:

- Evento, que es la operacin que provoca la activacin (INSERT, DELETE o UPDATE).

- Condicin, es la condicin de debe cumplirse para que se active el disparador.

- Accin, instrucciones que ejecuta el disparador cuando se activa.Su sintaxis general es:

NombreCREATE TRIGGER nom_disparador

Evento{BEFORE | AFTER } {INSERT | DELETE | UPDATE [OF columnas]} ON tabla

[REFERENCING OLD [ROW] [AS] vieja_filaNEW [ROW] [AS] nueva_filaOLD TABLE [AS] vieja_tablaNEW TABLE [AS] nueva_tabla ]

[{FOR EACH ROW | FOR EACH STATEMENT }]

Condicin[WHEN (condicin)]

Accin{ sentencia_sq;l | BEGIN sentencia_sql_1; sentencia_sql_n; END }

USE ebanca; (antes ejecutar el DB_ebanca.sql)Ejemplo: Crear un disparador que sume las cantidades insertadas en la variable de usuario @sum, cada vez que se introduce un nuevo movimiento

CREATE TRIGGER insertar_mov BEFORE INSERT ON movimiento

FOR EACH ROW SET @sum = @sum + NEW.cantidad;

Para ver su funcionamiento:SET @sum = 10000;

SELECT @sum;INSERT INTO movimiento VALUES ('11111111', '2011-11-20', 2000, 57, 1);

SELECT @sum;

Las instrucciones para gestionar los disparadores son: CREATE TRIGGER, SHOW TRIGGER y DROP TRIGGER.La sintaxis ms utilizada y sencilla es:

CREATE TRIGGER nombre_disp momento_disp evento_disp

ON nombre_tabla FOR EACH ROW sentencia_disp;

- momento_disp, puede ser BEFORE (antes) o AFTER (despus) para indicar que se ejecute antes o despus de la sentencia que lo activa.

- evento_disp, indica la clase de sentencias que activa el disparador (INSERT, UPDATE o DELETE).

No puede haber dos disparadores en una misma tabla que correspondan al mismo momento y sentencia.

- FOR EACH ROW, hace referencia a las acciones a llevar a cabo sobre cada fila de la tabla indicada.

- sentencia_disp, es la sentencia que se ejecuta cuando se activa el disparador. Si hay ms de una hay que colocarlas entre BEGIN . END.

Las columnas de la tabla asociada pueden referenciarse con:

- OLD.nom_columna (valor de la columna antes de ser actualizada o borrada).

- NEW.nom_columna (despus de ser actualizada o insertada).

En un disparador para INSERT solo se puede usar NEW, ya que no hay valor previo. Para DELETE, solo OLD. Si es para UPDATE podemos usar ambos.

Para obtener informacin de los disparadores creados:

SHOW TRIGGERS [ { FROM | IN } bd_nombre] [ LIKE patrn | WHERE expresin];

Al crear los disparadores se crea un nuevo registro en la tabla INFORMATION_SCHEMA llamada INFORMATION_SCHEMA.TRIGGERS que se puede ver de la siguiente manera:

SELECT trigger_name, action_statement FROM information_schema.triggers;

Para eliminar disparadores:

DROP TRIGGER [ IF EXISTS ] [esquema_nombre.]nombre_disp;10.1.- USO DE TRIGGERS o DISPARADORES

10.1.1. Control de sesiones:

Muchas veces queremos almacenar ciertos valores en variables de sesin creadas por el usuario para ver un resumen de lo realizado en la sesin. El caso del ejemplo anterior.

SET @sum = 0;

INSERT INTO movimiento VALUES ('11111111', '2011-11-20', 1000, 58, 1),

('11111111', '2011-11-20', -500, 59, 1), ('11111111', '2011-11-20', 750, 60, 1);

SELECT @sum AS Total insertado;

10.1.2. Control de valores de entrada:

Para controlar valores insertados o actualizados en tablas

DELIMITER $$

CREATE TRIGGER comprobar_saldo BEFORE UPDATE ON movimiento

FOR EACH ROW

BEGIN

IF NEW.cantidad < 0 THEN SET NEW.cantidad = 0;

ELSEIF NEW.cantidad > 100 THEN SET NEW.cantidad = 100;

END IF;

END$$

UPDATE movimiento SET cantidad = 500 where idmov = 59$$

UPDATE movimiento SET cantidad = -50 where idmov = 60$$SELECT * FROM movimiento$$10.1.3. Mantenimiento de campos derivados:

Para actualizar campos que pueden calcularse a partir de otros, por ejemplo el campo saldo en la tabla cuenta cada vez que se hace un ingreso:

DELIMITER ;CREATE TRIGGER actualizar_cta BEFORE INSERT ON movimiento

FOR EACH ROWUPDATE cuenta SET saldo = saldo + NEW.cantidadWHERE cod_cuenta = NEW.cod_cuenta;

SELECT * FROM cuenta where cod_cuenta = 1;INSERT INTO movimiento VALUES ('11111111', '2011-11-20', 1000, 61, 1);SELECT * FROM cuenta where cod_cuenta = 1;10.1.4. Estadsticas:

Podemos registrar estadsticas de operaciones o valores de nuestras BD en tiempo real. Por ejemplo podemos registrar los ingresos que se hacen cada mes en una tabla aparte:

/* Creamos la tabla donde almacenar la cantidad total para cada mes */

CREATE TABLE testadistica (tcantidad double, tmes char(2));

/* Creamos una funcin existe que devuelve 1 o 0 si existe o no el registro para cada mes */DELIMITER $$

DROP FUNCTION IF EXISTS existe$$

CREATE FUNCTION existe (mes char(2))

RETURNS intBEGIN

DECLARE cont INT;

SELECT count(*) INTO cont FROM testadistica WHERE tmes = mes;

RETURN cont;

END$$

CREATE TRIGGER ingresos_dia AFTER INSERT ON movimiento

FOR EACH ROW

BEGIN

IF existe(MONTH(NEW.fechahora)) = 0 THEN

INSERT INTO testadistica(tcantidad, tmes) VALUES (NEW.cantidad, MONTH(NEW.fechahora) );

ELSE UPDATE testadistica SET tcantidad = NEW.cantidad + tcantidad

WHERE tmes = MONTH(NEW.fechahora);

END IF;

END$$

DELIMITER ;

INSERT INTO movimiento VALUES ('11111111', '2011-11-28', 1000, 62, 1),

('11111111', '2011-11-28', -400, 63, 1), ('11111111', '2011-10-20', 750, 64, 1);SELECT * FROM testadistica;10.1.5. Registro o auditora:

Cuando muchos usuarios acceden a la BD puede que el registro log no sea suficiente o que solo se necesite alguna informacin especfica. Para ello podemos crear un disparador que despus de un DELETE o UPDATE, guarde determinados valores del registro o alguna informacin de utilidad en una tabla tipo log./* Creamos una tabla donde almacenar la informacin de actualizacin */

CREATE TABLE auditoria_mov (

Id_mov INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

Cod_cta_ant INT(11),

Fecha_ant DATE,

Cantidad_ant DOUBLE,

Cod_cta_n INT(11),

Fecha_n DATE,

Cantidad_n DOUBLE,

Usuario VARCHAR(40),

Fecha_mod DATETIME );

/* Cada vez que se actualice la tabla movimientos, se crear un registro en auditoria_mov */

DELIMITER $$

CREATE TRIGGER auditoria_mov AFTER UPDATE ON movimientoFOR EACH ROW

BEGIN

INSERT INTO auditoria_mov (Cod_cta_ant, Fecha_ant, Cantidad_ant,

Cod_cta_n, Fecha_n, Cantidad_n, Usuario, Fecha_mod)

VALUES(OLD.cod_cuenta, OLD.fechahora, OLD.cantidad, NEW.cod_cuenta, NEW.fechahora, NEW.cantidad, CURRENT_USER(), NOW() );

END$$

UPDATE movimiento SET cod_cuenta = 13, cantidad = 550,fechahora = 2011-10-29 where idmov = 37$$

UPDATE movimiento SET cantidad = 750 where idmov = 38$$SELECT * FROM auditoria_mov$$

10.2.- EJERCICIOS

1. En la BD Ventas, crea un disparador que asegure que el precio de cada producto sea un nmero mayor de 200.

delimiter //

drop trigger if exists comp_precios//

create trigger comp_precios after insert on productos for each row

begin

if new.precio < 200 then delete from productos where precio < 200;

end if;

end//

delimiter ;2. En la base ebanca, haz un disparador que cree un registro en la tabla nrojos con los campos cliente, cuenta, fecha y saldo cada vez que algn cliente se quede en nmeros rojos en alguna de sus cuentas.

drop trigger if exists registro;

delimiter //

create trigger registro after update on cuenta for each row

begin

if new.saldo < 0 then insert into nrojos values (null, new.fecha_creacion,

new.cod_cliente, new.cod_cuenta, new.saldo);

end if;

end //

delimiter ;

update cuenta set saldo=-500 where cod_cliente=2;

select * from nrojos;3. Haz lo necesario para que cada vez que un cliente de ebanca ingrese ms de 1000 se le bonifique con 100, solo para clientes con cuentas que superen tres aos de antigedad y para movimientos realizados entre el 1 de enero de 2011 y el 31 de marzo de 2011.

drop trigger if exists bono;

delimiter //

create trigger bono after insert on movimiento for each row

if new.cantidad > 1000 then update cuenta set saldo=saldo+new.cantidad+100

where fecha_creacionCALL.

FUNCIONESSimilares a los procedimientos salvo que devuelven un valor y todos sus parmetros son de tipo IN.

VISTASPartes determinadas de la BD en forma de tablas procedentes de consultas sobre tablas base.

DISPARADORESDe tipo BEFORE y AFTER permiten desencadenar acciones ante modificaciones de la BD.

EVENTOSRealizan conjuntos de acciones en ciertos momentos temporales.

SCRIPTSPequeos programas escritos en perl, php, python o C que permiten ampliar la funcionalidad de nuestro servidor.

Comandos para

programacin de tareasProgramas propios del SO. CRON para Linux, AT para Windows.

13.- EJERCICIOS PROPUESTOS (OPCIONAL).1. Haz lo necesario para poder saber el mes y ao con mejor saldo total en la base ebanca.

Dado que no tenemos modo de saber el saldo total mensual tenemos que crear un evento que registre el saldo de cada mes en la tabla saldo_mes:

2. Crea un procedimiento que encripte una cadena de caracteres cambiando cada letra por la siguiente, por ejemplo la a sera la b y as sucesivamente (usa las funciones ASCII y CHAR).

3. Haz lo necesario sobre la base de datos ebanca para alimentar la tabla num_rojos con datos de las cuentas que han tenido nmeros rojos en algn momento del ao.

4. Crea una funcin que devuelva 1 0 si una frase es o no palndroma (palabra a palabra).

5. Crea una funcin que devuelva 1 0 si una frase es o no palndroma (letra a letra y sin considerar los espacios en blanco).

6. Crea una vista para la cuenta limitada que permita ver el nmero de movimientos por da. Asigna los permisos necesarios a dicha cuenta. Es actualizable la vista?

7. Crea un evento que recoja cada 3 horas el estado del servidor en cuanto al nmero de consultas realizadas y conexiones creadas. Los valores deben almacenarse en la tabla estado_servidor(idestado, numero_consultas, numero_conexiones)8. En la base ebanca haz un procedimiento que ponga en negrita el nombre del cliente en la tabla cliente. Los parmetros son 0 para aplicarlo a todos los clientes o el dni del cliente.

9. En la BD Ventas, crea un procedimiento almacenado (cursor) que devuelva el vendedor con mayor nmero de clientes.

10. En la BD Ventas, crea una tabla Cantidad_Pedir. Esta tabla contendr informacin de las cantidades de diferentes productos que debemos pedir a nuestros proveedores para alcanzar un nivel de stock en nuestro almacen:

cantidad_Pedir (id_producto, cantidad) Adems id_producto es clave ajena de PRODUCTOS

CREATE TABLE cantidad_Pedir (id_producto INT(2) PRIMARY KEY,cantidad INT NOT NULL,FOREIGN KEY (id_producto) REFERENCES PRODUCTOS (producto_no) );

Crea un procedimiento almacenado que analice si el stock actual de cada uno de los productos es mayor de una cifra que se le pasar como parmetro. Si no es as, en la tabla Cantidad_Pedir se aadir una fila con el identificador del producto y la cantidad que se debe pedir para alcanzar dicho valor de stock.

11. En la BD Ventas, crea una tabla Comisiones. Esta tabla contendr informacin sobre la comisin que debe recibir cada uno de los vendedores

Comisiones (id_vendedor, comision) Adems id_vendedor es clave ajena de EMPLEADOS

CREATE TABLE comisiones (

id_vendedor INT(4) PRIMARY KEY,

comision NUMERIC (6,2) NOT NULL,

FOREIGN KEY (id_vendedor) REFERENCES EMPLEADOS (emp_no) );

Crea un procedimiento almacenado que actualice las comisiones de los empleados en la tabla. El procedimiento almacenado calcular, dado un cdigo de vendedor, una fecha y el porcentaje de comisin, la comisin que le corresponde al empleado por los artculos vendidos desde la fecha dada.

En caso de que en la tabla Comisiones no haya informacin de las comisiones del empleado se introducir la informacin en la tabla. En caso contrario, es decir, si ya existe informacin sobre las comisiones del empleado, se acumular el valor de la comisin al ya existente.

12.- En la BD Ventas, programa un disparador que cada vez que se inserte un empleado nuevo en la tabla EMPLEADOS, compruebe si la comisin es NULL y el oficio VENDEDOR, y si es as lo grabe con comisin cero.

Comprueba que funciona bien.

2 ASIR - Administracin de Sistemas Gestores de Bases de Datos 20/20