GBD06 Contenidos 2016 VI
GBD06 Contenidos 2016 VI
GBD06 Contenidos 2016 VI
Construcción de guiones.
Caso práctico
Con el trabajo desarrollado por Alejandra, Talleres Faber dispone de una base de datos
que integra toda la información necesaria para la gestión del taller. Atrás han quedado
los archivos de facturas en papel, las fichas manuales con los datos de nuestros clientes,
los listados para consultar los recambios y sus precios, etc. El trabajo se ha simplificado
enormemente, el acceso a la información es rápido y los socios están muy satisfechos
con su trabajo.
Pero Alejandra sabe que aún le puede sacar más partido a la base de datos si incorpora al
lenguaje SQL algunas características que, en general, están disponibles en cualquier
lenguaje de programación y que permiten a los usuarios autorizados escribir bloques de sentencias SQL, y guardarlos en el
servidor como otro objeto de la base de datos para utilizarlos cuando lo necesiten.
¿Es eso posible con SQL? Si, la mayoría de los SGBD permiten que los usuarios creen sus propios procedimientos y
funciones mediante una extensión del lenguaje SQL.
¿Cómo podría Alejandra aplicar esto en el taller? La creación de un procedimiento guardado podría servir, por ejemplo, para
recoger todas las sentencias que tenemos que realizar cada vez que un nuevo cliente nos trae su vehículo para que
solucionemos una avería. Si podemos hacer esto de forma automática sin más que activar el procedimiento nos ahorraremos
trabajo y posibles errores, ¿no crees? Los socios de TalleresFaber piensan que si es posible.
1 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Alejandra se plantea la necesidad de añadir funcionalidades nuevas a su aplicación. Para eso va a
tener que pasar de un lenguaje de consultas a un lenguaje de procedimientos. Cree que diseñar sus
propios guiones de sentencias y hacer que se ejecuten cuando lo necesite, le va a permitir mejorar en
el control y mantenimiento de los datos. Para dar este paso, tiene que empezar por investigar cómo
se pueden crear esas rutinas o subprogramas con MySQL.
Hasta ahora hemos visto que el SQL es un lenguaje que permite consultar, añadir, modificar y eliminar datos en las tablas de la base
de datos, pero existen otras funciones propias de un lenguaje de programación que no hemos visto hasta ahora.
Algunos de los procesos que llevamos a cabo habitualmente, como es el caso del registro de una reparación, implican que se ejecuten
varias sentencias, unas detrás de otras. Sería muy interesante que este conjunto de sentencias estuvieran recogidas en una
rutina, y que se pudieran guardar y ejecutar cuando lo necesitemos, sin necesidad de enviar las consultas de una en una.
Tampoco podemos, hasta ahora, establecer condiciones para que se lleven a cabo o no unas determinadas acciones o hacer que
estas se repitan. Si queremos que se muestre un listado para hacer una compra a los proveedores, cada vez que el stock de los
recambios baje por debajo de una cantidad que consideramos como el stock mínimo, y que este proceso se ejecute automáticamente,
necesitaríamos establecer condiciones, algo que no hemos visto con SQL.
Cuando tengamos que realizar operaciones que impliquen la ejecución condicional, así como operaciones repetitivas necesitaremos un
lenguaje de programación de procedimientos como C, Perl, Php, Visual Basic, etc. Podemos insertar sentencias SQL dentro de estos
lenguajes que actuarían como " Lenguaje anfitrión". Este método es muy común pero no el único.
Con la aparición de las bases de datos distribuidas y las bases de datos orientadas a objetos se necesita que
ese código se pueda guardar en la base de datos y sea compartido por las aplicaciones que accedan a los datos,
porque así se mejora el mantenimiento y el control lógico. Por este motivo actualmente la mayoría de los SGBD
han incluido extensiones de programación que incluyen:
El SQL de procedimientos permite utilizar código de procedimientos y sentencias SQL que se guardan dentro de la base de datos
y se ejecutan cuando el usuario los invoca.
Procedimientos guardados.
Funciones.
Triggers.
Reflexiona
En esta unidad vamos a crear procedimientos y funciones, utilizando estructuras alternativas y estructuras repetitivas
dentro de guiones escritos en SQL. La posibilidad de incrustar lenguaje SQL dentro de un lenguaje de programación como es
el caso MySQL con PHP, es muy utilizada en la creación de páginas Web dinámicas. En el módulo de Aplicaciones Web se
estudia esta alternativa.
2 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Alejandra se dispone a estudiar las nuevas posibilidades que se abren al analizar cómo puede crear sus propios
procedimientos y funciones, además de almacenarlos en TalleresFaber y asociar triggers a las tablas.
Con el resto de los empleados del taller que acceden a la base de datos estudia qué procesos se componen de varias
sentencias y se utilizan frecuentemente, para poder crear estas estructuras como procedimientos almacenados; por ejemplo
registrar una nueva reparación, o algunas operaciones habituales con las que sería interesante crear funciones y simplemente
utilizarlas en nuestras consultas, como podría ocurrir con el cálculo del coste de la mano de obra empleada en una reparación.
También que algunas operaciones se realicen automáticamente creando disparadores o triggers, caso de comprobar la
existencia de recambios en el almacén. Para ver si esto es posible, estudiaremos las estructuras funcionales que maneja
nuestro SGBD.
Módulos: Se denominan así a las partes en que se puede dividir un programa para
reducir su complejidad, que son independientes de la aplicación en sí y de las restantes
partes del programa, aunque tienen conexiones con otros módulos. También se les
denomina rutinas o subrutinas. En bases de datos las rutinas pueden ser
procedimientos, funciones o triggers.
En general las funciones y los procedimientos no son muy diferentes. Ambos están constituidos por un conjunto de sentencias lógicamente
agrupadas para realizar una tarea específica. Se trata de un bloque de código que se almacena en tablas del sistema de la base de datos.
Se pueden establecer autorizaciones variadas de ejecución para los usuarios sobre los procedimientos y funciones que se deseen, con
lo que se puede gestionar la seguridad.
Es posible disponer de librerías de procedimientos y funciones almacenadas en el servidor, que pueden ser invocadas desde otros
lenguajes de programación o ser reutilizados para construir otras rutinas.
Mejoran el rendimiento, ya que están almacenados en la base de datos.
Se simplifican las tareas cotidianas.
Son útiles cuando queremos realizar una misma operación que afecte a varias bases de datos y tablas de un mismo servidor.
Un procedimiento almacenado en el servidor ayuda a mantener la consistencia y la integridad de los datos, ya que evita que éstos
puedan ser corrompidos por el acceso de programas defectuosos.
Permiten la validación de datos, y se integran en la estructura de la base de datos. Cuando funcionan con este propósito se denominan
triggers.
Son dinámicos ya que admiten parámetros que se pasan antes de su ejecución y puedan realizar diferentes tareas dependiendo de
esos parámetros que se hayan pasado.
Facilitan la revisión de errores.
La diferencia más importante entre los procedimientos y las funciones es que una función, al final de su ejecución, devuelve un
valor; sin embargo, en los procedimientos esto no es posible, aunque sí que podemos definir múltiples parámetros de salida. Esto
último también es posible en las funciones.
Triggers
Autoevaluación
3 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Relaciona los siguientes conceptos con el término adecuado, incluyendo el número correspondiente:
Ejercicio de relacionar
Relación.
Términos. Conteptos.
(Nº)
Enviar
4 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
MySQL Workbench.
MySQL Query Browser, actualmente no se sigue ofreciendo pero las versiones antiguas están disponibles para su uso.
PhpMyAdmin.
5 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Para diseñar procedimientos, funciones o triggers, Alejandra tiene que empezar por revisar los
elementos que estas rutinas pueden incluir. En unidades anteriores ha tratado los tipos de datos
que emplea MySQL y algunos aspectos relativos a la estructura del lenguaje, como: tratamiento
de palabras reservadas, valores literales, escritura de comentarios, nombres de objetos, etc. Es
necesario tener presente todos estos temas, pero ahora tiene que detenerse en algunos
elementos que forman parte de la estructura del lenguaje, que no ha tratado hasta ahora: las
variables y las estructuras de control. En este apartado, vamos a seguirla en el estudio de las
reglas que hay que cumplir para definir y utilizar variables en un guión.
Variables de usuario.
Una variable de usuario permite almacenar un valor y referirnos a él más tarde. También pueden pasarse de una sentencia a otra. Las
variables 'de usuario' no pueden ser vistas por otros usuarios y desaparecen cuando la conexión se cierra. Para crear una variable de
usuario:
Podemos utilizar como operador de asignación tanto el signo = como el signo := cuando las definimos con SET. Si la variable recibe su
valor de otras sentencias el operador debe ser := para diferenciarlo del operador de comparación.
Podemos utilizar las variables de usuario en cualquier sitio donde se puedan usar expresiones, siempre y cuando no sea necesario que
sea un valor literal. Por ejemplo, la cláusula LIMIT en una SELECT tiene que contener un valor literal que refleje el número de filas
devueltas por la consulta.
No se debe asignar un valor a una variable de usuario en una parte de una sentencia y usarla en otra parte de la misma sentencia. Por
ejemplo: no se debe asignar un valor a una variable en SELECT y hacer referencia a ella en HAVING, GROUP BY u ORDER BY. Puede
dar resultados inesperados.
Si hacemos referencia a una variable sin inicializar con ningún valor, su valor es NULL y de tipo cadena.
Para recordar las reglas sobre cómo escribir: literales, nombres de objetos de la base de datos, comentarios, variables de
usuario y tratamiento de palabras reservadas, puedes acceder al manual de MySQL en el siguiente enlace:
Autoevaluación
Queremos obtener un listado con dos columnas: Marcas de vehículos y el número de vehículos de cada marca que nos ha
visitado. El listado estará ordenado por el número de vehículos.
-
¿Cuál de las siguientes consultas nos muestra el listado correcto?
SELECT Marca, @NUMERO:=count(Matricula) AS 'Numero de vehículos por marca' FROM VEHICULOS GROUP BY
marca ORDER BY @NUMERO;
SELECT Marca, @NUMERO:=count(Matricula) AS 'Numero de vehículos por marca' FROM VEHICULOS GROUP BY
marca ORDER BY 'Numero de vehículos por marca';
SELECT Marca, @NUMERO:=count(Matricula) AS 'Numero de vehículos por marca' FROM VEHICULOS GROUP BY
marca ORDER BY count(Matricula);
SELECT Marca, @NUMERO:=count(Matricula) AS 'Numero de vehículos por marca' FROM VEHICULOS GROUP BY
marca ORDER BY Marca;
6 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Incorrecto. No puede definirse una variable y usarla en otra parte de la misma sentencia.
Solución
1. Incorrecto
2. Incorrecto
3. Opción correcta
4. Incorrecto
7 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
4.- Procedimientos.
Caso práctico
Alejandra considera muy interesante poder almacenar en el servidor de TalleresFaber algunos
bloques de instrucciones que se repiten frecuentemente. Ella cree que esto va a resultar, en
primer lugar, más rápido porque no habrá que enviar las consultas de una en una al servidor, y en
segundo lugar más seguro. Si el código es correcto, no tendrá que estar pendiente de posibles
errores. Por otra parte, todas las personas que acceden a la base de datos con autorización podrán
ejecutar estos procedimientos, aunque se trate de tareas complejas. Parece interesante, ¿no
crees?
Los procedimientos son rutinas o subprogramas compuestos por un conjunto nombrado de sentencias SQL agrupados lógicamente para
realizar una tarea específica, que se guardan en la base de datos y que se ejecutan como una unidad cuando son invocados por su nombre.
Mejoran el rendimiento en la comunicación entre el cliente y el servidor. Dado que un procedimiento consta de varias instrucciones,
desde el cliente MySQL no hay que enviar todas esas instrucciones una a una, sino simplemente se envía la llamada al procedimiento.
Por tanto, con los procedimientos se agiliza el envío de instrucciones desde los clientes al servidor, no recibiendo éste tantas
peticiones de tareas. Como contrapartida, el servidor tiene una mayor carga de trabajo al tener que buscar y decodificar los
procedimientos almacenados cuando son invocados desde los clientes.
Cuando las aplicaciones cliente trabajan con diferentes lenguajes y plataformas, los procedimientos son muy útiles.
Proporcionan mayor seguridad en dos sentidos: por una parte, las aplicaciones y los usuarios no acceden directamente a las tablas,
sino que sólo pueden ejecutar algunos procedimientos y no tendrán que construir esos procesos sobre la base de las sentencias que
los forman, con el posible riesgo de alteraciones no deseadas de los datos por operaciones indebidamente realizadas; y por otra parte,
se pueden establecer autorizaciones de ejecución diferentes, para los usuarios sobre los procedimientos y funciones que se deseen.
Resumir un proceso con varias instrucciones SQL complejas. Se ejecuta más rápido como un procedimiento almacenado que si se
trata de un programa instalado en el cliente que envía y recibe consultas SQL al servidor.
En MySQL los procedimientos se almacenan en la tabla proc. Esta tabla se crea durante la instalación o actualización de MySQL.
8 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Autoevaluación
Con relación a los procedimientos señala la afirmación INCORRECTA:
Incorrecta. Los procedimientos permiten controlar que instrucciones puede ejecutar un usuario.
Solución
1. Incorrecto
2. Incorrecto
3. Opción correcta
4. Incorrecto
9 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
CuerpoDelProcedimiento
END
IN: el procedimiento recibe el parámetro y no lo modifica. Lo usa para consultar y utilizar su valor.
Los parámetros OUT o INOUT se usan cuando se desea que un procedimiento nos devuelva valores en
determinadas variables.
Es obligatorio escribir una lista de parámetros, aunque sea una lista vacía, reflejada con ( ). Por defecto cada parámetro es de tipo IN. Si
queremos especificar otro tipo se escribe delante del nombre del parámetro.
Tipo: Es el Tipo de datos devuelto, puede ser cualquier tipo de datos válido de MySQL.
Las características:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'CadenaComentario'
En características se pueden especificar varios valores.LANGUAGE SQL significa que el cuerpo del procedimiento está escrito en SQL. Por
defecto se tiene esa característica para prever la posible construcción de procedimientos almacenados con otros lenguajes como Java.
DETERMINISTIC: Siempre produce el mismo resultado para los mismos parámetros de entradas.
NOT DETERMINISTIC: Cuando no es determinista, es decir cuando no siempre produce el mismo resultado para
los mismos parámetros de entradas.
La característica SQL SEQURITY sirve para especificar si el procedimiento es llamado, usando los permisos del
usuario que lo creó (DEFINER, que es el valor por defecto), o usando los permisos del usuario que está haciendo la llamada (INVOKER).
La cláusula COMMENT se usa para escribir el comentario que aparecerá cuando se ejecute una sentencia para ver el contenido de un
procedimiento o de una función con: SHOW CREATE PROCEDURE o SHOW CREATE FUNCTION
CuerpoDelProcedimiento: Casi todas las sentencias SQL válidas. El cuerpo del procedimiento empieza con la sentencia BEGIN y termina
con la sentencia END y consta de varias instrucciones. Cada una termina con punto y coma (;).
DELIMITER
En MySQL las sentencias se ejecutan después de escribir el punto y coma (;). Para poder escribir el procedimiento completo evitando que se
ejecute al encontrar el símbolo (;) tenemos que asignar la función de delimitador a otro carácter, como por ejemplo la barra (|), como veremos
en el siguiente ejemplo. Al finalizar asignaremos al punto y coma su función habitual.
10 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Ejercicio resuelto
Obtener un listado de todos los clientes y otro de todos los vehículos de TalleresFaber. (La base de datos deberá estar
abierta).
Mostrar retroalimentación
END | DELIMITER ;
11 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
NombreBaseDatos.NombreProcedimiento
Los procedimientos pueden ser llamados por cualquier usuario con autorización para ello y por el usuario
que los creó. Al ejecutar un procedimiento, el servidor ejecuta automáticamente una sentencia USE BaseDatos.
Cuando se crea un procedimiento, el servidor MySQL nos devolverá indicaciones sobre los errores que
pueda tener el procedimiento. Cuando la sintaxis es correcta, el servidor almacena el procedimiento.
De esta manera dejamos los valores devueltos en las variables indicadas en lugar de mostrarlos en pantalla. Para
hacer esto la consulta debe devolver una sola fila porque no se puede asignar a una variable una lista de
contenidos.
Cuando un procedimiento utiliza un parámetro de salida para llamar al procedimiento, es necesario pasar una
variable que cargue el dato devuelto por el procedimiento. Para definir una variable desde la línea de
comandos, que será una variable temporal de sistema, la variable debe tener el nombre precedido del carácter @ y
se usa la sentencia:
SET @NombreVariable=Valor;
Ejemplos:
SET @Edad=23;
SET @Fecha=' 2017-03-16';
SET @Nombre='Alejandra';
También se puede crear una variable de sistema especificándola como parámetro en la llamada a un procedimiento, tanto si ya se tenía
creada la anterior variable @Num como si se crea en la propia llamada al procedimiento:
CALL NombreProcedimiento(@Num);
Una vez cargada ese valor @Num se puede usar en cualquier sentencia como por ejemplo:
SELECT @Num;
Ejercicio resuelto
Crear un procedimiento que reciba como parámetro de entrada la matrícula de un vehículo y nos muestre las
características del vehículo, y como parámetro de salida el número de reparaciones que ha sufrido ese vehículo.
Obtener un listado de los vehículos que igualen o superen el número de reparaciones anterior.
Mostrar retroalimentación
DELIMITER | CREATE PROCEDURE NumReparaciones (IN Matri varchar(8), OUT NumRep INT) BEGIN
SELECT * FROM VEHICULOS WHERE Matricula=Matri; SELECT COUNT(Matricula) INTO NumRep FROM
REPARACIONES WHERE Matricula=Matri;
END | DELIMITER ;
Para ejecutar el procedimiento:
Call NumReparaciones('1313 DEF', @NumRep);
Para ver el valor devuelto:
12 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
13 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Dentro de cada procedimiento se pueden definir variables locales, es decir, que sólo existen mientras se ejecuta el procedimiento y
después se destruyen. Las variables locales únicamente son visibles dentro del bloque BEGIN … END donde estén declaradas, y deben
estar al comienzo de este bloque, antes de cualquier sentencia.
Para proporcionar un valor inicial a la variable declarada se utiliza DEFAULT. El valor inicial puede ser una constante o una expresión. En
caso de no emplear DEFAULT el valor por defecto es NULL. En cuanto al tipo, puede utilizarse cualquier tipo válido en MySQL.
Para cada variable que se declara es necesario utilizar una sentencia DECLARE distinta.
Sentencia SET
Para asignar un valor a cualquier variable (local, global o pasada como parámetro) se utiliza la sentencia SET. Las variables que se asignan
con SET pueden declararse dentro de una rutina o como variables globales de servidor.
Para modificar el valor de una variable o de un parámetro utilizando una asignación debe utilizarse la sentencia:
En Expresión puede haber una constante, una función, una variable, una operación entre ellas o incluso una sentencia SELECT que devuelva
un solo resultado.
Procedencia.
Ejercicio resuelto
14 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Realizar un procedimiento que liste el vehículo o los vehículos que más reparaciones han sufrido durante el mes que se
indique:
Mostrar retroalimentación
DECLARE a INT; SELECT COUNT(*) INTO a FROM REPARACIONES WHERE MONTH(FechaEntrada)=Mes GROUP BY Matricula ORDER BY
COUNT(*) DESC LIMIT 1; SELECT VEHICULOS.Matricula, Marca, Modelo FROM REPARACIONES INNER JOIN VEHICULOS ON
REPARACIONES.Matricula=VEHICULOS.Matricula WHERE MONTH(FechaEntrada)=Mes GROUP BY VEHICULOS.Matricula HAVING
COUNT(*)=a;
END |
15 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
6.- Funciones.
Caso práctico
Aunque el uso de procedimientos almacenados ha supuesto para Alejandra la posibilidad de
ejecutar con una sola llamada una serie de sentencias, con las ventajas que hemos visto. Los
procedimientos no permiten algo que Alejandra considera muy útil: poder ser llamados dentro de
una sentencia SQL. Para poder llamar a un conjunto de instrucciones dentro de una sentencia SQL
necesitará utilizar funciones, que además devuelven siempre un valor.
En unidades anteriores Alejandra ha incorporado funciones propias de SQL para obtener los
resultados requeridos en las consultas que ha realizado en TalleresFaber. ¿Podrá crear ella sus
propias funciones?
La forma de crear una función es similar a la de creación de un procedimiento. Las funciones, una vez creadas, quedan almacenadas en el
servidor y pueden ser invocadas en cualquier momento por cualquier cliente MySQL.
Aunque las funciones comparten muchas características con respecto a los procedimientos, presentan también algunas diferencias:
Las funciones devuelven siempre un dato a través de una sentencia RETURN. El dato se corresponde con un tipo de datos declarado
para la función.
Las funciones no pueden trabajar con parámetros OUT o INOUT únicamente con parámetros de entrada IN, por eso no se especifica el
tipo.
En las funciones no se pueden usar sentencias que devuelvan filas de resultados (SELECT, SHOW, DESC). Se pueden usar sentencias
SELECT que devuelvan una fila siempre que los datos devueltos se carguen en variables, para sentencias que devuelvan varias filas
trataremos los cursores.
Las funciones son llamadas a ejecución, al igual que las funciones internas de MySQL, escribiendo su nombre y la lista de parámetros
pasados a la función encerrados entre paréntesis. Por tanto no usa una sentencia de llamada como la sentencia CALL en el caso de los
procedimientos.
Las funciones podrán ser llamadas desde cualquier sentencia SQL como SELECT, UPDATE, INSERT, DELETE. Los procedimientos nunca
pueden ser llamados a ejecución dentro de otra sentencia.
Las funciones se llaman usando: NombreFunción(parámetros).
Las funciones definidas por el usuario están disponibles en versiones anteriores de MySQL. Actualmente se soportan junto con los
procedimientos almacenados. En el futuro será posible un marco para procedimientos almacenados externos en lenguajes distintos a SQL,
por ejemplo PHP.
Ejercicio resuelto
Escribe una función que recibe una cadena de caracteres y devuelve un mensaje de saludo.
Mostrar retroalimentación
DELIMITER |
CREATE FUNCTION Saludo (S CHAR(20))
RETURNS CHAR(50)
BEGIN
DECLARE A CHAR(50) DEFAULT 'HOLA, ';
SET A=CONCAT(A, S, '!');
RETURN A;
END |
DELIMITER ;
Una vez creada la función podemos llamarla desde sentencias SQL, por ejemplo desde SELECT:
SELECT Saludo ('caracola');
16 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Mostrar retroalimentación
17 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
<CuerpoDeLaFuncion
END
Como hemos dicho en el punto anterior con relación a los parámetros, no es necesario especificar
el tipo porque no trabajan con parámetros OUTo INOUT. La cláusula RETURNS es obligatoria e
indica el tipo de retorno que nos va a devolver la función. En el cuerpo de la función debe incluirse
un comando RETURN valor o expresión, debiendo ser expresión del mismo tipo que la función.
Generalmente la sentencia RETURN para devolver un resultado es la última del cuerpo de la
función.
Tipo: El Tipo de datos devuelto puede ser cualquier tipo de datos válido de MySQL.
Las características:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'CadenaComentario'
La descripción de las características que puede incorporar una función es similar a la que hemos visto para los procedimientos.
CuerpoDeLaRutina:
Contiene el código ejecutable de la función entre las sentencias BEGIN y END.
Ejercicio resuelto
Escribe una función que reciba una fecha y devuelva el año correspondiente a esa fecha. (cómo si esa función no
18 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Probar la función obteniendo el año correspondiente a tu fecha de nacimiento. Probar la función con el valor que tendrá dentro
de un año.
Mostrar retroalimentación
DECLARE an CHAR(10); DECLARE a INT; SET an=CONVERT(fecha,CHAR); SET an=LEFT(an,4); SET a=CONVERT(an,UNSIGNED); RETURN a;
END| DELIMITER ;
SELECT año('1985-11-06') AS 'Año de mi nacimiento';
SELECT año (CURDATE())+1 AS 'Año próximo';
19 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Aunque Alejandra ha visto como crear sus propios procedimientos y
funciones, para muchas de las necesidades que le van a surgir en la
explotación de TalleresFaber, esto no va ser necesario ya que MySQL
incorpora numerosas funciones, y es muy probable que si las conoce,
pueda utilizarlas directamente en las sentencias SQL.
Todo SGBD permite que los usuarios creen sus propias funciones y procedimientos. Además, cualquier SGDB incluye un conjunto de
funciones en la distribución. Sean funciones de la distribución o funciones desarrolladas por el usuario, pueden usarse en sentencias SQL
para que estas sentencias traten los resultados que devuelven las funciones.
MySQL incluye un numeroso conjunto de funciones. En esta unidad se expone la sintaxis de utilización de cada función y se agrupan las
funciones por el tipo de datos que manipulan o por el tipo de proceso que llevan a cabo.
Normalmente las funciones operan con las columnas de una tabla, se utilizan en las expresiones que indican los datos que se muestran en
una consulta, en las condiciones WHERE, en las expresiones SET para obtener los datos con los que se modifica una columna, en los valores
que se insertan mediante INSERT, etc.
Funciones matemáticas.
Funciones de cadenas de caracteres.
Funciones de fecha y hora.
Funciones de control de flujo.
Funciones de búsqueda sobre índices FULLTEXT.
Funciones de conversión.
Funciones de agregado.
Otras funciones.
Debes conocer
Puedes consultar un resumen de las funciones disponibles en MySQL en en el siguiente enlace. Obviamente no se trata de
aprenderlas de memoria, sino de saber que existen para utilizarlas cuando se necesiten.
20 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Funciones matemáticas.
Funciones de conversión.
Funciones de agregado.
Otras funciones.
21 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Características:
Utilizamos la cláusula IF EXISTS para evitar que si el procedimiento o la función ya existen, la sentencia devuelva un error.
Otras sentencias útiles:
Devuelve algunas características del procedimiento o de la función como el nombre de la base de datos, el tipo de rutina: procedimiento o
función, el creador, fecha de creación y modificación, etc.
Ejercicio resuelto
1. Diseñar una función que calcule los días que pasa un vehículo en el taller de reparaciones. Comprobar que la
función no exista.
2. Probar la función en una consulta SELECT para el vehículo de matrícula 1313 DEF.
3. Mostrar la sentencia que describe las características de autor, fecha, etc. de todas las funciones que empiecen
22 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
por d.
Mostrar retroalimentación
DELIMITER | DROP FUNCTION IF EXISTS dias| CREATE FUNCTION dias (FechaE date, FechaS date) RETURNS INT BEGIN
END| DELIMITER ; SELECT IdReparacion, Matricula, dias (FechaEntrada, FechaSalida) AS 'Dias en el taller' FROM reparaciones WHERE matricula='1313 DEF';
SHOW FUNCTION STATUS LIKE 'd%';
23 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
A medida que va avanzando en la utilización de los procedimientos y de las funciones, a
Alejandra se le van abriendo nuevas perspectivas. Muchas de las sentencias que antes tenía
que repetir periódicamente, las tiene ahora almacenadas como procedimientos que puede
ejecutar como una única sentencia; algunas operaciones complejas que antes tenía que
ejecutar en una consulta ahora están recogidas como una función propia. Como suele
ocurrir, una vez que ha empezado a dominar estos nuevos recursos se le van ocurriendo otras
aplicaciones para su base de datos. ¿Y si pudiera elegir la sentencia que se ejecuta en
función de una condición o del valor de un dato? ¿No se podría repetir un proceso un número
determinado de veces hasta que se alcance una condición? Alejandra está echando en
falta dos tipos de estructuras que no hemos visto hasta ahora y que van a abrir nuevas
posibilidades a sus procedimientos y funciones: las estructuras de control.
En el SQL de MySQL disponemos de varias estructuras o sentencias de control de flujo. Estas sentencias sirven para codificar estructuras de
decisión y repetitivas en una función o en un procedimiento.
IF
CASE
LOOP
WHILE
ITERATE
LEAVE
En otros SGBD se utiliza también la sentencia FOR que en caso de MySQL no se admite.
Estas estructuras pueden contener una sola sentencia o un bloque de ellas usando los comandos BEGIN … END. Unas estructuras pueden
estar anidadas dentro de otras.
END IF;
Si ninguna de las condiciones es verdadera, en caso de que haya ELSE, se ejecutan las sentencias asociadas a ELSE
(Sentencias3), si no hay ELSE no se ejecuta ninguna sentencia.
Ejercicio resuelto
Realizar una función que devuelva el nombre del día de la semana correspondiente a la fecha indicada.
Mostrar retroalimentación
DELIMITER | DROP FUNCTION IF EXISTS DiaSemana| CREATE FUNCTION DiaSemana(d DATE) RETURNS VARCHAR(10) BEGIN
ELSEIF n=2 THEN RETURN 'Lunes'; ELSEIF n=3 THEN RETURN 'Martes'; ELSEIF n=4 THEN RETURN 'Miercoles';
ELSEIF n=5 THEN RETURN 'Jueves'; ELSEIF n=6 THEN RETURN 'Viernes';
24 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
END IF;
END| DELIMITER ;
SELECT DiaSemana(CURDATE());
El ejemplo anterior también podría haberse realizado con IF…ELSE anidados en lugar de ELSEIF.
25 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
CASE Expresión
[ELSE Sentencias_Else]
END CASE;
Se evalúa la expresión y se ejecutan las sentencias correspondientes al primer valor igual al valor de la expresión.
Si ningún valor es igual, se ejecutan las sentencias que hay dentro de ELSE, caso de que hubiera ELSE.
Ejercicio resuelto
Realizar la función que obtiene el día de la semana correspondiente a una fecha con la sentencia CASE.
Mostrar retroalimentación
DELIMITER | DROP FUNCTION IF EXISTS DiaSemana| CREATE FUNCTION DiaSemana(d DATE) RETURNS VARCHAR(10) BEGIN
WHEN 1 THEN SET dia= 'Domingo'; WHEN 2 THEN SET dia= 'Lunes'; WHEN 3 THEN SET dia= 'Martes'; WHEN 4 THEN SET dia= 'Miércoles'; WHEN 5 THEN
SET dia= 'Jueves'; WHEN 6 THEN SET dia= 'Viernes'; WHEN 7 THEN SET dia= 'Sábado';
END| DELIMITER ;
CASE
END CASE;
Si una condición se evalúa como verdadera, se ejecutan las sentencias correspondientes. Si no se cumple ninguna condición, se ejecutan las
sentencias de la cláusula ELSE en caso de que la haya.
Ejercicio resuelto
Realizar una función que devuelve la calificación (texto) correspondiente a una calificación numérica con decimales.
Deben tratarse errores.
Mostrar retroalimentación
DELIMITER | DROP FUNCTION IF EXISTS Calificacion| CREATE FUNCTION Calificacion (c FLOAT) RETURNS VARCHAR(20) BEGIN
DECLARE Ctexto VARCHAR(20); CASE WHEN c>=0 AND c<5 THEN SET Ctexto='Suspenso'; WHEN c>=5 AND c<6 THEN SET Ctexto='Suficiente';
WHEN c>=6 AND c<7 THEN SET Ctexto='Bien'; WHEN c>=7 AND c<9 THEN SET Ctexto='Notable'; WHEN c>=9 AND c<=10 THEN SET
Ctexto='Sobresaliente'; ELSE SET Ctexto='Calificación errónea'; END CASE; RETURN Ctexto;
END| DELIMITER ;
No hay que confundir las estructuras IF o CASE para procedimientos almacenados que acabamos de ver, con las funciones IF() o
CASE() ya que se trata de funciones de control de flujo y en el caso de CASE su sintaxis es también diferente.
26 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
[Etiqueta_Inicio:] LOOP
Sentencias
END LOOP [Etiqueta_Fin];
Permite realizar un bucle repetitivo que no tiene ninguna condición de salida. Para salir de un bucle LOOP es necesario incluir una sentencia
de salida forzada: LEAVE.
La sentencia LEAVE
LEAVE Etiqueta
Este comando se utiliza para abandonar cualquier control de flujo etiquetado. Puede usarse con BEGIN … END o con bucles.
La sentencia ITERATE
ITERATE Etiqueta
Sólo puede usarse dentro de LOOP, REPEAT y WHILE. Esta sentencia provoca un salto para reiniciarse de nuevo el bucle desde la primera
sentencia. Para ello, es necesario que el bucle correspondiente esté marcado con una etiqueta.
Ejercicio resuelto
Ejemplo de utilización de las sentencias LEAVE e ITERATE dentro de un bucle LOOP.
Mostrar retroalimentación
CREATE PROCEDURE BucleLoop (P1 INT, OUT P2 INT) BEGIN SET P2=0; Etiqueta1:LOOP
SET P1=P1+1; SET P2=P2+2; IF P1<10 THEN ITERATE Etiqueta1; END IF; LEAVE Etiqueta1;
La sentencia REPEAT
[Etiqueta_Inicio:] REPEAT
Sentencias;
UNTIL Condición
END REPEAT [Etiqueta_Fin];
Permite representar una estructura repetitiva del tipo repetir … hasta. En esta estructura se empieza ejecutando las sentencias que están
dentro de REPEAT. Al final se evalúa si se cumple o no la condición indicada en UNTIL. Si la condición se cumple, se sale del bucle, en caso
contrario se vuelve al comienzo y se repite una nueva iteración. Las sentencias continúan repitiéndose hasta que la condición es cierta.
Ejercicio resuelto
Realizar una función que recibe una cadena A de texto y una letra para devolver una cadena que sustituye con
caracteres subrayados todos los de A excepto los que son iguales a la letra pasada.
27 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Mostrar retroalimentación
DECLARE oculta VARCHAR(20); DECLARE t VARCHAR(20); DECLARE pos INT DEFAULT 1; SET oculta=''; REPEAT
SET t=substring(p, pos, 1); IF t= letra THEN SET oculta=concat(oculta,letra); ELSE SET oculta=concat(oculta, '_'); END IF; SET
pos=pos+1;
END
28 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Sentencias
Permite representar una estructura repetitiva del tipo Mientras… En este bucle la condición se evalúa al iniciar el bucle, si la condición se
cumple, se ejecutan las sentencias que están dentro del bucle y cuando se llega a la última se vuelve a evaluar la condición de WHILE,
repitiéndose este proceso de nuevo si la condición se cumple. La salida del bucle se produce cuando la condición es falsa.
Ejercicio resuelto
Realizar un procedimiento que carga en una tabla, FECHAS, con una sola columna F de tipo DATE, las fechas en la que no
se han realizado reparaciones desde la fecha pasada hasta la fecha actual.
Mostrar retroalimentación
ELSE
SELECT count(*) INTO n FROM REPARACIONES WHERE FechaEntrada=fi; IF n=0 THEN INSERT INTO fechas values(fi); END IF;
SET fi= adddate(fi, INTERVAL 1 DAY);
END
Crear un procedimiento que añada una línea en la tabla Incluyen para añadir un nuevo recambio en una reparación, y
que posteriormente actualice el stock de recambios disponible. Comprobar antes de añadir la fila que haya unidades
suficientes en stock de ese recambio.
Comprobar el procedimiento con los siguientes datos: IdRecambio= 'BJ_111_666', IdReparacion=2, Unidades=1
Mostrar retroalimentación
CREATE PROCEDURE InsertarIncluyen (IN IdRec char(10), IN IdRepar INT , IN Unid SMALLINT) BEGIN DECLARE Num INT; SELECT Stock INTO Num FROM
RECAMBIOS WHERE IdRecambio = IdRec;
IF Num >= Unid THEN INSERT INTO Incluyen VALUES (IdRec, IdRepar, Unid); UPDATE RECAMBIOS SET Stock=Stock-Unid; END IF;
END;
29 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Alejandra ha observado que, al ejecutar procedimientos o funciones éstos se interrumpen porque ha ocurrido algún error,
del cual MySQL sólo devuelve un código. En muchos casos a Alejandra le interesa que el procedimiento continúe su
ejecución o que se lleve a cabo una acción determinada, por eso se dispone ahora a repasar cómo manipular los errores en
MySQL, y las posibilidades que ofrece para cambiar la respuesta del servidor ante un error en una rutina almacenada.
Cuando se ejecuta una sentencia SQL, el servidor devuelve un código de error (numérico) relativo a esa sentencia. Por ejemplo, se devuelve
código de error, cuando se ejecuta una sentencia que trata de insertar una fila con un valor que ya existe en la columna que es clave
primaria.
Esto, generalmente, es un grave problema ya que es normal que, aunque una sentencia de una rutina produzca error, se desee procesar las
siguientes sentencias de la rutina. Para solucionar este problema, MySQL permite usar manipuladores de errores o handlers que sirven
para indicar como debe responder el servidor MyQL, en procedimientos y funciones, a situaciones de error.
Para trabajar con manipuladores de error hay que declarar estos manipuladores, e indicar sobre que sentencia
o sentencias actuarán. Al declarar un manipulador puede haberse declarado antes la condición ante la que
responderá el manipulador, dándole un nombre a esa condición. Para declarar condiciones de manipulación
de excepciones hay que usar la sintaxis:
Por ejemplo, si insertamos una fila en una tabla y esa fila no se puede insertar porque el valor de la clave primaria ya existe, MySQL devuelve
una indicación de error cuyo valor de estado (SQLSTATE) es '23000' y cuyo código de error MySQL es 1062 (los valores de estado
devueltos para las sentencias ejecutadas son cadenas de caracteres mientras que los códigos de error son enteros).
Para crear una condición de manipulación de excepciones sobre esa situación de error, declaramos el manipulador de la forma:
TipoManipulador:
CONTINUE
| EXIT
ValorCondicion:
SQLSTATE ValorEstado
| NombreCondicion
| SQLWARNING
| NOT FOUND
| SQLEXCEPTION
| CódigoErrorMySQL
mysql> delimiter //
30 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
-> END;
-> //
Query OK, 0 rows affected (0.00 sec)
Si se produce cualquiera de las condiciones de error declaradas en el manipulador, se ejecutará la sentencia especificada para el
manipulador.
Un manipulador de tipo CONTINUE hace que prosiga la ejecución de la siguiente sentencia a aquella donde se hay producido un error
controlado por el manipulador. Un manipulador de tipo EXIT hace que se termine el bloque en el que se encuentra la sentencia que ha
producido el error controlado por el manipulador y, por tanto, termine la rutina en la que se encuentra.
SQLWARNING se usa para referenciar a todos los valores de estado que comienzan por 01.
NOT FOUND se usa para referenciar a todos valores de estado que comienzan por 02.
SQLEXCEPTION se usa para referenciar a todos los valores de estado que no son controlados por SQLWARNING y por NOT FOUND.
31 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
BEGIN
END;
SELECT * FROM REPARACIONES;
END |
DELIMITER ;
Suponiendo que hacemos la llamada al procedimiento y la matrícula a insertar ya existe en la tabla, no se ejecutará ninguna de las sentencias
que siguen a la sentencia INSERT que da error.
CALL Manipuladores()
ERROR 1062 (23000): Duplicate entry '1234BMY' for key 1
Sabiendo que un error de inserción por clave duplicada da el valor de estado 23000 y el código de error 1062, modificaríamos el
procedimiento anterior para que controlase ese error de una forma similar a la siguiente:
DELIMITER |
CREATE PROCEDURE Manipuladores()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SELECT 'Se ha producido un error';
BEGIN
INSERT INTO VEHICULOS (Matricula,Marca, Modelo) VALUES ('2233 ABC','Ford','Mondeo');
SELECT * FROM CLIENTES;
SELECT COUNT(*) FROM CLIENTES;
END;
SELECT * FROM REPARACIONES;
END|
DELIMITER ;
Ahora, al ejecutar el procedimiento, se ejecuta la sentencia asociada al manipulador cuando se produce el error, es decir, se escribe el
mensaje se ha producido un error y se continua (tipo CONTINUE) con la sentencia que hay a continuación de la sentencia INSERT que produjo
error. Si el manipulador fuese tipo EXIT se ejecutaría la sentencia asociada al manipulador, pero no las siguientes y se iría al final del
procedimiento.
La declaración del manipulador podríamos haberla hecho usando el código de error y no el valor de estado de la sentencia. En su lugar
podríamos haber puesto:
DECLARE EXIT HANDLER FOR 1062 SELECT 'Se ha producido un error';
O incluso podíamos haber definido previamente una condición de error llamada Respuesta, para después declarar un manipulador para esa
condición de error:
DECLARE Respuesta CONDITION FOR 1062;
DECLARE CONTINUE HANDLER FOR Respuesta SELECT 'Se ha producido un error';
Una forma sencilla de cancelar la sentencia que desencadena un trigger asociado a un evento de tipo BEFORE y evitar que se
ejecute, es incluir en el trigger una instrucción que produzca un error. Esto provoca que el evento que lo disparó (INSERT, UPDATE o
DELETE) no se ejecute porque el trigger se ejecuta antes y encadena un error sobre el evento.
Ejercicio resuelto
Partiendo del ejercicio resuelto del apartado 4.2, añade un Handler que evite que las consultas se ejecuten cuando la
matrícula que se reciba no exista en TalleresFaber:
Mostrar retroalimentación
DELIMITER | DROP PROCEDURE IF EXISTS NumReparaciones | CREATE PROCEDURE NumReparaciones (IN Matri varchar(8), OUT NumRep INT) BEGIN
DECLARE m VARCHAR(8); DECLARE EXIT HANDLER FOR SQLSTATE '02000' SELECT 'La matricula no existe'; SELECT MATRICULA into m FROM
VEHICULOS WHERE Matricula=Matri; BEGIN
SELECT * FROM VEHICULOS WHERE Matricula=Matri; SELECT COUNT(Matricula) INTO NumRep FROM REPARACIONES WHERE
Matricula=Matri;
32 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Caso práctico
Alejandra está consiguiendo sacar mucho partido a las funciones y procedimientos que tiene almacenados
en la base de datos; no sólo para sus propias consultas sino que le permiten ocultar al resto de usuarios de
TalleresFaber operaciones complejas y manejar las acciones que deben llevarse a cabo cuando se produce
algún error.
Pero aún puede evitar algunas limitaciones importantes, como por ejemplo que el resultado de las consultas tratadas en sus
funciones y procedimientos tenga que devolver una única fila. ¿No podría utilizarse algún elemento que le permita tratar
varias filas? Efectivamente, se trata de los cursores y a continuación aprenderás a usarlos en tus procedimientos y funciones.
Un cursor es una consulta declarada que provoca que el servidor, cuando se realiza la operación de abrir cursor, cargue en memoria los
resultados de la consulta en una tabla interna. Teniendo abierto el cursor, es posible, mediante una sentencia FETCH, leer una a una las filas
correspondientes al cursor y, por tanto, correspondientes a la consulta definida. Los cursores deben declararse después de las variables
locales.
DECLARAR UN CURSOR:
Se declaran mediante una sentencia:
En la sentencia SELECT de declaración del cursor puede haber cualquier cláusula utilizada dentro de una SELECT, excepto la cláusula INTO. En
un procedimiento o en una función podemos definir tantos cursores como necesitemos.
En el siguiente ejemplo se crea un procedimiento que obtiene en una variable S la suma de los Precios correspondientes a los 3 Recambios
más caros obtenidos en una consulta:
DELIMITER $$
DROP PROCEDURE IF EXISTS `SumaPrecios` $$
CREATE PROCEDURE `SumaPrecios`(OUT S INT)
BEGIN
DECLARE k INT;
DECLARE cur_1 CURSOR FOR
SELECT PrecioReferencia FROM RECAMBIOS ORDER BY PrecioReferencia DESC;
OPEN cur_1;
SET S=0;
FETCH cur_1 INTO k;
SET S=S+k;
FETCH cur_1 INTO k;
SET S=S+k;
FETCH cur_1 INTO k;
SET S=S+k;
END $$
DELIMITER ;
ABRIR UN CURSOR:
Para abrir el cursor o, lo que es lo mismo, hacer que los resultados de la consulta asociada al cursor queden cargados en memoria, se usa la
sentencia:
OPEN NombreCursor;
La primera vez que se lea sobre el cursor se leerá la primera fila de la consulta, la segunda vez se leerá sólo la segunda fila y así
sucesivamente. Si quisiéramos volver a leer desde la primera, tendríamos que cerrar el cursor y abrirlo nuevamente.
Esta sentencia asigna los valores devueltos de la fila que se está leyendo sobre las variables indicadas tras INTO. Debe haber una variable por
cada valor que devuelve el cursor (por cada valor seleccionado en la SELECT). Un cursor se comporta como un puntero que inicialmente
apunta a los datos de la primera fila y, cuando se lee, el puntero se incrementa para apuntar a la siguiente fila y así sucesivamente hasta que
el puntero llega al final tomando el valor nulo.
33 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
CLOSE NombreCursor;
A continuación se muestra un ejemplo de una función que obtiene usando cursores la suma de los kilómetros de los automóviles en
reparación de la marca que se pase a esa función.
Si la marca no existe se controla el error devuelto por una SELECT nula ('02000') asignando un valor 0 a la variable Existe. Esto hace que
cuando se llegue a no poder leer una fila con la condición dada también se pueda salir del bucle.
CREATE FUNCTION SumaKmMarca(m char(15))
RETURNS INT
BEGIN
END
Ejercicio resuelto
Crear una función que reciba una Ciudad y nos devuelva en una fila los nombres de los clientes de esa ciudad
separados por comas, utilizando cursores.
Mostrar retroalimentación
DELIMITER $$ DROP FUNCTION IF EXISTS `talleresfaber`.`ListadoClientes` $$ CREATE FUNCTION `ListadoTelefono`(Ciudad VARCHAR(20)) RETURNS
VARCHAR(200) BEGIN DECLARE ultima_fila INT DEFAULT 0; DECLARE SNombre VARCHAR(50) default ''; DECLARE Resultado VARCHAR(200) default '';
DECLARE LONGITUD INT default CHAR_LENGTH(Ciudad); DECLARE cursor1 CURSOR FOR SELECT Nombre FROM Clientes WHERE Right(Direccion,
Longitud)=Ciudad; DECLARE CONTINUE HANDLER FOR NOT FOUND SET ultima_fila=1; OPEN cursor1; c1_loop: LOOP
LEAVE c1_loop;
ELSE
END IF;
RETURN Resultado;
END;
34 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
11.- Triggers I.
Caso práctico
Aunque muchos procesos rutinarios que se llevan a cabo en TalleresFaber se han
simplificado, todavía en muchos casos es necesario que las personas recuerden que
deben realizar determinadas operaciones. Es el caso, por ejemplo, del registro de la
disminución de las unidades en Stock de un recambio cuando se incorpora a una
reparación.
Un trigger o disparador es una rutina asociada con una tabla que se activa o ejecuta automáticamente cuando se produce algún evento
sobre la tabla.
Un trigger siempre se invoca antes o después de que una fila se selecciona, inserta o actualiza.
Un trigger siempre está asociado con una tabla de la base de datos.
Cada base de datos puede o no, tener uno o más triggers.
Un trigger se ejecuta como parte de la transacción que lo activó.
CREACIÓN DE UN TRIGGER
La sintaxis de creación de un trigger es la siguiente:
El NombreTabla especifica la tabla que motiva el disparo del trigger cuando se produce algún evento sobre la
tabla. No se puede asociar un trigger con una vista o con una tabla de tipo TEMPORARY. Disparo especifica el
momento en el que se disparará el trigger, puede ser BEFORE o AFTER indicando antes o después,
respectivamente, de ejecutarse la sentencia que produzca el disparo del trigger. El evento indica la acción
sobre la tabla que provocará el disparo del trigger y puede ser INSERT, UPDATE, o DELETE. Por ejemplo, un
trigger BEFORE para sentencias INSERT puede usarse para comprobar si los valores que van a ser insertados son
válidos ante alguna regla.
FOR EACH ROW indica que el trigger se lanza por cada fila afectada por el evento.
Sentencia indica la sentencia o sentencias que se ejecutan cuando se lanza a ejecución el trigger, es decir, la acción realizada por el trigger.
Nunca puede haber dos triggers o más para una misma tabla, que respondan al mismo evento y en el mismo momento de disparo.
No pueden incluirse sentencias de control de transacción, tales como COMMIT y ROLLBACK en el código de un trigger.
Si queremos que se ejecuten varias sentencias, tenemos que agruparlas dentro de un bloque BEGIN ... END. Dado que los triggers son rutinas,
podemos usar para ellos las mismas sentencias que en los procedimientos y en las funciones.
Para hacer referencia en un trigger a las columnas de la tabla que dispara el trigger, no podemos usar normalmente el nombre de esa
columna. Para hacer referencia a esas columnas necesitamos los especificadores OLD y NEW que indican el valor de la columna antes de la
modificación que se trata de hacer y después de la modificación respectivamente.
Por ejemplo el valor de la columna nombre que se ha insertado con un INSERT que ha disparado un trigger, se representa como NEW.Nombre.
35 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
VER UN TRIGGER
SHOW TRIGGERS
Ejemplo: Realizar un trigger que antes de insertar una nueva fila en la tabla facturas modifique el estado de la reparación del vehículo
(REPARADO=1) y la fecha de salida de la tabla REPARACIONES sustituyéndola por la fecha actual de emisión de la factura en caso de que
no se hubiera modificado anteriormente.
DELIMITER $$
Ejercicio resuelto
Realiza un trigger que cuando modifiquemos la tabla REPARACIONES, si el valor de la columna REPARADO es 1 añada
la factura correspondiente insertando una fila en la tabla FACTURAS.
NOTA: Para ejecutar este trigger deberás eliminar previamente el anterior para evitar que se activen mutuamente.
Mostrar retroalimentación
DELIMITER $$ DROP TRIGGER IF EXISTS `talleresfaber`.`EmitirFactura` $$ CREATE TRIGGER `talleresfaber`.`EmitirFactura` AFTER UPDATE ON
REPARACIONES FOR EACH ROW BEGIN DECLARE Codcli CHAR(5); DECLARE Nfact INT(4) default 0; DECLARE Rep INT(1) default 0; SELECT REPARADO
INTO Rep FROM REPARACIONES WHERE IdReparacion=New.Idreparacion; SELECT IdFactura INTO Nfact FROM FACTURAS WHERE
IdReparacion=New.Idreparacion;
END IF;
END $$ DELIMITER ;
36 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Recurso Recurso
Datos del recurso (1) Datos del recurso (2)
(1) (2)
Autoría: Joebeone.
Autoría: Jorge Felipe González.
Licencia: CC by.
Licencia: CC by-nc-sa.
Procedencia: Montaje sobre:
Procedencia: Montaje sobre: http://www.flickr.com/photos/jork85
http://www.everystockphoto.com
/1446872526/
/photo.php?imageId=2744567&s=1#top
Autoría: Thmagans.
Autoría: Pst. Licencia: CC BY-SA.
Licencia: GPL. Procedencia: Montaje sobre:
Procedencia: Elaboración propias. Captura de pantalla del programa http://commons.wikimedia.org
PhpMyAdmin. /wiki/File:Logo_Rencontres_Cin
%C3%A9matographiques_In%26Out.jpg
Autoría: Maiscio.
Autoría: Derek Ramsey.
Licencia: CC by-nc-sa.
Licencia: CC-by.
Procedencia:
Procedencia: Montaje sobre: http://commons.wikimedia.org/wiki/File:
http://maiscio.blogspot.com/2010/10
Christmas_Fern_Polystichum_acrostichoides_Curl_Closeup_2000xpx.jpg
/filming-archidonne.html
Autoría: bjornmeansbear.
Autoría: Pitr.
Licencia: CC by-sa.
Licencia: PD.
Procedencia: http://www.flickr.com
Procedencia: Montaje sobre: http://www.openclipart.org/detail/121
/photos/bjornmeansbear/4646140669
Autoría: Bardomm.
Autoría: Dawn Endico.
Licencia: CC by-nc-sa.
Licencia: CC by-sa.
Procedencia: http://www.flickr.com
Procedencia: http://www.flickr.com/photos/candiedwomanire/1651870l
/photos/bardomm/1470585937
Autoría: Mattes.
Autoría: Jorge Felipe González.
Licencia: PD.
Licencia: CC by-nc-sa.
Procedencia:
Procedencia: Montaje sobre: http://www.flickr.com/photos/jork85
http://commons.wikimedia.org
/1776778995/
/wiki/File:VTBS-structure.JPG
Autoría: CountingPine.
Autoría: Wangi.
Licencia: CC by.
Licencia: PD.
Procedencia: Montaje sobre:
Procedencia: http://commons.wikimedia.org
http://commons.wikimedia.org
/wiki/File:Southampton_Airport_Control_Tower.jpg
/wiki/File:If-Then-Else-diagram.svg
37 de 38 16/7/16 19:25
Construcción de guiones. http://localhost:51235/temp_print_dirs/eXeTempPrintDir_Uwst...
Autoría: Busysignals.
Autoría: Jumanji Solar.
Licencia: Cc by-nc-sa.
Licencia: Cc by-nc-sa.
Procedencia: http://www.flickr.com
Procedencia: http://www.flickr.com/photos/jumanjisolar/5309418648/
/photos/crazyglue/5536313
38 de 38 16/7/16 19:25