Está en la página 1de 62

Conceptos básicos de

desarrollo en
Procedimientos almacenados y
Funciones
Que son?

Un procedimiento almacenado es un conjunto de


comandos SQL que pueden almacenarse en un
servidor de base de datos. Luego de su creación,
cada cliente puede invocarlo y de esta manera se
reutiliza el código, sin necesidad de ejecutar los
comandos individualmente.
Ventajas de la utilización
● Sintaxis flexible: Los rutinas almacenadas pueden escribirse
utilizando extensiones de la sintaxis de SQL, tales como,
construcciones de control de flujo, que facilitan realizar lógicas
complejas.
● Capacidad de manejo de errores: Se pueden crear manejadores
de error para ser utilizados cuando se produzca una condición
determinada. De esta forma puede evitarse la finalización de la
rutina ante un error.
● Compilación Estándar: Se atienen a la sintaxis estándar, por lo
que son trasladables a otras bases de datos que sigan los
estándares.
● Encapsulación y empaquetado de código: Permite tener el código
almacenado y ser utilizado desde múltiples aplicaciones.
● Menos “Re-invención de la rueda”: Actúan como una biblioteca
de soluciones para resolver problemas, facilitan el compartir del
conocimiento y la experiencia. Un programador SQL que tiene
habilidades, puede resolver un problema de gran índole con un
SP y este puede ser reutilizado por otro.
Ventajas de la utilización
● Fácil de Mantener: Una rutina es mas fácil de mantener que una
copia embebida en cada aplicación.
● Reducción del requerimiento de Ancho de Banda: Imagine
múltiples operaciones realizadas por un cliente sin la utilización
de un SP. Cada sentencia y su resultado viaja a través de la red,
incluso aquellos que calculan un resultado intermedio. Si en
cambio esta operación se realiza con SP, las sentencias y
resultados intermedios se procesan completamente del lado del
servidor.
● Mejor seguridad: Una rutina puede ser escrita para acceder a
información sensible. Esta se puede configurar para que el que la
invoca o ejecuta, no vea información que no debe. Un SP puede
ser utilizado para modificar tablas en modo seguro sin darle al
usuario acceso a la tabla.
Diferencias entre SP y funciones

● Un SP no retorna solo un valor. Es invocado con


una sentencia CALL.
● Una función se invoca dentro de una expresión y

retorna un valor simple directamente al invocador


para ser utilizada en esa expresión
● No se puede invocar una función con CALL, y

tampoco se puede invocar un SP en una expresión


Invocacion de rutinas asociadas a la
BD
Cada rutina almacenada esta asociada con una base en
particular.
● Cuando se invoca la rutina, se realiza implícitamente USE
db_name. No se permite cambiar la base dentro de un SP.
● Se interpreta una referencia, nombre_rutina, en la base de
datos por defecto. Para referirse a una rutina en una base
de datos especifica hay que hacerlo de la manera:
CALL nombre_base_de_datos.nombre_rutina
● Cuando una rutina se ejecuta,se cambia la base de datos a
la que esta asociada la rutina y cuando termina setea como
base por defecto la base anterior. Debido a esto, se debe
tener acceso a esa base de datos para poder invocar la
rutina.
● Cuando se borra una base de datos, todos los
procedimientos almacenados asociados con ella también se
borran.
Definición
CREATE PROCEDURE nombre_procedure ([Parámetros[,...]])
[características.]
Cuerpo_de_la_rutina

CREATE FUNCTION nombre_funcion ([Parámetros[,...]])


RETURNS tipo_dato
[Características..]
Cuerpo_de_la_rutina
Puede darse como un bloque BEGIN / END que contiene
varias sentencias. Cada declaración debe terminar con punto y
coma (;).
Si usa el cliente de consola de mysql, el (;) es ambiguo entre la
definición de la rutina y el final de sentencia. Para esto existe el
comando delimiter que nos permite definir como queremos
marcar el final de una rutina.
Definición
La clausula opcional [Características] contiene los siguientes
valores, estos pueden aparecer en cualquier orden:

● SQL SECURITY {DEFINER | INVOKER}


Un SP corre con los privilegios del usuario que los creo o el que
lo invoca. DEFINER es el valor por default.
● DETERMINISTIC o NOT DETERMINISTIC
Este valor indica si la rutina produce siempre el mismo resultado
o no. El default es NOT DETERMINISTIC.
● COMMENT 'Cadena'
Especifica una descripción para la rutina. Esta descripción es
mostrada junto con la información de definición de una rutina.

Cuerpo_de_la_rutina

Contiene el cuerpo del SP o función Esta puede ser una


sentencia simple,varias sentencias, o puede contener varios
bloques de sentencias.
Declaración de Parámetros
● Los parámetros nos permiten pasarle valores a la rutina
cuando se la invoca.
● La declaración de parámetros ocurre entre los
paréntesis () que le siguen al nombre de la rutina
CREATE PROCEDURE o CREATE FUNCTION.

CREATE FUNCTION F_MontoAlquiler(p_CantPelicula INT,


p_importe DECIMAL(10,2))
RETURNS DECIMAL(10,2)
RETURN p_CantPelicula * p_importe;

SELECT F_MontoAlquiler(3,5)
Parámetros en los SP
El nombre en la declaración de una variable debe ser
precedido por una de las siguientes palabras para indicar en
que dirección fluye la información a través de ese parámetro:
● IN : Indica un parámetro de entrada. El valor del parámetro
es pasado al SP. El SP puede asignarle diferentes valores
al parámetro, pero el cambio es visible solamente dentro del
SP.
● OUT : Indica un parámetro de salida. Si se pasa un valor al
parámetro, es ignorado por el SP, y su valor inicial es NULL.
El SP setea su valor y cuando termina el valor del
parámetro es pasado por el SP al que lo llama. Este valor
se ve accediendo a la variable.
● INOUT : Indica un parámetro que puede ser tanto de
entrada como de salida. El SP recibe el parámetro tal como
lo pasa el invocador y cuando termina vuelve a pasar su
estado final.
Ejemplo: Parámetros de SP
DELIMITER $$
CREATE PROCEDURE PRC_testeo_parametros (IN p_in
INT, OUT p_out INT, INOUT p_inout INT)
BEGIN
SELECT p_in , p_out , p_inout;
SET p_in = 100 , p_out = 200 , p_inout = 300;
END;
$$

SET @v_in = 0 , @v_out = 0 , @v_inout = 0

CALL PRC_testeo_parametros (@v_in, @v_out, @v_inout);

SELECT @v_in, @v_out, @v_inout


Ejemplo: Parámetros de SP
mysql> SET @v_in = 0 , @v_out = 0 , @v_inout = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> CALL PRC_testeo_parametros (@v_in, @v_out, @v_inout);


+------+-------+---------+
| p_in | p_out | p_inout |
+------+-------+---------+
| 0 | NULL | 0 |
+------+-------+---------+
1 row in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @v_in, @v_out, @v_inout;


+-------+--------+----------+
| @v_in | @v_out | @v_inout |
+-------+--------+----------+
| 0 | 200 | 300 |
+-------+--------+----------+
1 row in set (0.00 sec)
La sentencia DECLARE
La sentencia DECLARE es usada para declarar distintos tipos
de items en rutinas almacenadas:
● Variables locales
● Condiciones, tales como alertas o excepciones
● Manejadores de error
● Cursores

Solo puede ser usada en un bloque BEGIN/END y debe


aparecer en el bloque antes que cualquier otra sentencia. Si se
declaran distintos tipos de items, se deben declarar variables y
condiciones primero, luego cursores y finalmente manejadores
de error.
Los items creados por DECLARE dentro de un bloque son
locales a este. Cuando un bloque termina, cualquier cursor
abierto se cierra y las variables dejan de ser accesibles.
Variables dentro de Rutinas
Almacenadas
DECLARE nombre_variable tipoDato [DEFAULT valor]

A una variable se le pueden asignar valores usando SET,


FETCH ... INTO y SELECT ... INTO. Las variables locales
difieren de las variables de usuario, estas no contienen un
“@” adelante.

DECLARE v_prueba INT DEFAULT 0


Asignando valores a Variables
con SET
La sentencia SET asigna valores a variables, estas
pueden ser variables de sistema o variables de usuario.
Se pueden setear variables fuera de las rutinas o pueden
ser variables dentro de la rutina previamente declaradas
con DECLARE.
La sentencia SET puede realizar asignaciones simples o
múltiples:

DECLARE var1, var2, var3;

SET var1 = 1 , var2 = 2 ;


SET var3 = var1 + var2 ;
Asignando valores con
SELECT...INTO
Asigna el resultado de una sentencia SELECT a una variable. Fuera
de la rutina deben ser variables de usuario. Dentro de las rutinas, la
sentencia puede utilizarse también para asignarle valores a variables
locales, las cuales fueron declaradas previamente con DECLARE.

DECLARE first_name_var CHAR(45);


DECLARE last_name_var CHAR(45);
SELECT first_name, last_name INTO v_first_name, v_last_name
FROM actor a
WHERE actor_id=1;

La sentencia SELECT debe traer como máximo una fila, sino ocurrirá
un error. Si no trae ninguna fila, las variables utilizadas en el INTO
permanecerán sin cambio.
También se puede usar para asignar valores a parámetros de una
rutina.
Ejemplo Asignación de Variables
DELIMITER $$

DROP PROCEDURE IF EXISTS `sakila`.`PRC_MejorCliente`$$


CREATE PROCEDURE `sakila`.`PRC_MejorCliente`(
OUT p_cliente SMALLINT,
OUT p_pay DECIMAL(5,2)
)
BEGIN
DECLARE v_last_month_start DATE;
DECLARE v_last_month_end DATE;
/* Determine start and end time periods */
SET v_last_month_start = STR_TO_DATE('2005-06-01','%Y-
%m-%d');
SET v_last_month_end = LAST_DAY(last_month_start);
SET p_cliente = 0,p_pay=0;
Ejemplo Asignación de Variables
SELECT customer_id, SUM(amount) pago
INTO p_cliente,p_pay
FROM payment
WHERE DATE(payment_date) BETWEEN
v_last_month_start AND v_last_month_end
GROUP BY customer_id
ORDER BY pago DESC
LIMIT 1;

END $$

CALL PRC_MejorCliente(@cli,@pay);

SELECT @cli,@pay;
Recuperación múltiples conjuntos
de datos
Una extensión del MySQL a los procedures es que la sentencia
SELECT puede ser ejecutada para generar conjuntos de resultados
que son retornados directamente al cliente sin proceso intermediario.
EL cliente recibe los resultados como si ejecutara la sentencia
SELECT el mismo. Esto no aplica a las funciones almacenadas.

DELIMITER //
CREATE PROCEDURE PRC_Pelicula_Actor_Contador ()
BEGIN
SELECT 'Film', COUNT(*) FROM film;
SELECT 'Actor', COUNT(*) FROM actor;
SELECT 'Film_Actor', COUNT(*) FROM film_actor;
END;
//
Cuando se lo invoca retorna tres conjuntos de resultados de un
registro
Recuperación múltiples conjuntos
de datos
CALL Pelicula_Actor_Contador()
mysql> CALL Pelicula_Actor_Contador();
+------+----------+ +-------+----------+
| Film | COUNT(*) | | Actor | COUNT(*) |
+------+----------+ +-------+----------+
| Film | 1000 | | Actor | 200 |
+------+----------+ +-------+----------+
1 row in set (0.01 sec) 1 row in set (0.01 sec)

+------------+----------+
| Film_Actor | COUNT(*) |
+------------+----------+
| Film_Actor | 5462 |
+------------+----------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)


Control de Flujo: Pruebas
Condicionales
La sentencia IF y CASE nos permiten evaluar condiciones. Notese
que tienen sintaxis diferente que la función IF() y la expresión CASE.
Las ultimas producen un valor que es utilizado en una expresión
Estas no son sentencias por si mismas. También terminan con END
en lugar de END CASE. Gracias a la sintaxis diferente, es posible
utilizar las funciones IF() y las expresiones CASE dentro de las
rutinas almacenadas sin tener ambigüedad, incluso si se utilizan
dentro de una sentencia IF o CASE.
IF expr
THEN statement_list
[ELSEIF expr THEN statement_list] ...
[ELSE statement_list]
END IF
IF val IS NULL
THEN SELECT 'val is NULL';
ELSE SELECT 'val is not NULL';
END IF;
Condicional CASE
CASE es la otra sentencia que evalúa condiciones. Tiene dos
modos. La primera sintaxis es la siguiente:
CASE case_expr
WHEN when_expr THEN statement_list
[WHEN when_expr THEN statement_list] ...
[ELSE statement_list]
END CASE
Condicional CASE
La expresión case_expr se evalúa y suele determinar cual de las
siguientes clausulas en el resto de la sentencia debe ejecutarse. La
when_expr en la clausula inicial del WHEN se evalúa y compara con la
case_expr. Si son iguales, la lista de sentencias siguiente al THEN se
ejecuta. Si when_expr no es igual a case_expr, y existen otras clausulas
WHEN, se manejan de forma similar a su turno. Si ninguna clausula
WHEN tiene una when_expr igual a case_expr, y hay una clausula ELSE,
la lista de sentencias de de la clausula ELSE es ejecutada.
Cada comparación tiene el formato case_expr = when_expr. El
significado de esto es que la comparación nunca es verdadera si el
operando es NULL, no importa el valor del operando.
La siguiente sentencia CASE evalúa si un valor dado es 0, 1, o diferente:
Ejemplo CASE 1er Forma
CASE val
WHEN 0 THEN SELECT 'val es 0';
WHEN 1 THEN SELECT 'val es 1';
ELSE SELECT 'val no es 0 or 1';
END CASE;
CASE 2da Forma
CASE
WHEN when_expr THEN statement_list
[WHEN when_expr THEN statement_list] ...
[ELSE statement_list]
END CASE

Ejemplo CASE 2da Forma


CASE
WHEN val IS NULL THEN SELECT 'val is NULL';
WHEN val < 0 THEN SELECT 'val es menor que 0';
WHEN val > 0 THEN SELECT 'val es mayor que 0';
ELSE SELECT 'val es 0';
END CASE;
Ciclos
●La sintaxis de sentencias en MySQL provee tres tipos distintos de
ciclo:
●LOOP construye un ciclo incondicional sin sintaxis de terminación

Por esta razón, debe contener una sentencia que especifique la


salida del ciclo.
●REPEAT y WHILE, las otras dos construcciones de ciclo, son

condicionales. Incluyen una clausula que determina si continua o


termina la ejecución del ciclo.
●Los SQL estándar incluyen un ciclo FOR también. MySQL no lo

soporta.
Ciclo LOOP
La sentencia LOOP crea un ciclo incondicional con la siguiente
sintaxis:

LOOP
statement_list
END LOOP

La lista de sentencias dentro del ciclo se ejecuta


repetidamente. El ciclo iterara por siempre a menos
que la lista de sentencias contenga alguna sentencia
que genera la salida del ciclo. La salida puede hacerse
efectiva con una sentencia LEAVE o (en una función)
una sentencia de retorno. El siguiente LOOP itera
mientras la variable i sea menor que 10:
Construcción Ciclo LOOP

DECLARE i INT DEFAULT 0;


my_loop: LOOP
SET i = i + 1;
IF i = 10 THEN
LEAVE my_loop;
END IF;
END LOOP my_loop;
Ciclo REPEAT
La sentencia REPEAT crea un ciclo condicional. Tiene la siguiente
sintaxis:

REPEAT
lista_sentencias
UNTIL expr
END REPEAT

Las sentencias dentro del ciclo se ejecutan y luego se evalúa la


expresión condicional expr. Si la expresion es verdadera, el ciclo
termina. De otro modo, comienza nuevamente. Notese que no se
utiliza punto y coma entre la expresión y END REPEAT. El
siguiente ciclo REPEAT itera mientras la variable i sea menor a 10:
Construcción Ciclo REPEAT
DECLARE i INT DEFAULT 0;
REPEAT
SET i = i + 1;
UNTIL i >= 10
END REPEAT;
Ciclo WHILE
La sentencia WHILE crea un ciclo condicional. Es similar al
REPEAT a excepción que la expresión de condición aparece al
principio del ciclo en vez de al final. También, un ciclo WHILE
continua mientra la condición sea verdadera, mientras que el ciclo
REPEAT termina tan pronto como la condición se vuelve
verdadera. La sintaxis de WHILE es la siguiente:

WHILE expr DO
statement_list
END WHILE
Ciclo WHILE
La expresión de condición se evalúa y el ciclo finaliza si la la
condición no es verdadera. De otra forma, la lista de sentencias
dentro del ciclo se ejecuta, el control se transfiere al comienzo, y la
expresión es evaluada nuevamente. El siguiente ciclo WHILE itera
mientras la variable sea menor a 10(diez):

DECLARE i INT DEFAULT 0;


WHILE i < 10 DO
SET i = i + 1;
END WHILE;
Diferencia REPEAT y WHILE
Debido a que la evaluación en un REPEAT se encuentra al final del
ciclo, las sentencias dentro del ciclo siempre se ejecutan al menos
una vez. Con WHILE, la evaluación se realiza al principio, por lo
que es posible que las sentencias dentro del ciclo no se ejecuten ni
siquiera una vez. Por ejemplo, el siguiente ciclo WHILE no
ejecutara nunca las sentencias dentro del ciclo:

WHILE 1 = 0 DO
SET x = 1;
END WHILE;

Así como los bloques BEGIN/END pueden ser


anidados, los ciclos también En tales casos, es útil
etiquetarlos por si es necesario salir de mas de un
nivel de ciclo a la vez.
Transferencia de Control
Dentro de una rutina existen dos sentencias que transfieren el
control. Cada sentencia requiere una etiqueta que indica a que
construcción etiquetada debe aplicarse:

LEAVE label

ITERATE label

LEAVE transfiere el control al final de la construcción nombrada y


puede ser usada con bloques y ciclos: BEGIN/END, LOOP,
REPEAT, o WHILE.
Transferencia de Control

ITERATE transfiere el control al principio de la construcción


nombrada. Solo puede utilizarse dentro de ciclos: LOOP,
REPEAT, o WHILE. No puede utilizarse para reiniciar un
bloque BEGIN/END.

LEAVE y ITERATE deben aparecer dentro de una construcción


etiquetada.

El siguiente ejemplo incluye un ciclo etiquetado y muestra como


salir del ciclo o comenzarlo nuevamente con LEAVE y
ITERATE.
Ejemplo Transferencia
DECLARE i INT DEFAULT 0;

my_loop: LOOP
SET i = i + 1;
IF i < 10 THEN ITERATE my_loop;
ELSEIF i > 20 THEN LEAVE my_loop;
END IF;
SELECT 'i is between 10 and 20';
END LOOP my_loop;
Transferencia de Control
La ultima forma de transferir el control es ejecutando una sentencia
RETURN para retornar un valor al proceso que llamo la rutina. Esto
solo se aplica a las funciones, no a los procesos almacenados. El
siguiente ejemplo retorna el nombre del país asociado a un código
de país:

CREATE FUNCTION `sakila`.`F_NombrePelicula`(p_id INT)


RETURNS CHAR(255)
BEGIN
DECLARE v_namer CHAR(255);
SELECT title INTO v_name FROM film
WHERE film_id=p_id;
RETURN v_namer;
END;
Condiciones y manejadores de
error
Un manejador de error tiene un nombre y una sentencia que
sera ejecutada luego de la ocurrencia de una condición dada,
tal como una advertencia o un error. Comúnmente los
manejadores se usan para detectar problemas y tratarlos de
una manera mas apropiada y evitar que la rutina termine con
un error.
Se pueden nombrar mediante una declaración con la sentencia
DECLARE, debe hacerse junto con las
variables.
DECLARE nombre_condición CONDITION FOR tipo_condición;

Tipo de condición puede ser un valor SQLSTATE o un código


de error numérico
DECLARE no_permite_null CONDITION FOR SQLSTATE '23000';
DECLARE no_permite_null CONDITION FOR 1048;
Condiciones y manejadores de
error
DECLARE HANDLER crea un manejador para una o mas
condiciones y las asocia con una sentencia SQL que
sera ejecutada si cualquiera de las condiciones ocurre:
DECLARE tipo_manejador HANDLER FOR
tipo_condición [, tipo_condición] ...
sentencia
El tipo de manejador indica lo que sucede. CONTINUE causa
que la rutina continúe la ejecución, la instrucción SQL que
sigue a la declaración en la que se produjo la condición es la
siguiente para ser procesada. EXIT causa la transferencia del
control al final del bloque en el que el manejador se declaro. El
SQL estándar también define manejadores UNDO, pero
MySQL no es compatible con ellos.
Condiciones y manejadores de
error
Cada condición asociada a un manejador debe ser una de las
siguientes:
● Un valor SQLSTATE o código de error de MYSQL, se
especifica de la misma manera que en una sentencia
DECLARE CONDITION.
● El nombre de una condición declarada previamente con una
sentencia DECLARE CONDITION.
● SQLWARNING, las cuales manejan condiciones para todos
los valores SQLSTATE que comienzan con 01.
● NOT FOUND, las cuales manejan condiciones para todos
los valores SQLSTATE que comienzan con 02.
● SQLEXCEPTION, las cuales manejan condiciones para
todos los valores SQLSTATE no manejados por
SQLWARNING o NOT FOUND
Ejemplo Manejadores de
CREATE PROCEDURE PRC_AgregaCliente (p_id
Error
INT,p_id_tienda INT,p_nombre VARCHAR(45),p_apellido
VARCHAR(45))
BEGIN
DECLARE EXIT HANDLER FOR 1062
BEGIN
SELECT 'ERROR numero de tienda invalido' AS result;
END;
DECLARE EXIT HANDLER FOR 1452
BEGIN
SELECT 'ERROR numero de cliente ya existe' AS result;
END;

INSERT INTO customer


VALUES(p_id,p_id_tienda,p_nombre,p_apellido,NULL,1,1,CU
RRENT_DATE(),CURRENT_DATE());
SELECT 'Cliente agregado con éxito' AS result;
END;
Ejemplo Manejadores de Error
mysql> CALL PRC_AgregaCliente(601,9999999,'Maria', 'Rodriguez');
ERROR 1452 (23000): Cannot add or update a child row: a foreign key
constraint fails (`sakila/customer`, CONSTRAINT `fk_customer_store`
FOREIGN KEY (`store_id`)
REFERENCES `store` (`store_id`) ON UPDATE CASCADE)
mysql> CALL PRC_AgregaCliente(2,1,'Maria', 'Rodriguez');
ERROR 1062 (23000): Duplicate entry '2' for key 1
mysql> CALL PRC_AgregaCliente(601,9999999,'Maria', 'Rodriguez');
+---------------------------------+
| result |
+---------------------------------+
| ERROR numero de tienda invalido |
+---------------------------------+
mysql> CALL PRC_AgregaCliente(2,1,'Maria', 'Rodriguez');
+-----------------------------------+
| result |
+-----------------------------------+
| ERROR numero de cliente ya existe |
+-----------------------------------+
mysql> CALL PRC_AgregaCliente(601,1,'Maria', 'Rodriguez');
Handlers
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET exit_loop = 1;

DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'


BEGIN
statement_list
END;

Para ignorar una condición, declare un manejador CONTINUE y


asocielo con un bloque vacío:

DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;


Declaración de Cursores
Un cursor te permite acceder a un conjunto de datos de a una fila a
la vez. Debido a esto, los cursores son utilizados en ciclos que fijan y
procesan una fila dentro de cada iteración del ciclo.
La implementación del cursor en MySQL tiene las siguientes
propiedades:
● Provee de cursores de lectura; no pueden utilizarse para modificar

tablas.
● Los cursores solo avanzan a través de los datos fila por fila; esto
significa, que no son desplazables en cualquier sentido.
Para usar un cursor en una rutina almacenada, comience
escribiendo una sentencia DECLARE CURSOR en el cual se nombra
el cursor y se asocia al la sentencia SELECT que produce el
conjunto de datos:
DECLARE nombre_cursor CURSOR FOR sentencia_select
Cada cursor declarado dentro de un bloque debe tener un nombre
diferente.
Manejo de Cursores
Los cursores deben abrirse con una sentencia OPEN. Esta ejecuta la
sentencia SELECT asociada con el cursor:
OPEN nombre_cursor
La sentencia FETCH obtiene la próxima fila del conjunto de datos del
cursor abierto. Tiene que utilizarse una variable por cada columna
del conjunto de datos. Pueden obtenerse los valores dentro de
variables o parámetros de rutinas:
FETCH nombre_cursor INTO var_name [, var_name] ...
FETCH frecuentemente se utiliza en un ciclo para que todas las filas
puedan ser procesadas. Esto presenta un problema: Que pasa
cuando se alcanza el fin de datos? Ocurre una condición de “No
Data” (SQLSTATE 02000), la cual puede ser detectada declarando
un manejador para esa condición
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
statement;
Cierre de Cursores
Cuando se termina de utilizar el cursor, se debe cerrar con una
sentencia CLOSE:
CLOSE nombre_cursor
Cerrar un cursor es opcional. Cualquier cursor declarado en un
bloque se cierra automáticamente cuando el bloque termina.
El siguiente ejemplo declara un cursor nombrado “c” y lo asocia con
una sentencia que selecciona filas de las películas que realizo un
actor. También declara una manejador de condición que detecta el
fin del conjunto de datos. (La sentencia del manejador esta vacía
pues el único propósito es transferir el control al final del bloque.)
Ejemplo Cursores
DELIMITER $$
CREATE PROCEDURE PRC_Films_actor(p_actor INT)
BEGIN
DECLARE v_row_count INT DEFAULT 0;
DECLARE v_code INT;
DECLARE v_name CHAR(52);
DECLARE c CURSOR FOR
SELECT f.film_id,title FROM film_actor f_a,film f WHERE
f_a.film_id=f.film_id AND actor_id=p_actor;
OPEN c;
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '02000' BEGIN END;
LOOP
FETCH c INTO v_code, v_name;
SET v_row_count = v_row_count + 1;
END LOOP;
END;
CLOSE c;
SELECT 'Numero de peliculas realizadas =', v_row_count;
END;
$$
Ejemplo Cursores
En el ejemplo precedente se utiliza un bloque de
sentencias anidadas porque un manejador EXIT
termina el bloque dentro del cual se declara, no el
ciclo dentro del cual la condición ocurre. Si no se
hubiera utilizado un bloque de sentencias
anidadas, el manejador hubiera transferido el
control al final del bloque principal luego de llegar
al fin del conjunto de datos, y la sentencia CLOSE y
SELECT siguientes al ciclo no hubieran sido
ejecutadas. Un enfoque alternativo no requiere de
un bloque de sentencias anidadas: Usa un
manejador CONTINUE que setea una variable de
estado que produce la terminación del ciclo, ya que
la misma se se controla dentro de ciclo.
Ejemplo Cursores
CREATE PROCEDURE PRC_Films_actor_2(p_actor INT)
BEGIN
DECLARE v_exit_flag INT DEFAULT 0;
DECLARE v_row_count INT DEFAULT 0;
DECLARE v_code INT;
DECLARE v_name CHAR(52);
DECLARE c CURSOR FOR
SELECT f.film_id,title FROM film_actor f_a,film f WHERE f_a.film_id=f.film_id AND
actor_id=p_actor;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET v_exit_flag = 1;
OPEN c;
fetch_loop: LOOP
FETCH c INTO v_code, v_name;
IF v_exit_flag THEN LEAVE fetch_loop; END IF;
SET v_row_count = v_row_count + 1;
END LOOP;
CLOSE c;
SELECT 'Numero de peliculas =', v_row_count;
END;
Ejemplo General
CREATE PROCEDURE PRC_Pais() SQL SECURITY INVOKER
BEGIN
DECLARE v_id INT;
DECLARE v_name VARCHAR(50);
DECLARE cur CURSOR FOR
SELECT country_id,country FROM country;
CREATE TEMPORARY TABLE tmpcountry (country VARCHAR(50));
OPEN cur;
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '02000' BEGIN END;
LOOP
FETCH cur INTO v_id,v_name;
INSERT INTO tmpcountry VALUES(v_name);
END LOOP;
END;
CLOSE cur;
SELECT * FROM tmpcountry;
END
Triggers
A partir de MySQL 5.0.2 se incluyó soporte básico
para triggers. Por ahora es muy básico y limitado.
Que son los triggers?
Los triggers (disparadores en español) son acciones que pueden
ejecutarse de manera automática cuando determinado evento
ocurre en una tabla.

Al crear un trigger, se lo asocia con una tabla, y se lo programa para


que se active antes o después de la ejecución de que una
sentencia del tipo DML (INSERT, DELETE o UPDATE) ocurra en
esa tabla. Básicamente un trigger se compone de tres partes:
 Evento
Es el evento que tiene que ocurrir para que el trigger se active.
Puede ser una sentencia INSERT, DELETE o UPDATE
 Restricción
Una vez activado el trigger, se puede evaluar una condición, para
ser ejecutado opcionalmente bajo ciertas condiciones.
 Acción
La acción que realiza el trigger al ejecutarse.
Triggers
CREATE TRIGGER nombre_trigger

{ BEFORE | AFTER }

{ INSERT | UPDATE | DELETE }

ON nombre_tabla

FOR EACH ROW

sentencia_a_ejecutar
Referencias
● En el trigger de un INSERT , NEW.nombre_columna indica el
valor de una columna a ser insertado dentro de una nueva
columna. OLD no esta permitido.

● En el trigger de un DELETE, OLD.nombre_columna indica el


valor de una columna a ser borrado. NEW no esta disponible.

● En el trigger de un UPDATE, OLD.nombre_columna y


NEW.nombre_columna hacer referencia al valor de una fila antes
y después del UPDATE.

● OLD debe ser utilizado en modo solo-lectura. NEW puede ser


utilizado para leer o cambiar valores de columnas.
Eliminación de Triggers
Para eliminar un Trigger se utiliza la sentencia DROP
TRIGGER.

DROP TRIGGER nombre_base.nombre_trigger;

Si no se hace referencia al nombre de la base de datos se utiliza


la base por defecto.

DROP TRIGGER nombre_trigger;


Triggers:Premisos

Para poder crear o eliminar Triggers con las sentencias DROP y


CREATE se deben tener privilegios SUPER

Para asignar valor a una columna con SET NEW.col = VALOR,


debe tener permiso de UPDATE o INSERT para la columna,
dependiendo de la operación realizada.

Para utilizar NEW.col en una expresión, debe tener permiso de


SELECT para la columna.
Ejemplo Triggers
CREATE TABLE `sakila`.`audi_actor` (
`actor_id` SMALLINT(5) UNSIGNED NOT NULL
AUTO_INCREMENT,
`first_name` VARCHAR(45) NOT NULL,
`last_name` VARCHAR(45) NOT NULL,
`last_update` TIMESTAMP NOT NULL DEFAULT
CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,
user_audi VARCHAR(30),
accion_audi VARCHAR(1),

KEY `idx_actor_last_name` (`last_name`)


)
Creación trigger INSERT

DROP TRIGGER IF EXISTS tr_audi_actor_insert;

CREATE TRIGGER tr_audi_actor_insert AFTER INSERT


ON actor

FOR EACH ROW


BEGIN
INSERT INTO audi_actor VALUES
(NEW.actor_id,NEW.first_name,NEW.last_name,NE
W.last_update,USER(),'i');
END;
Creación trigger DELETE

DROP TRIGGER IF EXISTS tr_audi_actor_delete;

CREATE TRIGGER tr_audi_actor_delete AFTER DELETE


ON actor

FOR EACH ROW


BEGIN
INSERT INTO audi_actor VALUES
(OLD.actor_id,OLD.first_name,OLD.last_name,
OLD.last_update,USER(),'d');
END;
Creación trigger UPDATE

DROP TRIGGER IF EXISTS tr_audi_actor_update;

CREATE TRIGGER tr_audi_actor_update AFTER


UPDATE ON actor

FOR EACH ROW


BEGIN
INSERT INTO audi_actor VALUES
(OLD.actor_id,OLD.first_name,OLD.last_name,
OLD.last_update,USER(),'u');
END;
Comprobación
INSERT INTO actor(last_name,first_name)
VALUES('Roberto','Darin');

UPDATE actor SET first_name='Ricardo' WHERE


actor_id=213;

DELETE FROM actor WHERE actor_id=213

#Comprobamos tabla de auditoria


SELECT * FROM audi_actor;