Sentencias compuestas
BEGIN ... END
Esta sentencia nos permite escribir una o más sentencias contenidas entre las palabras clave BEGIN ... END
. Esta sentencia compuesta puede figurar en programas almacenados (funciones, procedimientos, disparadores, etc.).
Su sintaxis es la siguiente:
1BEGIN2 <lista de sentencias>3END;
Además, es posible escribir sentencias compuestas BEGIN ... END
anidadas.
DELIMITER
Vamos a ver que cuando necesitemos escribir varias sentencias para definir una rutina, el cliente (Workbench o mysql
por línea de comandos) no nos permitirá ejecutarla.
Podemos ver que Workbench nos indica que hay errores en las sentencias, cuando hay más de una, aún utilizando BEGIN ... END
.
Esto se debe a que el cliente interpreta las sentencias una por vez, separándolas por cada aparición del carácter ‘;
’ para luego enviarlas al servidor. Por lo tanto, no puede interpretar la creación de la rutina completa como una sola sentencia.
Solo toma una sentencia hasta donde figura el primer carácter ‘;
’ en lugar de toda la definición, lo cual da un error.
DELIMITER
es un comando del cliente (no del servidor), que tiene como objetivo modificar el carácter que se usa para delimitar o separar sentencias (por defecto ‘;
’) a otro de nuestra elección, por ejemplo: ‘$$
’ o ‘//
’.
Si estamos definiendo una rutina, necesitamos que el cliente tome todas las sentencias que la componen como una sola, y la envíe al servidor. Podemos lograr esto modificando el delimitador y usándolo solo en la última sentencia, para que el cliente pueda pasar toda la definición al servidor. El resto de las sentencias mantienen el caráceter ‘;
’ para que el servidor pueda ejecutarlas correctamente. Luego podemos revertir el delimitador modificado a su valor original.
Por ejemplo, si cambiamos el delimitador a $$
:
1DELIMITER $$ -- modificamos el delimitador de sentencias2
3CREATE FUNCTION ...4BEGIN5 <sentencia1>;6 <sentencia2>;7 <sentencia3>;8 <sentencia4>;9END$$10
11DELIMITER ; -- revertimos al valor original
Esto, efectivamente enviará la creación de la función al servidor como una sola sentencia.
Pronto veremos su uso en la definición de una rutina.
Variables locales
MySQL permite el uso de variables locales en funciones y procedimientos almacenados. Estas variables deben declararse y usarse siempre dentro de la sentencia compuesta BEGIN ... END
. Al terminar de ejecutarse la sentencia compuesta, las variables locales son eliminadas.
La declaración de una variable local debe incluir su tipo, y puede incluir un valor por defecto. La sintaxis es la siguiente:
1DECLARE <nombre> <tipo> [DEFAULT <valor>];
Por ejemplo:
1DECLARE edad INT;2DECLARE nombre VARCHAR(75);3DECLARE e_civil VARCHAR(20) DEFAULT "soltero/a";
Un ejemplo de definición y uso
Veamos un ejemplo que aplica las características vistas:
1USE world;2
3DELIMITER $$4
5CREATE FUNCTION promedio_pob(codigo CHAR(3))6RETURNS FLOAT DETERMINISTIC7BEGIN8 DECLARE prom FLOAT;9 SELECT AVG(population) INTO prom10 FROM city11 WHERE city.CountryCode = codigo;12 IF prom IS NULL THEN13 RETURN 0;14 ELSE15 RETURN prom;16 END IF;17END$$18
19DELIMITER ;
La función pretende recibir un código de tres caracteres como parámetro, que representan el código de un país (en world
cada país tiene un código de 3 letras como PK), y luego devolver el promedio de población de sus ciudades.
En este ejemplo podemos ver que estamos usando el comando DELIMITER
para enviar la definición entera al servidor.
Podemos ver que MySQL mismo aplica esta funcionalidad automáticamente al crear una función si usamos la opción “Create Function” de Workbench, como se ve a continuación:
Por otro lado, en este ejemplo, estamos utilizando una variable local, definida dentro de la función, llamada prom
. Y, además, estamos utilizando una sentencia SELECT ... INTO
para almacenar el resultado de la consulta dentro de la variable. Esto se debe a que no está permitido usar consultas que devuelvan result-sets dentro de una función, como dijimos antes.
Finalmente, utilizamos una sentencia IF
por si el país no contiene ciudades, y por consiguiente no se puede calcular el promedio. En este caso la función AVG()
, que usamos dentro de nuestra función, devolverá NULL
. De ser así, queremos que nuestra función devuelva un 0
en su lugar.
Una vez ejecutada la sentencia de creación, el nombre de la función será conocido para la base de datos y podremos invocarla de la siguiente manera:
1SELECT Name, promedio_pob(Code) AS Poblacion_promedio2FROM country;
O, en la cláusula WHERE
:
1SELECT Name2FROM country3WHERE promedio_pob(Code) > 500000;