Lenguaje Procedimental - PL
Lenguaje Procedimental - PL
Daniel Santiago
Lenguaje procedimental
Introducción 2
Estructuras condicionales 3
Bucles 4
Handlers 5
Cursores 6
Funciones 8
Procedimientos 9
Triggers 10
1
Lenguaje procedimental - MySQL
Daniel Santiago
1. Introducción
El hecho de poder crear procedimientos almacenados dentro del servidor MySQL ofrece
algunas ventajas. Por ejemplo, estas funciones son independientes del lenguaje de las
aplicaciones clientes que usen las bases de datos del servidor. Además, con procedimientos
conseguimos entornos de ejecución más seguros.
Pero una de las ventajas del uso del lenguaje procedimental es la ganancia en el rendimiento
que representa ejecutar funciones dentro del servidor, allá donde tenemos los datos. De esta
forma el envío de información entre cliente y servidor es menor.
En este documento se explica la sintaxis básica que ofrece MySQL para poder crear funciones y
procedimientos almacenados.
MySQL permite el uso de variables de usuario para que éste pueda almacenar valores. De esta
forma se puede, por ejemplo, pasar valores entre diferentes sentencias.
Las variables que declara un usuario están presentes únicamente en la conexión desde la que
se crean, por lo tanto estas variables no pueden ser vistas ni utilizadas por otros clientes.
Hay un tipo de variables que son las que van dentro de un procedimiento o función. Estas
variables se llaman locales, y se declaran de la siguiente forma:
Entre corchetes se ha puesto el valor por defecto que se le puede dar a una variable al
declararla. Esta parte es opcional.
El operador SET nos permite también asignar a una variable el resultado de una consulta
SELECT, siempre y cuando esta consulta devuelva un único resultado.
2
Lenguaje procedimental - MySQL
Daniel Santiago
Si queremos almacenar los valores devueltos por una consulta SELECT en diferentes variables,
podemos usar la sentencia SELECT … INTO:
En el apartado de declaración de variables hemos visto el operador SET y DECLARE. Pero hay
otra forma de crear una variable. El código siguiente ejemplifica la tercera forma de declarar
una variable, asignándole un valor en la misma sentencia:
Se aprecia que el operador de asignación es dos puntos igual (:=). Éste es el que debemos usar
cuando no usemos los operadores SET o SELECT … INTO.
Veamos otro ejemplo de asignación de valores a variables, en este caso dentro de una consulta
SELECT, sin usar el operador INTO:
4. Estructuras condicionales
MySQL nos ofrece las estructuras IF…ELSE y CASE…WHEN para poder crear estructuras
condicionales.
IF condición THEN
Sentencias;
ELSEIF condición THEN
Sentencias;
ELSE
Sentencias;
END IF;
La condición puede ser una comparación entre variables y/o valores, usando los operadores de
comparación (=, <, <=, =>, <>, !=, [NOT] BETWEEN). También podemos verificar en la condición
si una variable es nula, con el operador IS NULL. Para verificar que no es nula, usaremos IS NOT
NULL.
CASE variable
WHEN valor1 THEN sentencias;
WHEN valor2 THEN sentencias;
ELSE sentencias;
END CASE;
3
Lenguaje procedimental - MySQL
Daniel Santiago
Si no se quisiera añadir ninguna sentencia en el bloque ELSE, añadiremos BEGIN END; tal y
como sigue:
CASE variable
WHEN valor1 THEN sentencias;
WHEN valor2 THEN sentencias;
ELSE BEGIN END;
END CASE;
5. Bucles
Para repetir varias veces un fragmento de sentencias, MySQL nos ofrece los bucles LOOP,
REPEAT y WHILE. Además, disponemos de las sentencias LEAVE e ITERATE.
[etiqueta:] LOOP
Sentencias;
END LOOP [etiqueta];
En el código anterior vemos que aparecen las sentencias ITERATE y LEAVE. El comando
ITERATE sirve para volver a ejecutar el bucle con la etiqueta que acompaña a ITERATE. El
comando LEAVE se utiliza para salir del bucle con la etiqueta que acompaña a LEAVE.
[etiqueta:] REPEAT
sentencias;
UNTIL condición
END REPEAT [etiqueta];
Las sentencias que hay dentro del bucle REPEAT se repetirán hasta que la condición sea cierta.
El etiquetado del bucle REPEAT es opcional.
4
Lenguaje procedimental - MySQL
Daniel Santiago
END
Las sentencias dentro de un bucle WHILE se repiten mientras la condición sea cierta. El
etiquetado del bucle WHILE es opcional.
Un handler en MySQL se invoca cuando se produce una condición previamente definida. Esta
condición o evento está asociado con una condición de error. El handler lleva un comando
específico asociado, que se ejecutará inmediatamente después de que se cumpla la condición.
La condición de un handler hemos comentado que está asociada a una condición de error, que
podemos definir de tres formas:
Los códigos SQLSTATE son independientes de la base de datos con la que trabajemos. Por
tanto, en Oracle, SQL Server, MySQL, etc. tendremos el mismo código de error SQLSTATE, lo
que hará que nuestro código sea más portable. Sin embargo, los códigos de error de MySQL
son propios, y nada tendrán que ver con los códigos de error de otras bases de datos.
Dicho lo anterior, podemos pensar que es mejor usar en nuestros handlers los códigos de
SQLSTATE en lugar de los códigos de error de MySQL. Esto no es correcto, ya que no todos los
códigos de error de MySQL tienen su equivalencia en código SQLSTATE. Además, si pensamos
5
Lenguaje procedimental - MySQL
Daniel Santiago
En el código anterior vemos que hacemos dos operaciones INSERT con la misma PRIMARY KEY.
Esto produciría un error en MySQL con el código SQLSTATE 23000. Lo que hacemos para que
nuestro programa siga ejecutándose es declarar un handler de tipo CONTINUE. Así pues, el
procedimiento seguirá ejecutándose después de producirse el error. Si luego ejecutamos la
sentencia SELECT @x, nos devolverá un 3. Esto es porque se ha ejecutado la última línea de
código del procedimiento. Si hubiéramos declarado el handler de tipo EXIT, la ejecución
hubiera terminado al producirse el error y la variable @x tendría el valor 2.
7. Cursores
Los cursores nos sirven para ir procesando fila a fila el resultado de una consulta SELECT dentro
de procedimientos y funciones almacenadas. Los cursores deben declararse antes de declarar
los handlers, y las variables y condiciones deben declararse antes de declarar cursores o
handlers.
Después de declarar un cursor, para poder utilizarlo tenemos que abrirlo. Lo hacemos con la
sentencia:
OPEN nombre_cursor;
Para recorrer las filas de una consulta SELECT mediante un cursor declarado y abierto,
usaremos la sentencia:
6
Lenguaje procedimental - MySQL
Daniel Santiago
De esta forma avanzaremos el cursor abierto hacia el siguiente registro. En caso de no haber
más registros, se producirá un error con código SQLSTATE 02000 (que podremos capturar con
un handler previamente declarado).
CLOSE nombre_cursor;
Si no usamos este comando para cerrar explícitamente un cursor, éste se cerrará al final del
procedimiento en que se declaró.
Se han creado dos cursores para recorrer las filas devueltas por dos consultas SELECT. Además,
se ha declarado un handler con el código SQLSTATE 02000. Éste se ejecutará cuando uno de los
dos cursor se quede sin filas que procesar (falle la operación FETCH). Hacemos un bucle en el
que iremos tratando cada uno de los registro de los cursores (con la operación FETCH), e
insertaremos en una tercera tabla los valores de los cursores en función de una condición (IF …
ELSE). Este bucle se repetirá mientras ninguna de las dos operaciones FETCH de error (nos
quedemos sin registros). En tal caso, capturaremos el error mediante el handler y
cambiaremos el valor de la variable done, con lo que se terminará la ejecución del bucle y del
procedimiento.
7
Lenguaje procedimental - MySQL
Daniel Santiago
8. Funciones
Las funciones y los procedimientos se guardan en la tabla proc de MySQL, y están asociados a
una base de datos concreta. Para poder crear y modificar funciones y procedimientos
necesitamos los permisos CREATE ROUTINE y ALTER ROUTINE. Para poder ejecutar las
funciones y procedimientos necesitamos el permiso EXECUTE. El creador de una rutina tendrá
este permiso automáticamente.
CREATE FUNCTION
Las funciones pueden llamarse desde dentro de otras funciones, y pueden retornar un valor
escalar.
SELECT nombre_funcion();
Para borrar una función sólo debemos especificar su nombre, sin paréntesis ni parámetros.
delimiter //
CREATE FUNCTION hola (s CHAR(20)) RETURNS CHAR(50)
RETURN CONCAT(‘Hola ‘, s, ‘!’);
//
delimiter ;
La función anterior se llama hola, tiene un parámetro de entrada de tipo CHAR(20) que se
llama s, y devuelve una cadena de caracteres. La sentencia RETURN acaba la ejecución de la
función, devolviendo un valor. Ejecutamos la función de la siguiente forma:
SELECT hola(‘Dani’);
En el código anterior aparece una cláusula importante, delimiter. En MySQL, cada instrucción
que ejecutamos la terminamos con punto y coma(;). MySQL entiende que hemos terminado
una transacción y la ejecuta. En funciones y procedimientos, queremos que se ejecuten de
golpe varias instrucciones, sin tener en cuenta el punto y coma con el que finalizamos cada
una. Por eso, definimos un delimitador que nos indica cuándo termina la función y que, todas
las instrucciones antes del delimitador, deben ejecutarse juntas. En el ejemplo, se ha usado
como delimitador dos barras (//). Al terminar el código de la función, se escribe el delimitador,
8
Lenguaje procedimental - MySQL
Daniel Santiago
y después volvemos a definir el punto y coma (;) como delimitador de las siguientes
instrucciones.
¿Qué hace?
9. Procedimientos
Los procedimientos son parecidos a las funciones, sólo que en los procedimientos no se
devuelven valores (mediante la sentencia RETURN, veremos que tienen otros métodos).
CALL nombre_procedimiento(lista_parámetros);
delimiter //
CREATE PROCEDURE miProc (OUT param1 INT)
BEGIN
SELECT COUNT(*) INTO param1 FROM t;
END
//
delimiter ;
Del código anterior cabe destacar cómo se ha declarado el único parámetro que tiene el
procedimiento: OUT param1 INT. Esto indica que el procedimiento devolverá un valor entero
al ejecutarse. Tenemos diversos tipos de parámetros, según sean de entrada o salida, o ambos:
IN, OUT, INOUT.
9
Lenguaje procedimental - MySQL
Daniel Santiago
delimiter //
CREATE PROCEDURE insertaUsuario (in usu_nombre varchar(25))
BEGIN
INSERT INTO usuario (nombre) VALUES (usu_nombre);
INSERT INTO registro(codigo_usu, fecha, tipo)
values(LAST_INSERT_ID(), NOW(), 'Alta');
END
//
delimiter ;
delimiter //
CREATE PROCEDURE listaEmails (INOUT listaEmails TEXT)
BEGIN
DECLARE final INTEGER DEFAULT 0;
DECLARE v_email varchar(100) DEFAULT "";
-- declare cursor for employee email
DECLARE cursorEmpl CURSOR FOR
SELECT email FROM Empleado;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET final=1;
OPEN cursorEmpl;
WHILE final = 0 DO
FETCH cursorEmpl INTO v_email;
IF final = 0 THEN
SET listaEmails = CONCAT (v_email, ";",
listaEmails);
END IF;
END WHILE;
CLOSE cursorEmpl;
END//
delimiter ;
10. Triggers
Los triggers o disparadores son procedimientos que se ejecutan como respuesta a un evento
producido sobre una tabla concreta de la base de datos. Los eventos causantes del trigger son
las operaciones INSERT, UPDATE o DELETE.
10
Lenguaje procedimental - MySQL
Daniel Santiago
Así pues, a diferencia de las funciones y los procedimientos, los triggers no los ejecuta
explícitamente el usuario de la base de datos. El administrador los programa para que se
ejecuten cuando el usuario realice alguna operación de INSERT, UPDATE o DELETE sobre la
tabla a la que se asocia el trigger.
delimiter //
CREATE TRIGGER nombre_trigger MOMENTO EVENTO ON nombre_tabla FOR
EACHROW
BEGIN
Sentencias;
END//
Es importante tener en cuenta que en MySQL sólo podemos crear un trigger sobre la misma
tabla y el mismo momento y evento.
Vamos a crear un trigger de ejemplo: supongamos que tenemos una tabla Venta, donde se
almacenan las ventas que se realizan en una tienda. Además tenemos otra tabla llamada
Ingreso, en la que se registran las entradas de dinero a la tienda. Queremos que cada vez que
se registre una venta, automáticamente se inserte en la tabla Ingreso una nueva fila que
contenga el importe de la venta realizada. Vamos a automatizar esta operación con el
siguiente trigger:
delimiter //
CREATE TRIGGER registra_ingreso AFTER INSERT ON Venta FOR
EACHROW
BEGIN
INSERT INTO Ingreso(valor) VALUES(NEW.precio);
END//
delimiter ;
11
Lenguaje procedimental - MySQL
Daniel Santiago
El trigger hace un INSERT sobre la tabla Ingreso cuyo valor es el que contiene la variable
NEW.precio. “precio” es el nombre del campo de la tabla Venta, contiene el valor de la venta.
El alias NEW se utiliza en triggers para hacer referencia a la fila que se acaba de añadir (para
triggers de tipo AFTER), o se va a añadir (para triggers de tipo BEFORE).
Así pues, el alias NEW contiene todos los valores de la fila que se va a añadir, identificados por
los nombres de los campos de la tabla sobre la que se actúa. También tenemos el alias OLD,
que hace referencia a una fila existente, antes de ser actualizada o borrada.
delimiter //
CREATE TRIGGER promocionar BEFORE DELETE ON EquipoB FOR EACHROW
BEGIN
INSERT INTO EquipoA VALUES(OLD.nombre, OLD.dorsal);
END//
delimiter ;
Hay casos en los que crearemos un trigger con el objetivo de comprobar que los datos
modificados en una tabla con las operaciones INSERT, UPDATE o DELETE cumplen algún
requisito. En caso de no hacerlo, debemos cancelar la operación, es decir, evitar que se
produzca la operación que desencadenó al trigger. Para esto MySQL nos ofrece la sentencia
SIGNAL.
delimiter //
CREATE TRIGGER comprueba_sueldo BEFORE UPDATE ON Empleado FOR
EACHROW
BEGIN
if NEW.sueldo <= OLD.sueldo then
signal sqlstate ‘12345’ set message_text = ‘El sueldo
debe incrementarse’;
end if;
END//
delimiter ;
12