Mysql Procedural SQL - Puro
Mysql Procedural SQL - Puro
Mysql Procedural SQL - Puro
Seguridad: Los procedimientos ocultan el nombre de las tablas a usuarios que no tengan
los privilegios para manipular datos. Simplemente llaman los procedimientos sin conocer la
estructura de la base de datos.
Un procedimiento puede tener uno o mas parmetros o tambin no tener ninguno. Puede
carecer de atributos o puede poseer varios. Y como ves, el cuerpo del procedimiento es
un bloque de instrucciones definido.
Pero su utilidad se basa en la suposicin de que en el futuro los procedimientos podran ser
escritos en otros lenguajes como Php, Java, etc. Ya que aun los escribimos en SQL entonces
no es necesario ponerlo.
SQL SECURITY {DEFINER|INVOKER}: Establece el nivel de seguridad de invocacin de un
procedimiento. Si usas DEFINER el procedimiento sera ejecutado con los permisos del usuario
que lo cre, y si usas INVOKER ser ejecutado con los permisos del usuario que lo esta
invocando.
[NOT] DETERMINISTIC: Especifica si el procedimiento devolver siempre el mismo resultado
al ingresar los mismo parmetros. O si devolver distintos resultados al ingresar los mismo
resultados. Un ejemplo sera ingresar la suma 1+2, se sabe que siempre el resultado ser 3,
as que usamos DETERMINISTIC. Pero si el parmetro es un valor de un retiro de cuenta
bancaria, el resultado del saldo que queda ser diferente sin importar la cantidad retirada.
NO SQL|CONTAINS SQL|READS SQL DATA|MODIFIES SQL DATA: Estas caractersticas
determinan la estructura del procedimiento. NO SQL indica que el procedimiento no contiene
sentencias del lenguaje SQL. READS SQL DATA especifica que el procedimiento lee
informacin de la base de datos mas no escribe datos. MODIFIES SQL DATA indica que el
procedimiento escribe datos en la base de datos. CONTAINS SQL es el tipo por defecto de un
procedimiento e indica que el procedimiento contiene sentencias SQL
COMMENT cadena: Con este atributo podemos aadir una descripcin al procedimiento con
respecto a las instrucciones que ejecuta. Por ejemplo,Este procedimiento da de baja a todos
los clientes que hace 3 meses no compran a la compaa.
DELIMITER ;
La sentencia DELIMITER cambia el carcter de terminacin ';' por cualquier otro carcter,
en este caso elegimos '//'. Se hace con el fin de que MySQL no termine el procedimiento
al encontrar el primer punto y coma. Al final restablecemos el valor original del caracter de
escape.
Veamos el resultado:
END//
DELIMITER ;
Si el procedimiento que deseamos crear ya existe, entonces lo borraremos para darle paso a
la nueva definicin.
Un cursor es un objeto que apunta a las filas retornadas de una consulta. Esta caracterstica
permite manipular los datos de cada fila de forma individual. MySQL usa la palabra
reservada CURSOR para declarar estos espacios de lectura.
Declaracin
Apertura
Lectura
Cierre
1. DECLARACION
Al igual que una una variable, los cursores se declaran con la sentencia DECLARE.
Debemos declararlos despus de nuestras variables corrientes, de lo contrario MySQL,
generar un error. Veamos la sintaxis:
No significa que el objeto cursor_edad vaya a guardar los datos de la consulta a la cual esta
referenciando. Lo que hace es apuntar a la direccin de memoria del primer resultado de
dicha consulta. Si tienes conocimientos en C++ se te har mas fcil comprender esta
interpretacin.
2. APERTURA
En la fase de declaracin la consulta a la que hace referencia el cursor, aun no se ha ejecutado.
Para ejecutarla usaremos el comando OPEN. Sin esta apertura los resultados del cursor no
pueden ser ledos por MySQL, por lo tanto se producir un error.
Debes tener en cuenta que al abrir el cursor este sita un puntero a la primera fila arrojada
por la consulta.
OPEN nombre_cursor;
3. LECTURA
La lectura de los resultados de un cursor se hace con el comando FETCH. Este nos permite
acceder a la primer fila generada por la consulta. Si se vuelve a usar el cursor pasa a apuntar
a la segunda fila, luego a la tercer y as sucesivamente hasta que el cursor no tenga resultados
que referenciar.
Es importante tener variables declaradas para almacenar temporalmente los datos de las
columnas de cada fila, generadas por la consulta. Estas variables lgicamente deben tener el
mismo tipo de dato que el valor de la columna a almacenar, y luego relacionarlas con la
sentencia
INTO.
Por ejemplo, si quisiramos almacenar el id, nombre y apellido del primer empleado de la
tabla EMPLEADO, hacemos lo siguiente:
-- Declaracin de variables para el cursor
DECLARE ID INT;
DECLARE NOMBRE VARCHAR(100);
DECLARE APELLIDO VARCHAR(100);
Me imagino que intuyes que si queremos recorrer todas las filas de la consulta, necesitaremos
de alguna estructura repetitiva, no es cierto?, claro!, incluir el comando FETCH dentro
de un buclepermite leer todos los resultados de un cursor. Cuando el cursor llegue al final
de los resultados de la consulta, entonces el bucle termina. Pero terminar un bucle de este
tipo
necesita
una
condicin
de
parada
especial
en
MySQL.
Existen manejadores de errores en MySQL para esta tarea, aunque por el momento no los
hemos
estudiado
es
necesario
saber
lo
siguiente:
Cuando usamos FETCH en el cursor, pero ya no hay mas filas por retornar, MySQL arroja
un error llamado 02000 NO DATA FECH. As que lo que debemos hacer es crear
un manejador para indicar que cuando suceda ese error, el programa no termine, pero que
si termine el bucle. Veamos:
-- Declaracin de un manejador de error tipo NOT FOUND
Aqu indicamos que si ocurre un error tipoNOT FOUND, entonces asignemos a la variable
@hecho el valor de TRUE. Con esa variable podremos manejar la terminacin de nuestro
bucle mas adelante.
4. CIERRE
Una vez ledo todos los resultados del cursor, procedemos a cerrar y limpiar espacios de
memoria con CLOSE.
CLOSE nombre_cursor;
Procesos
Consultar las facturas que liquid el vendedor en las fechas estipuladas como entrada.
Salida
Una vez comprendido estos pasos, solo queda implementar el cdigo. Veamos:
DELIMITER //
BEGIN
-- Declaracin de variables
DECLARE ID_VENDEDOR INT;
DECLARE ACUMULADO_VENTAS INT;
DECLARE TEMPV INT DEFAULT 0;
DECLARE TEMPID INT DEFAULT 0;
-- Definicin de la consulta
DECLARE mejor_vendedor_cursor CURSOR FOR
SELECT V.IDVENDEDOR,SUM(DF.UNIDADES*DF.PRECIO)
FROM VENDEDOR AS V INNER JOIN FACTURA AS F
ON V.IDVENDEDOR = F.IDVENDEDOR AND (F.FECHA BETWEEN fecha_inicio AND fecha_final)
INNER JOIN DETALLEFACTURA AS DF
ON F.IDFACTURA = DF.IDFACTURA
GROUP BY V.IDVENDEDOR;
-- Abrimos el cursor
OPEN mejor_vendedor_cursor;
-- Cerramos el cursor
CLOSE mejor_vendedor_cursor;
END//
DELIMITER ;
MySQL no permite que los cursores lean los resultados de una consulta desde el ultimo
elemento hasta el primero. Usa ORDER BY para organizar tu mismo la informacin.
MySQL no permite que saltemos a una fila en particular para ahorrarnos tiempo, debemos
recorrer obligatoriamente uno a uno los resultados.
La nica diferencia entre la creacin de un procedimiento y una funcin es que la sintaxis de una
funcin contiene la palabra reservada RETURNS para indicar que tipo de dato se retornar.
DELIMITER //
-- Guardamos el valor de x
SET factorial = x ;
bucle: LOOP
-- Factorial parcial
SET factorial = factorial * x ;
-- Retornamos en el factorial
RETURN factorial;
END//
DELIMITER ;
No confundas RETURNS con RETURN. La primera es para indicar el tipo de dato de retorno
de la funcin y la segunda es para retornar el valor en el cuerpo de la funcin.
Creacin de la funcin:
DELIMITER //
CREATE FUNCTION EXT_PRIORIDAD (cliente_prioridad VARCHAR(5)) RETURNS VARCHAR(20)
BEGIN
CASE cliente_prioridad
Con ella podremos consultar de la siguiente forma a los clientes de la base de datos:
De esta manera hemos usado nuestra funcin en un contexto de consulta. Tambin podemos
usar funciones en las sentencias DELETE y UPDATE, siempre y cuando el valor retornado
sea acorde con las necesidades.
Si quisiramos aadir una descripcin a una funcin que calcula el promedio de huspedes diario con
respecto a una fecha llamada promedio_huespedes, hacemos lo siguiente:
Por ejemplo, para borrar una funcin que retorna en el ingreso neto con respecto a todos los tiquetes
areos comprados en una sucursal de una aerolnea, llamada ingreso_neto_sucursal:
CREATE [DEFINER={usuario|CURRENT_USER}]
TRIGGER nombre_del_trigger {BEFORE|AFTER} {UPDATE|INSERT|DELETE}
ON nombre_de_la_tabla
FOR EACH ROW
<bloque_de_instrucciones>
DEFINER={usuario|CURRENT_USER}
: Indica al gestor de bases de datos qu usuario tiene privilegios en su cuenta, para la
invocacin de los triggers cuando surjan los eventos DML. Por defecto este caracterstica
tiene el valor CURRENT_USER que hace referencia al usuario actual que esta creando el
Trigger.
nombre_del_trigger:
Indica el nombre de nuestro trigger. Existe una nomenclatura muy prctica para nombrar
UPDATE|INSERT|DELETE:
Aqu eliges que sentencia usars para que se ejecute el Trigger.
ON nombre_de_la_tabla:
En esta seccin estableces el nombre de la tabla asociada.
FOR EACH ROW: Establece que el Trigger se ejecute por cada fila en la tabla asociada.
NEW.idproducto.
Si usamos la sentencia UPDATE podremos referirnos a un valor OLD y NEW, ya que
modificaremos registros existentes por nuevos valores. En cambio si usamos INSERT solo
usaremos NEW, ya que su naturaleza es nicamente de insertar nuevos valores a las
columnas. Y si usamos DELETE usaremos OLD debido a que borraremos valores que
existen con anterioridad.
Este Trigger se ejecuta antes de haber insertado el registro, lo que nos da el poder de verificar
primero si el nuevo valor de la edad esta en el rango apropiado, si no es as entonces
asignaremosNULL a ese campo. Grandes los Triggers!
DELIMITER //
CREATE TRIGGER detalle_factura_AU_Trigger
AFTER UPDATE ON detalle_factura FOR EACH ROW
sobre
la
BEGIN
INSERT INTO log_updates
(idusuario, descripcion)
VALUES (user( ),
CONCAT('Se modific el registro ','(',
OLD.iddetalle,',', OLD.idfactura,',',OLD.idproducto,',',
OLD.precio,',', OLD.unidades,') por ',
'(', NEW.iddetalle,',', NEW.idfactura,',',NEW.idproducto,',',
NEW.precio,',', NEW.unidades,')'));
END//
DELIMITER ;
Con este registro de logs podremos saber si algn vendedor ocioso esta alterando las
facturas, lo que lgicamente sera atentar contra las finanzas de nuestro negocio. Cada
registro nos informa el usuario que modific la tabla DETALLE_FACTURA y muestra una
descripcin sobre los cambios en cada columna.
Con todos ellos mantendremos el total de ventas de cada cliente actualizado dependiendo del
evento realizado sobre un pedido.
Si insertamos un nuevo pedido generado por un cliente existente, entonces vamos
rpidamente a la tabla TOTAL_VENTAS y actualizamos el total comprado por ese cliente con
una sencilla suma acumulativa.
Ahora, si cambiamos el monto de un pedido, entonces vamos a TOTAL_VENTAS para descontar
el monto anterior y adicionar el nuevo monto.
Y si eliminamos un pedido de un cliente simplemente descontamos del total acumulado el
monto que con anterioridad habamos acumulado. Prctico cierto?
Tambin puedes ver los Triggers que hay en tu base de datos con:
SHOW TRIGGERS;
Recuerda que podemos adicionar la condicion IF EXISTS para indica que si el Trigger ya
existe, entonces que lo borre.