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

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 <= 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
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.
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

1/20

>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):
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

2/20

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 FOR
SELECT 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 processed
El 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 */

2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

3/20

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 FOR
SELECT 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( )
BEGIN
DECLARE 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$$

2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

4/20

DELIMITER $$
DROP PROCEDURE IF EXISTS mis_productos2$$
CREATE PROCEDURE mis_productos2( )
BEGIN
DECLARE 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;

2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

5/20

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 final
DELIMITER $$
DROP PROCEDURE IF EXISTS CursorProc$$
CREATE PROCEDURE CursorProc( )
BEGIN
DECLARE 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
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

6/20

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 = 1
END 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;


2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

7/20

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,
fecha_alta, dep_no from empleados where oficio<>"vendedor";
Declare continue handler for not found set no_mas=1;
open cursor_noven;

salario,

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
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos
8/20

update
empleados
where apellido=apellido_C;
select dnombre from
into dpto;
select
salario_c,fecha_alta_c,dpto;
select "Salario del

set

salario=salario+(salario*0.1)

departamentos where dep_no=dep_no_c


apellido_c,

oficio_c,

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 ;
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

9/20

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 (
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

10/20

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.
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

11/20

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:
Nombre
Evento

Condicin
Accin

CREATE TRIGGER nom_disparador


{BEFORE | AFTER } {INSERT | DELETE | UPDATE [OF columnas]} ON tabla
[REFERENCING OLD [ROW] [AS] vieja_fila
NEW [ROW] [AS] nueva_fila
OLD TABLE [AS] vieja_tabla
NEW TABLE [AS] nueva_tabla ]
[{FOR EACH ROW | FOR EACH STATEMENT }]
[WHEN (condicin)]
{ 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);
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

12/20

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
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

13/20

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 ROW
UPDATE cuenta SET saldo = saldo + NEW.cantidad
WHERE 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 int
BEGIN
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)
MONTH(NEW.fechahora) );
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

VALUES

(NEW.cantidad,
14/20

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 movimiento
FOR 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
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

15/20

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
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;

(null,

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_creacion<"2011-02-19" and cod_cuenta in(select cod_cuenta
from movimiento where cuenta.cod_cuenta=movimiento.cod_cuenta and
fechahora between "2011-01-01" and "2011-03-31");
end if;
end //
delimiter ;
11.- EVENTOS.
Los eventos son tareas que se ejecutan de acuerdo a un horario. Por eso, tambin se les llama eventos
programados.
Es un concepto similar al Programador de Tareas (comando AT) de Windows.
Un evento se identifica por su nombre y el esquema o la BD al que se le asigna. Lleva a cabo una accin
especfica (una o varias instrucciones SQL dentro de un bloque BEGIN/END) de acuerdo a un horario.
Pueden distinguirse 2 tipos de eventos: los que se programan para una nica ocasin y los que ocurren
peridicamente cada cierto tiempo.
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

16/20

La variable global event_scheduler determina si el programador de eventos est habilitado y en


ejecucin en el servidor. Puede estar a ON (activado), OFF (desactivado) y DISABLED (si queremos que no se
pueda activar en tiempo de ejecucin).
Con el siguiente comando podemos comprobar si el programador de eventos est activo ya que se
ejecuta como un hilo ms del servidor:
SHOW PROCESSLIST \G
Si global_event_scheduler no est como DISABLED podemos activar el programador con:
SET GLOBAL event_scheduler = ON;
11.1.- GESTIN DE EVENTOS
Podemos usar CREATE EVENT, ALTER EVENT, SHOW EVENT y DROP EVENT.
11.1.1. Creacin de eventos:
CREATE [ DEFINER = { user | CURRENT_USER } ]
EVENT [ IF NOT EXISTS ] nombre_evento ON SCHEDULER schedule
[ ON COMPLETION [ NOT ] PRESERVE ]
[ ENABLE | DISABLE | DISABLE ON SLAVE ]
[ COMMENT comentario ]
DO cuerpo_evento;
schedule:
AT timestamp [ + INTERVAL intervalo] | EVERY intervalo ]
[ STARTS timestamp [ + INTERVAL intervalo] ]
[ ENDS timestamp [ + INTERVAL intervalo] ]
intervalo:
quantity { YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND }
- La clusula ON SCHEDULE permite establecer como y cuando se ejecutar el evento: una sola vez,
durante un intervalo, cada cierto tiempo o en una fecha-hora de inicio y fin determinadas.
- DEFINER especifica el usuario cuyos permisos se tendrn en cuenta en la ejecucin del evento.
- cuerpo_evento es el contenido del evento que se va a ejecutar.
- COMPLETION permite mantener el evento aunque haya expirado mientras que DISABLE permite
crear el evento en estado inactivo.
- DISABLE ON SLAVE sirve para indicar que el evento se cre en el master de una replicacin y que,
por tanto, no se ejecutar en el esclavo.
Ejemplo 1:
Vemos si est activo el programador de eventos:
SHOW PROCESSLIST \G
Si no lo est, lo activamos:
SET GLOBAL event_scheduler = ON;
CREATE TABLE cron
(id INT(11) PRIMARY KEY AUTO_INCREMENT, data DATE);
CREATE EVENT primer_evento ON SCHEDULE EVERY 5 SECOND
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

17/20

ON COMPLETION PRESERVE ENABLE


DO INSERT INTO cron(data) VALUES (NOW());
SELECT * FROM cron;
Si queremos eliminarlo:
DROP EVENT primer_evento;
Ejemplo 2:
/* en ebanca, se bonifica con 100 a las cuentas dadas de alta en el intervalo de 1 mes */
CREATE EVENT bonificacion
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MONTH
DO
UPDATE cuenta SET saldo = saldo + 100 WHERE fecha_creacion
BETWEEN now() AND DATE_ADD(NOW(), interval -1 month);
11.1.2. Modificacin de eventos:
ALTER [ DEFINER = { user | CURRENT_USER } ]
EVENT nombre_evento [ ON SCHEDULER schedule ]
[ ON COMPLETION [ NOT ] PRESERVE ]
[ RENAME TO nuevo_nombre_evento ]
[ ENABLE | DISABLE | DISABLE ON SLAVE ]
[ COMMENT comentario ]
[ DO cuerpo_evento ];
11.1.3. Consulta de eventos:
Para mostrar la informacin de eventos asociados a un esquema o BD y filtrados segn el patrn que
determinamos o una clusula WHERE.
SHOW EVENTS [ { FROM | IN } nombre_esquema ]
[ LIKE patrn | WHERE expresin ]
Tambin podemos consultar la tabla INFORMATION_SCHEMA.EVENTS del diccionario de datos.
11.2.- EJERCICIOS
1. Crea un evento que cargue una comisin del 2% sobre las cuentas en nmeros rojos cada primero de mes
comenzando el 4 de FEBRERO de 2014 (actualice el saldo negativo aadiendo el 2%).
drop event if exists comision;
CREATE EVENT comision ON SCHEDULE EVERY 1 MONTH STARTS '2014-02-04'
DO UPDATE cuenta SET SALDO=SALDO+(SALDO*0.02) WHERE SALDO<0;

2. Programa un anlisis (ANALYZE TABLE) de las tablas de la base ebanca para el 1 de febrero de 2012.
DROP EVENT IF EXISTS ANALISIS;
CREATE EVENT ANALISIS ON SCHEDULE AT current_timestamp
DO ANALYZE TABLE CLIENTE, CUENTA, MOVIMIENTO, NROJOS;
3. Crea un evento que registre diariamente los movimientos superiores a 1000 en una tabla temponal tmp_mov.
Cralo deshabilitado.
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos

18/20

DROP table tmp_mov;


CREATE TABLE tmp_mov (movi int primary key, cuenta int, cantidad int,
fecha date);
DROP EVENT IF EXISTS REGISTRO_MOVIMIENTOS;
CREATE EVENT REGISTRO_MOVIMIENTOS ON SCHEDULE every 1 day disable DO
insert into tmp_mov select idmov, cod_cuenta, cantidad, fechahora from
movimiento where cantidad>1000;
12.- RESUMEN DE OBJETOS PARA AUTOMATIZACIN DE TAREAS.
PROCEDIMIENTOS
FUNCIONES
VISTAS
DISPARADORES
EVENTOS
SCRIPTS
Comandos para
programacin de tareas

Pequeos programas que hacen tareas sencillas y bien definidas.


Se llaman con el comando >CALL.
Similares a los procedimientos salvo que devuelven un valor y todos
sus parmetros son de tipo IN.
Partes determinadas de la BD en forma de tablas procedentes de
consultas sobre tablas base.
De tipo BEFORE y AFTER permiten desencadenar acciones ante
modificaciones de la BD.
Realizan conjuntos de acciones en ciertos momentos temporales.
Pequeos programas escritos en perl, php, python o C que permiten
ampliar la funcionalidad de nuestro servidor.
Programas 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.
2 ASIR - Administracin de Sistemas Gestores de Bases de Datos
19/20

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