diff --git a/.gitignore b/.gitignore
index 9fc7696..5529635 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.pdf
006_challenge_encriptador/test/
otros/
+*.bak/
.idea/
out/
*.class
diff --git a/011_mysql/dml.md b/011_mysql/dml.md
index af6c514..951f907 100644
--- a/011_mysql/dml.md
+++ b/011_mysql/dml.md
@@ -1,3 +1,529 @@
# Data Manipulation Language
+Se repasa lo visto en el primer curso sobre
+[bases de datos](../010_spring_boot/base_de_datos.md)
+### Tipos de datos
+
+- MySQL data [types](https://dev.mysql.com/doc/refman/8.0/en/data-types.html)
+- MariaDB data [types](https://mariadb.com/kb/en/data-types/)
+
+## Diseño de la base de datos
+
+### 1. Análisis de requerimientos
+
+- Comprender las reglas del negocio (entrevistas, reuniones)
+- El diseño del modelo debe corresponder con la realidad
+
+### 2. Modelo Conceptual
+
+- Diagrama **E**ntidad **R**elación
+- Establecer la cardinalidad de las entidades (relaciones `1-1`, `1-N`,
+`N-N`)
+
+
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'linear'}}}%%
+flowchart LR
+V[Vendedor] ---- R{Realiza}
+C[Cliente] ---- I{Involucra}
+P[Productos] ---- Co{Contiene}
+Ve[Ventas] --- Po{Posee}
+IV["Items
+Vendidos"]
+R --- Ve
+I --- Ve
+Co --- IV
+IV --- Po
+```
+
+### 3. Establecer atributos de las entidades
+
+#### Vendedor
+
+| Vendedor | Cliente | Venta | Producto | Items Vendidos |
+| - | - | - | - | - |
+| ***Matrícula*** | ***DNI*** | ***Número*** | ***Código*** | *Número* |
+| Nombre | Nombre | Fecha | Sabor | *Código* |
+| Barrio | Dirección | *Matrícula* | Tamaño | Cantidad |
+| Comisión | Barrio | Impuesto | Envase | Precio |
+| Fecha Admisión | Ciudad | *DNI* | Precio Lista | |
+| Vacaciones | Estado | | Descripción | |
+| | CP | | | |
+| | Fecha nacimiento | | | |
+| | Edad | | | |
+| | Sexo | | | |
+| | Límite credito | | | |
+| | Volumen compra | | | |
+| | Primera compra | | | |
+
+### 4. Creación de diagrama entidad relación
+
+```mermaid
+erDiagram
+ Productos {
+ CODIGO varchar PK
+ DESCRIPCION varchar
+ SABOR varchar
+ TAMANO varchar
+ ENVASE varchar
+ PRECIO_LISTA float
+ }
+ Vendedor {
+ MATRICULA varchar PK
+ NOMBRE varchar
+ BARRIO varchar
+ COMISION float
+ FECHA_ADMISION date
+ VACACIONES bit
+ }
+ Clientes {
+ DNI varchar PK
+ NOMBRE varchar
+ BARRIO varchar
+ CIUDAD varchar
+ ESTADO varchar
+ FECHA_NACIMIENTO date
+ EDAD int
+ SEXO varchar
+ LIMITE_CREDITO int
+ VOLUMEN_COMPRA int
+ PRIMERA_COMPRA bit
+ }
+ Ventas {
+ NUMERO varchar PK
+ FECHA date
+ DNI varchar FK
+ MATRICULA varchar FK
+ IMPUESTO float
+ }
+ Items_Vendidos {
+ NUMERO varchar FK
+ CODIGO varchar FK
+ CANTIDAD bigint
+ PRECIO float
+ }
+ Vendedor ||--|{ Ventas : realiza
+ Clientes ||--|{ Ventas : involucra
+ Productos ||--|{ Items_Vendidos : contiene
+ Ventas ||--|{ Items_Vendidos : posee
+```
+
+### 5. Crear base de datos
+
+Se pueden utilizar herramientas para esto. Computer-Aide Software Engineering,
+Start UML, Astah, ERWin, etc.
+
+#### Creación
+
+```sql
+CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
+ [create_options] ...
+
+create_options: [DEFAULT] {
+ CHARACTER SET [=] charset_name
+ | COLLATE [=] collation_name
+ | ENCRYPTION [=] {'Y' | 'N'}
+}
+```
+
+#### Eliminación
+
+```sql
+DROP {DATABASE | SCHEMA} [IF EXISTS] db_name
+```
+
+#### Creación de tablas
+
+```sql
+CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+ (create_definitions, ...)
+ [table_options]
+ [partition_options]
+
+CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+ (create_definitions, ...)
+ [table_options]
+ [partition_options]
+ [IGNORE | REPLACE]
+ [AS] query_expression
+
+CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+ (create_definitions, ...)
+ { LIKE old_tbl_name | (LIKE old_tbl_name) }
+```
+
+### Creación tablas
+
+```sql
+USE ventas_jugos;
+
+CREATE TABLE tb_vendedor(
+ MATRICULA VARCHAR(5) NOT NULL,
+ NOMBRE VARCHAR(100) NULL,
+ BARRIO VARCHAR(50) NULL,
+ COMISION FLOAT NULL,
+ FECHA_ADMISION DATE NULL,
+ DE_VACACIONES BIT(1) NULL,
+ PRIMARY KEY(MATRICULA)
+);
+
+CREATE TABLE tb_producto(
+ CODIGO VARCHAR(10) NOT NULL,
+ DESCRIPCION VARCHAR(100) NULL,
+ SABOR VARCHAR(50) NULL,
+ TAMANO VARCHAR(50) NULL,
+ ENVASE VARCHAR(50) NULL,
+ PRECIO_LISTA BIT(1) NULL,
+ PRIMARY KEY(CODIGO)
+);
+
+...
+```
+
+```sql
+CREATE TABLE tb_venta(
+ NUMERO VARCHAR(5) NOT NULL,
+ FECHA DATE NULL,
+ DNI VARCHAR(11) NOT NULL,
+ MATRICULA VARCHAR(5) NOT NULL,
+ IMPUESTO FLOAT,
+ PRIMAREY KEY(NUMERO)
+);
+
+/* tb_cliente.DNI como llave foranea en tb_venta */
+ALTER TABLE tb_venta ADD CONSTRAIN FK_CLIENTE
+FOREIGN KEY (DNI) REFERENCES tb_cliente(DNI);
+
+/* tb_vendedor.MATRICULA como llave foranea tb_venta */
+ALTER TABLE tb_venta ADD CONSTRAIN FK_VENDEDOR
+FOREIGN KEY (MATRICULA) REFERENCES tb_vendedor(MATRICULA);
+
+CREATE TABLE tb_items_facturas
+(NUMERO VARCHAR(5) NOT NULL,
+CODIGO VARCHAR(10) NOT NULL,
+CANTIDAD INT,
+PRECIO FLOAT,
+PRIMARY KEY (NUMERO, CODIGO));
+
+/* Renombrando tb_venta a tb_factura */
+ALTER TABLE tb_venta RENAME tb_factura;
+
+/* tb_factura.MATRICULA como llave foranea tb_factura */
+ALTER TABLE tb_items_facturas ADD CONSTRAINT FK_PRODUCTO
+FOREIGN KEY (NUMERO) REFERENCES tb_facturas(NUMERO);
+
+/* tb_producto.CODIGO como llave foranea tb_items_facturas */
+ALTER TABLE tb_items_facturas ADD CONSTRAINT FK_PRODUCTO
+FOREIGN KEY (NUMERO) REFERENCES tb_producto(CODIGO);
+```
+
+## Insert
+
+```sql
+INSET [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
+ [INTO] tbl_name
+ [PARTITION (partition_name [, partition_name] ...)]
+ [(col_name [, col_name] ...)]
+ { {VALUES | VALUE} (value_list) [, (value_list)] ...
+ |
+ VALUES row_constructor_list
+ }
+ [AS row_alias[(col_alias [, col_alias] ...)]]
+
+
+INSET [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
+ [INTO] tbl_name
+ [PARTITION (partition_name [, partition_name] ...)]
+ [AS row_alias[(col_alias [, col_alias] ...)]]
+ SET assignment_list
+ [ON DUPLICATE KEY UPDATE assignment_list]
+```
+
+```sql
+INSERT INTO tb_producto (
+ CODIGO, DESCRIPCION, SABOR, TAMANO, ENVASE, PRECIO_LISTA
+) VALUES
+ ('1040107', 'Light', 'Sandía', '350 ml', 'Lata', 4.56),
+ ('1040108', 'Light', 'Guanába', '350 ml', 'Lata', 4.56),
+ ('1040109', 'Light', 'Asaí', '350 ml', 'Lata', 3.50);
+```
+
+```sql
+INSERT INTO TABLA_DE_CLIENTES
+ DNI, NOMBRE, DIRECCION, BARRIO, CIUDAD, ESTADO, CP, FECHA_NACIMIENTO,
+ EDAD, SEXO, LIMITE_CREDITO, VOLUMEN_COMPRA, PRIMERA_COMPRA
+VALUES
+ ('9283760794', 'Edson Calisaya', 'Sta Úrsula Xitla', 'Barrio del Niño Jebús',
+ 'Ciudad de México', 'EM', '22002002', '1995-01-07', 25, 'M', 150000, 250000, 1),
+ ('7771579779', 'Marcelo Perez', 'F.C. de Cuernavaca 13', 'Carola',
+ 'Ciudad de México', 'EM', '88202912', '1992-01-25', 29, 'M', 120000, 200000, 1),
+ ('5576228758', 'Patricia Olivera', 'Pachuca 75', 'Condesa', 'Ciudad de México',
+ 'EM', '88192029', '1995-01-14', 25, 'F', 70000, 160000, 1);
+```
+
+### Insertar datos desde otro schema
+
+```sql
+USER jugos_ventas;
+
+INSERT INTO tb_producto
+SELECT CODIGO_DEL_PRODUCTO AS CODIGO, NOMBRE_DEL_PRODUCTO AS DESCRIPCION,
+SABOR, TAMANO, ENVASE, PRECIO_DE_LISTA AS PRECIO_LISTA
+FROM jugos.vetnas.tabla_de_productos
+WHERE CODIGO_DEL_PRODUCTO NOT IN (SELECT CODIGO FROM tb_producto);
+
+INSERT INTO tb_cliente
+SELECT DNI, NOMBRE, DIRECCION_1 AS DIRECCION,
+BARRIO, CIUDAD, ESTADO, CP, FECHA_DE_NACIMIENTO
+AS FECHA_NACIMIENTO, EDAD, SEXO, LIMITE_DE_CREDITO
+AS LIMITE_CREDITO, VOLUMEN_DE_COMPRA AS VOLUMEN_COMPRA,
+PRIMERA_COMPRA FROM jugos_ventas.tabla_de_clientes
+WHERE DNI NOT IN (SELECT DNI FROM tb_cliente);
+```
+
+### Table Data Import Wizard
+
+
+
+## Update
+
+```sql
+UPDATE [LOW_PRIORITY] [IGNORE] table_reference
+ SET assignment_list
+ [WHERE where_condition]
+ [ORDER BY ...]
+ [LIMIT row_count]
+
+value: {expr | DEFAULT}
+
+assignment:
+ col_name = value
+
+assignment_list:
+ assignment [, assignment] ...
+```
+
+### Actualizar un campo
+
+```sql
+UPDATE tb_producto SET PRECIO_LISTA = 5
+WHERE CODIGO = '1000889';
+```
+
+### Actualizar varios campos
+
+```sql
+UPDATE tb_producto SET
+ DESCRIPCION = 'Sabor de la Montaña',
+ TAMANO = '1 Litro',
+ ENVASE = 'Botella PET'
+WHERE CODIGO = '1000889';
+
+UPDATE tb_cliente SET
+ DIRECCION = 'Ruben Ramirez 16',
+ BARRIO = 'Antonio',
+ CIUDAD = 'Guadalajara',
+ ESTADO = 'Jalisco',
+ CP = '44700000'
+WHERE DNI = '5840119709';
+```
+
+### Actualizar todos los registros de una columna
+
+```sql
+UPDATE tb_cliente SET VOLUMEN_COMPRA = VOLUMEN_COMPRA/10;
+```
+
+### Update con datos de otro schema
+
+```sql
+/* Se usa substring ya que el format de matrícula en la tabla B
+ es '00???' */
+UPDATE tb_vendedor A
+ SELECT * FROM tb_vendedor A
+ INNER JOIN
+ jugos_ventas.tabla_de_vendedores B
+ ON A.MATRICULA = SUBSTRING(B.MATRICULA, 3, 3)
+SET A.DE_VACACIONES = B.VACACIONES;
+```
+
+***Aumentar en 30% el volumen de compra de los clientes***
+***cuyos barrios tengan vendedores con domicilio allí***
+
+```sql
+UPDATE tb_clientes A
+INNER JOIN
+ tb_vendedor B
+ON A.BARRIO = B.BARRIO
+SET A.VOLUMEN_COMPRA = A.VOLUMEN_COMPRA * 1.3;
+```
+
+## DELETE
+
+```sql
+DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [[AS] tbl_alias]
+ [PARTITION (partition_name [, partition_name] ...)]
+ [ORDER BY ...]
+ [LIMIT row_count]
+```
+
+```sql
+DELETE FROM tb_producto WHERE CODIGO = '1001000';
+
+DELETE FROM tb_producto WHERE TAMANO = '1 Litro';
+```
+
+```sql
+DELTE FROM tb_producto
+WHERE CODIGO NOT IN (
+ SELECT CODIGO_DEL_PRODUCTO
+ FROM jugos_.tabla_de_productos
+);
+```
+
+***Eliminar las facturas cuyos clientes tengan menos de 18 años***
+
+```sql
+DELETE A FROM tb_facturas A
+INNER JOIN tb_vendedor B
+ON A.DNI = B.DNI
+WHERE B.EDAD < 18;
+```
+
+## COMMIT y ROLLBACK
+
+Confirmar o revertir una transacción
+
+```sql
+START TRANSACTION
+
+-- TRANSACCIÓN
+
+ROLLBACK
+-- o
+COMMIT
+```
+
+## AUTO_INCREMENT
+
+```sql
+CREATE TABLE tb_identificacion(
+ ID INT AUTO_INCREMENT NOT NULL,
+ DESCRIPCION VARCHAR(50) NULL,
+ PRIMARY KEY(ID)
+);
+```
+
+## Campo con valor DEFAULT
+
+```sql
+CREATE TABLE tb_default(
+ ID INT AUTO_INCREMENT,
+ DESCRIPCION VARCHAR(50) NOT NULL,
+ DIRECCION VARCHAR(100) NULL,
+ CIUDAD VARCHAR(50) DEFAULT 'Gaza',
+ CREACION TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
+ PRIMARY KEY(ID)
+);
+```
+
+## TRIGGERS
+
+```sql
+CREATE
+ [ DEFINER = user ]
+ TRIGGER trigger_name
+ trigger_time trigger_event
+ ON tbl_name FOR EACH ROW
+ [trigger_order]
+ trigger_boddy
+
+trigger_time: { BEFORE | AFTER }
+
+trigger_event: { INSERT | UPDATE | DELETE }
+
+trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
+```
+
+```sql
+CREATE TABLE tb_facturacion(
+ FECHA DATE NULL,
+ VENTA FLOAT
+);
+
+DELIMITER //
+
+CREATE TRIGGER TG_FACTURACION_INSERT
+AFTER INSERT ON tb_items_facturas
+FOR EACH ROW BEGIN
+
+ DELETE FROM tb_facturacion;
+ INSERT INTO tb_facturacion
+ SELECT A.FECHA, SUM(B. CANTIDAD * B.PRECIO) AS VENTA
+ FROM tb_factura A
+ INNER JOIN
+ tb_items_facturas B
+ ON A.NUMERO = B.NUMERO
+ GROUP BY A.FECHA;
+
+END //
+```
+
+***Crear trigger que actualize la edad de los clientes cada vez que se
+inserte un nuevo cliente***
+
+```sql
+DELIMITER //
+CREATE TRIGGER TG_EDAD_CLIENTES_INSERT
+BEFORE INSERT ON tb_clientes
+FOR EACH ROW BEGIN
+ SET NEW.EDAD = timestampdiff(YEAR, NEW.FECHA_NACIMIENTO, NOW());
+END//
+```
+
+### Triggers para DELETE y UPDATE
+
+```sql
+DELIMITER //
+
+CREATE TRIGGER TG_FACTURACION_DELETE
+AFTER DELETE ON tb_items_facturas
+FOR EACH ROW BEGIN
+
+ DELETE FROM tb_facturacion;
+ INSERT INTO tb_facturacion
+ SELECT A.FECHA, SUM(B. CANTIDAD * B.PRECIO) AS VENTA
+ FROM tb_factura A
+ INNER JOIN
+ tb_items_facturas B
+ ON A.NUMERO = B.NUMERO
+ GROUP BY A.FECHA;
+
+END //
+
+DELIMITER //
+
+CREATE TRIGGER TG_FACTURACION_UPDATE
+AFTER UPDATE ON tb_items_facturas
+FOR EACH ROW BEGIN
+
+ DELETE FROM tb_facturacion;
+ INSERT INTO tb_facturacion
+ SELECT A.FECHA, SUM(B. CANTIDAD * B.PRECIO) AS VENTA
+ FROM tb_factura A
+ INNER JOIN
+ tb_items_facturas B
+ ON A.NUMERO = B.NUMERO
+ GROUP BY A.FECHA;
+
+END //
+```
+
+## Manipulación de datos
+
+- Stored Procedures
+- Herramientas ETL (extract transform load) Pentaho, Power Center, SAP Data
+Services, SQL Server Information Services, ...
+- Lenguajes Python, Java, PHP, ...
diff --git a/011_mysql/procedures.md b/011_mysql/procedures.md
new file mode 100644
index 0000000..cf07ded
--- /dev/null
+++ b/011_mysql/procedures.md
@@ -0,0 +1,1493 @@
+# Stored Procedures
+
+- [MySQL](https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html)
+- [MariaDB](https://mariadb.com/kb/en/stored-procedures/)
+- [MariaDB Create Procedure](https://mariadb.com/kb/en/create-procedure/)
+- MariaDB [W3Schools](https://www.w3schools.blog/procedure-mariadb) procedure
+
+```sql
+CREATE
+ [DEFINER = user]
+ PROCEDURE sp_name ([proc_parameter[, ...]])
+ [characteristic ...] routine_body
+
+CREATE
+ [DEFINER = user]
+ FUNCTION sp_name ([func_parameter[, ...]])
+ RETURN type
+ [characteristic ...] routine_body
+
+proc_parameter:
+ [ IN | OUT | INOUT ] param_name type
+
+func_parameter:
+ param_name type
+
+type:
+ Any valid MySQL data type
+
+characteristic: {
+ COMMENT 'string'
+ | LANGUAGE SQL
+ | [NOT] DETERMINISTIC
+ | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
+ | SQL SECURITY { DEFINER | INVOKER }
+}
+
+routine_body:
+ Valid SQL routine statement
+```
+
+```sql
+CREATE PROCEDURE
+ (parámetros)
+BEGIN
+ DECLARE ;
+ ...
+ ;
+ ...
+END;
+```
+
+### Ejemplo STORE PROCEDURE
+
+```sql
+USE jugos_ventas;
+
+DROP PROCEDURE IF EXISTS print_procedure;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE print_procedure ()
+BEGIN
+ SELECT CONCAT("Procedimiento,", " de ", "ejemplo");
+END$$
+DELIMITER ;
+```
+
+### Stored Procedures Sintaxis
+
+- Puede tener **letras**, **números** y los caracteres `$` y `_`
+- Tamaño máximo: 64 caracteres
+- Nombre único
+- Case Sensitive
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS hola_pianola;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE hola_pianola ()
+BEGIN
+ SELECT "Hola pianola!";
+END$$
+
+DELIMITER ;
+```
+
+### CALL Procedure
+
+```sql
+CALL print_procedure;
++-----------------------------+
+| CONCAT("hola,", " pianola") |
++-----------------------------+
+| hola, pianola |
++-----------------------------+
+```
+
+
+```sql
+CALL hola_pianola;
+
++---------------+
+| Hola pianola! |
++---------------+
+| Hola pianola! |
++---------------+
+```
+
+#### Otros ejemplos
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS calcular;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE calcular ()
+BEGIN
+ /* Este procedure tiene un comentario */
+ -- Suma 6+3 y lo mútliplica por 5
+ # Y muestra el "RESULTADO"
+ SELECT (6+3)*5 AS RESULTADO;
+END$$
+
+DELIMITER ;
+
+CALL calcular;
+
++-----------+
+| RESULTADO |
++-----------+
+| 45 |
++-----------+
+```
+
+## Eliminar procedure
+
+```sql
+DROPR PROCEDURE jugos_ventas.hola_pianola;
+```
+
+### Ejemplo de un procedimiento almacenado mas extenso
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS proc_extenso;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE proc_extenso ()
+BEGIN
+ INSERT INTO tabla_de_productos (
+ CODIGO_DEL_PRODUCTO,
+ NOMBRE_DEL_PRODUCTO,
+ SABOR,
+ TAMANO,
+ ENVASE,
+ PRECIO_DE_LISTA
+ ) VALUES
+ ('1001001','Sabor Alpino','Mango','700 ml','Botella',7.50),
+ ('1001000','Sabor Alpino','Melón','700 ml','Botella',7.50),
+ ('1001002','Sabor Alpino','Guanábana','700 ml','Botella',7.50),
+ ('1001003','Sabor Alpino','Mandarina','700 ml','Botella',7.50),
+ ('1001004','Sabor Alpino','Banana','700 ml','Botella',7.50),
+ ('1001005','Sabor Alpino','Asaí','700 ml','Botella',7.50),
+ ('1001006','Sabor Alpino','Mango','1 Litro','Botella',7.50),
+ ('1001007','Sabor Alpino','Melón','1 Litro','Botella',7.50),
+ ('1001008','Sabor Alpino','Guanábana','1 Litro','Botella',7.50),
+ ('1001009','Sabor Alpino','Mandarina','1 Litro','Botella',7.50),
+ ('1001010','Sabor Alpino','Banana','1 Litro','Botella',7.50),
+ ('1001011','Sabor Alpino','Asaí','1 Litro','Botella',7.50);
+
+ SELECT * FROM tabla_de_productos
+ WHERE NOMBRE_DEL_PRODUCTO LIKE 'Sabor Alp%';
+
+ UPDATE tabla_de_productos
+ SET PRECIO_DE_LISTA = 5
+ WHERE NOMBRE_DEL_PRODUCTO LIKE 'Sabor Alp%';
+
+ SELECT * FROM tabla_de_productos
+ WHERE NOMBRE_DEL_PRODUCTO LIKE 'Sabor Alp%';
+
+ DELETE FROM tabla_de_productos
+ WHERE NOMBRE_DEL_PRODUCTO LIKE 'Sabor Alp%';
+
+ SELECT * FROM tabla_de_productos
+ WHERE NOMBRE_DEL_PRODUCTO LIKE 'Sabor Alp%';
+END$$
+
+DELIMITER ;
+```
+
+```sql
+CALL proc_extenso;
+```
+
+```txt
++---------------------+---------------------+---------+------------+---------+-----------------+
+| CODIGO_DEL_PRODUCTO | NOMBRE_DEL_PRODUCTO | TAMANO | SABOR | ENVASE | PRECIO_DE_LISTA |
++---------------------+---------------------+---------+------------+---------+-----------------+
+| 1001000 | Sabor Alpino | 700 ml | Melón | Botella | 7.5 |
+| 1001001 | Sabor Alpino | 700 ml | Mango | Botella | 7.5 |
+| 1001002 | Sabor Alpino | 700 ml | Guanábana | Botella | 7.5 |
+| 1001003 | Sabor Alpino | 700 ml | Mandarina | Botella | 7.5 |
+| 1001004 | Sabor Alpino | 700 ml | Banana | Botella | 7.5 |
+| 1001005 | Sabor Alpino | 700 ml | Asaí | Botella | 7.5 |
+| 1001006 | Sabor Alpino | 1 Litro | Mango | Botella | 7.5 |
+| 1001007 | Sabor Alpino | 1 Litro | Melón | Botella | 7.5 |
+| 1001008 | Sabor Alpino | 1 Litro | Guanábana | Botella | 7.5 |
+| 1001009 | Sabor Alpino | 1 Litro | Mandarina | Botella | 7.5 |
+| 1001010 | Sabor Alpino | 1 Litro | Banana | Botella | 7.5 |
+| 1001011 | Sabor Alpino | 1 Litro | Asaí | Botella | 7.5 |
++---------------------+---------------------+---------+------------+---------+-----------------+
+12 rows in set (0.008 sec)
+
++---------------------+---------------------+---------+------------+---------+-----------------+
+| CODIGO_DEL_PRODUCTO | NOMBRE_DEL_PRODUCTO | TAMANO | SABOR | ENVASE | PRECIO_DE_LISTA |
++---------------------+---------------------+---------+------------+---------+-----------------+
+| 1001000 | Sabor Alpino | 700 ml | Melón | Botella | 5 |
+| 1001001 | Sabor Alpino | 700 ml | Mango | Botella | 5 |
+| 1001002 | Sabor Alpino | 700 ml | Guanábana | Botella | 5 |
+| 1001003 | Sabor Alpino | 700 ml | Mandarina | Botella | 5 |
+| 1001004 | Sabor Alpino | 700 ml | Banana | Botella | 5 |
+| 1001005 | Sabor Alpino | 700 ml | Asaí | Botella | 5 |
+| 1001006 | Sabor Alpino | 1 Litro | Mango | Botella | 5 |
+| 1001007 | Sabor Alpino | 1 Litro | Melón | Botella | 5 |
+| 1001008 | Sabor Alpino | 1 Litro | Guanábana | Botella | 5 |
+| 1001009 | Sabor Alpino | 1 Litro | Mandarina | Botella | 5 |
+| 1001010 | Sabor Alpino | 1 Litro | Banana | Botella | 5 |
+| 1001011 | Sabor Alpino | 1 Litro | Asaí | Botella | 5 |
++---------------------+---------------------+---------+------------+---------+-----------------+
+12 rows in set (0.011 sec)
+
+Empty set (0.016 sec)
+
+Query OK, 36 rows affected (0.016 sec)
+```
+
+## Variables en procedure
+
+```sql
+DECLARE DEFAULT ;
+```
+
+- **Datatype** es olbligatorio
+- **DEFAULT** es opcional, si no se establece, default es **NULL**
+- Solo **letras**, **números**, `$` y `_`
+- Se pueden declarar varias, con la restricción que sean del mismo tipo
+- Tamaño máximo de 255 caracteres
+- Nombre único, **case sesitive**
+- La declaración termina con `;`
+
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS mostrar_vars;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE mostrar_vars ()
+BEGIN
+ DECLARE texto CHAR(20) DEFAULT 'Hola';
+ DECLARE numero INTEGER DEFAULT 789;
+ DECLARE decimales DECIMAL(5,3) DEFAULT 45.678;
+ DECLARE fecha DATE DEFAULT '2023-10-10';
+ DECLARE tiempo TIMESTAMP DEFAULT CURRENT_TIMESTAMP();
+ SELECT texto, numero, decimales, fecha, tiempo;
+ /* Modificando valor */
+ SET numero = 1;
+ SELECT numero AS numero_modificado;
+END$$
+
+DELIMITER ;
+
+CALL mostrar_vars;
+```
+
+```txt
++-------+--------+-----------+------------+---------------------+
+| texto | numero | decimales | fecha | tiempo |
++-------+--------+-----------+------------+---------------------+
+| Hola | 789 | 45.678 | 2023-10-10 | 2023-10-21 13:11:34 |
++-------+--------+-----------+------------+---------------------+
+```
+
+***Crear un Stored Procedure que actualice la edad de los clientes.***
+
+```sql
+/* Cálculo de edad */
+TIMESTAMPDIFF(YEAR, FECHA_DE_NACIMIENTO, CURDATE()) as EDAD
+```
+
+```sql
+DELIMITER $$
+CREATE PROCEDURE `calcula_edad`()
+BEGIN
+ UPDATE tabla_de_clientes
+ SET EDAD = TIMESTAMPDIFF(YEAR, FECHA_DE_NACIMIENTO, CURDATE());
+END $$
+```
+
+## Procedure params
+
+### Ejemplo de procedimiento con variables
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS insert_prod;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE insert_prod (
+ vcodigo VARCHAR(20),
+ vnombre VARCHAR(20),
+ vsabor VARCHAR(20),
+ vtamano VARCHAR(20),
+ venvase VARCHAR(20),
+ vprecio DECIMAL(4,2)
+)
+BEGIN
+ /*
+ DECLARE vcodigo VARCHAR(20) DEFAULT '30030001';
+ DECLARE vnombre VARCHAR(20) DEFAULT 'Sabor Intenso';
+ DECLARE vsabor VARCHAR(20) DEFAULT 'Tutti Frutti';
+ DECLARE vtamano VARCHAR(20) DEFAULT '700 ml';
+ DECLARE venvase VARCHAR(20) DEFAULT 'Botella PET';
+ DECLARE vprecio DECIMAL(4,2) DEFAULT '7.25';
+ */
+ INSERT INTO tabla_de_productos (
+ CODIGO_DEL_PRODUCTO, NOMBRE_DEL_PRODUCTO, SABOR,
+ TAMANO, ENVASE, PRECIO_DE_LISTA
+ ) VALUES
+ (vcodigo, vnombre, vtamano, vsabor, venvase, vprecio);
+END$$
+
+DELIMITER ;
+```
+
+```sql
+CALL insert_prod(
+ '1000800', 'Sabor del Mar', '700 ml',
+ 'Naranja', 'Botella de Vidrio', 2.25
+);
+```
+
+***Crear un Stored Procedure para reajustar la comisión de los vendedores.***
+***Usando como parámetro el valor (Ej. 0,90).***
+
+```sql
+DELIMITER $$
+CREATE PROCEDURE `reajuste_comision`(vporcentaje FLOAT)
+BEGIN
+ UPDATE tabla_de_vendedores
+ SET PORCENTAJE_COMISION = PORCENTAJE_COMISION * (1 + vporcentaje);
+END $$
+```
+
+#### Ejemplo de procedimiento que establece una variable `a`
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS ejm_params;
+
+DELIMITER //
+USE jugos_ventas//
+
+CREATE PROCEDURE ejm_params (OUT param1 INTEGER)
+BEGIN
+ DECLARE total INTEGER DEFAULT 16;
+ SET param1 = total;
+END; //
+
+DELIMITER ;
+
+CALL ejm_params(@a);
+
+SELECT @a;
+```
+
+## Manejo de errores en procedures
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS insert_prod;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE insert_prod (
+ vcodigo VARCHAR(20),
+ vnombre VARCHAR(20),
+ vsabor VARCHAR(20),
+ vtamano VARCHAR(20),
+ venvase VARCHAR(20),
+ vprecio DECIMAL(4,2)
+)
+BEGIN
+ DECLARE mensaje VARCHAR(40);
+ DECLARE EXIT HANDLER FOR 1062
+ BEGIN
+ SET mensaje = 'PK duplicada!';
+ SELECT mensaje AS ERROR_PK;
+ END;
+ INSERT INTO tabla_de_productos (
+ CODIGO_DEL_PRODUCTO, NOMBRE_DEL_PRODUCTO, SABOR,
+ TAMANO, ENVASE, PRECIO_DE_LISTA
+ ) VALUES
+ (vcodigo, vnombre, vtamano, vsabor, venvase, vprecio);
+ SET mensaje = "Producto insertado con exito";
+ SELECT mensaje AS INFORMACION;
+END$$
+
+DELIMITER ;
+```
+
+```sql
+CALL insert_prod(
+ '1000800', 'Sabor del Mar', '700 ml',
+ 'Naranja', 'Botella de Vidrio', 2.25
+);
+
++---------------+
+| ERROR_PK |
++---------------+
+| PK duplicada! |
++---------------+
+
+CALL insert_prod(
+ '1000801', 'Sabor del Mar', '900 ml',
+ 'Naranja', 'Botella de Vidrio', 2.45
+);
+
++------------------------------+
+| INFORMACION |
++------------------------------+
+| Producto insertado con exito |
++------------------------------+
+```
+
+### Ejemplo rutine que muestra el sabor del produco insertado
+
+```sql
+USE jugos_ventas;
+DROP PROCEDURE IF EXISTS mostrar_sabor;
+
+DELIMITER $$
+USE jugos_ventas$$
+CREATE PROCEDURE mostrar_sabor (
+ vcodigo VARCHAR(20) COLLATE 'utf8mb4_uca1400_as_ci'
+)
+BEGIN
+ DECLARE vsabor VARCHAR(20);
+ SELECT SABOR INTO vsabor FROM tabla_de_productos
+ WHERE CODIGO_DEL_PRODUCTO = `vcodigo`;
+ SELECT vsabor AS SABOR;
+END$$
+
+DELIMITER ;
+```
+
+```sql
+CALL mostrar_sabor('1000800');
+
++---------+
+| SABOR |
++---------+
+| Naranja |
++---------+
+```
+
+***Crear una variable `N_FACTURAS` y asignar el número de facturas del día***
+***`01/01/2017` y mostrar el valor de la variable.***
+
+```sql
+DELIMITER $$
+CREATE PROCEDURE `cantidad_facturas`()
+BEGIN
+ DECLARE N_FACTURAS INTEGER;
+ SELECT COUNT(*) INTO N_FACTURAS FROM facturas WHERE
+ FECHA_VENTA = '2017-01-01';
+ SELECT N_FACTURAS;
+END $$
+```
+
+## IF THEN ELSE
+
+```sql
+IF THEN
+ ;
+ELSE
+ ;
+END IF
+```
+
+```sql
+DROP PROCEDURE IF EXISTS edad_clientes;
+DELIMITER $$
+CREATE PROCEDURE edad_clientes(
+ vdni VARCHAR(20) COLLATE 'utf8mb4_uca1400_as_ci'
+)
+BEGIN
+ DECLARE vresultado VARCHAR(50);
+ DECLARE vfecha DATE;
+ SELECT fecha_de_nacimiento INTO vfecha
+ FROM tabla_de_clientes WHERE dni=vdni;
+ IF
+ vfecha < '19950101'
+ THEN
+ SET vresultado = 'Cliente Antiguo';
+ ELSE
+ SET vresultado = 'Cliente Normal';
+ END IF;
+ SELECT vresultado AS TIPO_CLIENTE;
+END $$
+
+DELIMITER ;
+```
+
+```
+CALL edad_clientes('1471156710');
++-----------------+
+| TIPO_CLIENTE |
++-----------------+
+| Cliente Antiguo |
++-----------------+
+
+CALL edad_clientes('3623344710');
++----------------+
+| TIPO_CLIENTE |
++----------------+
+| Cliente Normal |
++----------------+
+```
+
+***Crear un Stored Procedure que, según una fecha dada, calcule el número***
+***de facturas. Si el resultado son más de 70 facturas mostar el mensaje:***
+***'Muchas facturas'. En otro caso, el mensaje será 'Pocas facturas'.***
+***En ambos casos se debe mostrar el número de facturas***
+
+```sql
+DROP PROCEDURE IF EXISTS cant_facturas;
+DELIMITER $$
+CREATE PROCEDURE cant_facturas(vfecha DATE)
+BEGIN
+ DECLARE vresultado VARCHAR(50);
+ DECLARE vfacturas INTEGER;
+ SELECT COUNT(matricula) INTO vfacturas
+ FROM facturas WHERE fecha_venta=vfecha;
+ IF
+ vfacturas > 70
+ THEN
+ SET vresultado = CONCAT('Muchas Facturas: ', vfacturas);
+ # SET vresultado = 'Muchas Facturas: ';
+ ELSE
+ SET vresultado = CONTACT('Pocas Facturas: ', vfacturas);
+ # SET vresultado = 'Pocas Facturas: ';
+ END IF;
+ SELECT vresultado AS FACTURAS_EN_FECHA;
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL cant_facturas('2018-01-01');
++---------------------+
+| FACTURAS_EN_FECHA |
++---------------------+
+| Muchas Facturas: 87 |
++---------------------+
+```
+
+### ELSE IF
+
+```sql
+IF THEN
+ ;
+ELSEIF
+ ;
+ (...)
+ELSEIF
+ ;
+ELSE
+ ;
+END IF;
+```
+
+```sql
+/*
+ precio >= 12 producto costoso
+ precio >= 12 < 12 producto asequible
+ precio < 7 producto barato
+*/
+DROP PROCEDURE IF EXISTS clasific_precio;
+DELIMITER $$
+CREATE PROCEDURE clasific_precio(
+ vcodigo VARCHAR(20)
+ COLLATE 'utf8mb4_uca1400_as_ci'
+)
+BEGIN
+ DECLARE vresultado VARCHAR(40);
+ DECLARE vprecio FLOAT;
+
+ SELECT precio_de_lista INTO vprecio
+ FROM tabla_de_productos
+ WHERE codigo_del_producto = vcodigo;
+
+ IF vprecio >= 12 THEN
+ SET vresultado = CONCAT(
+ 'Producto Costoso: $', vprecio, '.-'
+ );
+ ELSEIF vprecio >= 7 AND vprecio < 12 THEN
+ SET vresultado = CONCAT(
+ 'Producto Asequible: $', vprecio, '.-'
+ );
+ ELSE
+ SET vresultado = CONCAT(
+ 'Producto Barato: $', vprecio, '.-'
+ );
+ END IF;
+ SELECT vresultado AS "Clasificación por precio";
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL clasific_precio('1000800');
++--------------------------+
+| Clasificación por precio |
++--------------------------+
+| Producto Barato: $2.25.- |
++--------------------------+
+
+CALL clasific_precio('783663');
++-----------------------------+
+| Clasificación por precio |
++-----------------------------+
+| Producto Asequible: $7.71.- |
++-----------------------------+
+
+CALL clasific_precio('1004327');
++----------------------------+
+| Clasificación por precio |
++----------------------------+
+| Producto Costoso: $19.51.- |
++----------------------------+
+```
+
+***Crear un Stored Procedure `comparacion_ventas` que compare las ventas***
+***en entre 2 fechas distintas. Si el porcentaje de variación es mayor al***
+***10% debe retornar 'Verde'. Si está entre -10% y 10% debe restornar***
+***'Amarillo'. Si es menor al -10% debe retornar 'Rojo'***
+
+```sql
+/*
+ total > 10% Verde
+ precio <= 10% y >= -10% Amarillo
+ precio < -10% Rojo
+*/
+DROP PROCEDURE IF EXISTS comparacion_ventas;
+DELIMITER $$
+CREATE PROCEDURE comparacion_ventas(
+ vfecha1 DATE,
+ vfecha2 DATE
+)
+BEGIN
+ DECLARE vresultado VARCHAR(50);
+ DECLARE vtotal1 INTEGER;
+ DECLARE vtotal2 INTEGER;
+ DECLARE vtotal FLOAT;
+
+ SELECT SUM(B.CANTIDAD * B.PRECIO) INTO vtotal1
+ FROM facturas A INNER JOIN items_facturas B
+ ON A.NUMERO = B.NUMERO
+ WHERE A.FECHA_VENTA = vfecha1;
+
+ SELECT SUM(B.CANTIDAD * B.PRECIO) INTO vtotal2
+ FROM facturas A INNER JOIN items_facturas B
+ ON A.NUMERO = B.NUMERO
+ WHERE A.FECHA_VENTA = vfecha2;
+
+ SELECT ROUND(((vtotal2*100)/vtotal1)-100, 2) INTO vtotal;
+
+ IF vtotal > 10 THEN
+ SET vresultado = CONCAT('Verde: ', vtotal, '%');
+ ELSEIF vtotal >= -10 AND vtotal <= 10 THEN
+ SET vresultado = CONCAT('Amarillo: ', vtotal, '%');
+ ELSE
+ SET vresultado = CONCAT('Rojo: ', vtotal, '%');
+ END IF;
+ SELECT vresultado AS "Porcentaje variación ventas";
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL comparacion_ventas('2016-12-31','2017-12-31');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Verde: 28.23% |
++------------------------------+
+```
+
+```sql
+CALL comparacion_ventas('2018-03-28','2018-03-27');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Amarillo: -2.2% |
++------------------------------+
+```
+
+```sql
+CALL comparacion_ventas('2018-03-28','2015-03-28');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Rojo: -41.86% |
++------------------------------+
+```
+
+## CASE
+
+```sql
+CASE
+ WHEN THEN ;
+ WHEN THEN ;
+ (...)
+ WHEN THEN ;
+ [ELSE ]
+END CASE;
+```
+
+```sql
+/*
+Maracuya = Rico
+Frutilla = Rico
+Uva = Rico
+Sandía = Normal
+Mango = Normal
+Otros = Comunes
+*/
+DROP PROCEDURE IF EXISTS clasif_sabores;
+DELIMITER $$
+CREATE PROCEDURE clasif_sabores(
+ vcodigo VARCHAR(20)
+ COLLATE 'utf8mb4_uca1400_as_ci'
+)
+BEGIN
+ DECLARE msg_error VARCHAR(40);
+ DECLARE vresultado VARCHAR(50);
+ DECLARE vsabor VARCHAR(50);
+ # Se comenta el ELSE para testear HANDLER CASE NOT FOUND
+ #DECLARE CONTINUE HANDLER FOR 1339
+ DECLARE EXIT HANDLER FOR 1339
+ BEGIN
+ SET msg_error = "Sabor sin clasificar!";
+ SELECT msg_error AS "ERROR CASE NOT FOUND";
+ END;
+
+ SELECT sabor INTO vsabor
+ FROM tabla_de_productos
+ WHERE codigo_del_producto = vcodigo;
+
+ CASE vsabor
+ WHEN 'Maracuya' THEN SELECT 'Rico' INTO vresultado;
+ WHEN 'Limón' THEN SELECT 'Rico' INTO vresultado;
+ WHEN 'Frutilla' THEN SELECT 'Rico' INTO vresultado;
+ WHEN 'Uva' THEN SELECT 'Rico' INTO vresultado;
+ WHEN 'Sandía' THEN SELECT 'Normal' INTO vresultado;
+ WHEN 'Mango' THEN SELECT 'Normal' INTO vresultado;
+ #ELSE SELECT 'Común' INTO vresultado;
+ END CASE;
+ SELECT CONCAT(vsabor, ': ', vresultado) AS "Clasificación de sabor";
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL clasif_sabores('1042712');
++-------------------------+
+| Clasificación de sabor |
++-------------------------+
+| Limón: Rico |
++-------------------------+
+```
+
+```sql
+CALL clasif_sabores('1004327');
++-------------------------+
+| Clasificación de sabor |
++-------------------------+
+| Sandía: Normal |
++-------------------------+
+```
+
+```sql
+CALL clasif_sabores('394479');
++-------------------------+
+| Clasificación de sabor |
++-------------------------+
+| Cereza: Común |
++-------------------------+
+```
+
+### CASE condicional
+
+```sql
+DROP PROCEDURE IF EXISTS clasif_precio;
+DELIMITER $$
+CREATE PROCEDURE clasif_precio(
+ vcodigo VARCHAR(20)
+ COLLATE 'utf8mb4_uca1400_as_ci'
+)
+BEGIN
+ DECLARE vresultado VARCHAR(40);
+ DECLARE vprecio FLOAT;
+
+ SELECT precio_de_lista INTO vprecio
+ FROM tabla_de_productos
+ WHERE codigo_del_producto = vcodigo;
+
+ CASE
+ WHEN vprecio >= 12 THEN
+ SET vresultado = CONCAT(
+ 'Producto Costoso: $', vprecio, '.-'
+ );
+ WHEN vprecio >= 7 AND vprecio < 12 THEN
+ SET vresultado = CONCAT(
+ 'Producto Asequible: $', vprecio, '.-'
+ );
+ ELSE
+ SET vresultado = CONCAT(
+ 'Producto Barato: $', vprecio, '.-'
+ );
+ END CASE;
+ SELECT vresultado AS "Clasificación por precio";
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL clasif_precio('1013793');
++----------------------------+
+| Clasificación por precio |
++----------------------------+
+| Producto Costoso: $24.01.- |
++----------------------------+
+```
+
+```sql
+CALL clasif_precio('1096818');
++-----------------------------+
+| Clasificación por precio |
++-----------------------------+
+| Producto Asequible: $7.71.- |
++-----------------------------+
+```
+
+```sql
+CALL clasif_precio('1000801');
++---------------------------+
+| Clasificación por precio |
++---------------------------+
+| Producto Barato: $2.45.- |
++---------------------------+
+```
+
+***Modificar SP `comparacion_ventas` usando CASE***
+
+```sql
+DROP PROCEDURE IF EXISTS compara_ventas;
+DELIMITER $$
+CREATE PROCEDURE compara_ventas(
+ vfecha1 DATE,
+ vfecha2 DATE
+)
+BEGIN
+ DECLARE vresultado VARCHAR(50);
+ DECLARE vtotal1 INTEGER;
+ DECLARE vtotal2 INTEGER;
+ DECLARE vtotal FLOAT;
+
+ SELECT SUM(B.CANTIDAD * B.PRECIO) INTO vtotal1
+ FROM facturas A INNER JOIN items_facturas B
+ ON A.NUMERO = B.NUMERO
+ WHERE A.FECHA_VENTA = vfecha1;
+
+ SELECT SUM(B.CANTIDAD * B.PRECIO) INTO vtotal2
+ FROM facturas A INNER JOIN items_facturas B
+ ON A.NUMERO = B.NUMERO
+ WHERE A.FECHA_VENTA = vfecha2;
+
+ SELECT ROUND(((vtotal2*100)/vtotal1)-100, 2) INTO vtotal;
+
+ CASE
+ WHEN vtotal > 10 THEN
+ SET vresultado = CONCAT('Verde: ', vtotal, '%');
+ WHEN vtotal >= -10 AND vtotal <= 10 THEN
+ SET vresultado = CONCAT('Amarillo: ', vtotal, '%');
+ ELSE
+ SET vresultado = CONCAT('Rojo: ', vtotal, '%');
+ END CASE;
+ SELECT vresultado AS "Porcentaje variación ventas";
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL comparacion_ventas('2016-12-31','2017-12-31');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Verde: 28.23% |
++------------------------------+
+```
+
+```sql
+CALL comparacion_ventas('2018-03-28','2018-03-27');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Amarillo: -2.2% |
++------------------------------+
+```
+
+```sql
+CALL comparacion_ventas('2018-03-28','2015-03-28');
++------------------------------+
+| Porcentaje variación ventas |
++------------------------------+
+| Rojo: -41.86% |
++------------------------------+
+```
+
+## WHILE
+
+```sql
+WHILE
+ DO ;
+END WHILE;
+```
+
+```sql
+DROP PROCEDURE IF EXISTS looping;
+DELIMITER $$
+CREATE PROCEDURE looping(vinicial INT, vfinal INT)
+BEGIN
+ DECLARE vcontador INT;
+ SET vcontador = vinicial;
+ CREATE TABLE tb_looping (ID INT);
+ WHILE vcontador <= vfinal
+ DO
+ INSERT INTO tb_looping VALUES(vcontador);
+ SET vcontador = vcontador+1;
+ END WHILE;
+ SELECT * FROM tb_looping;
+ DROP TABLE tb_looping;
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL looping(1,5);
++------+
+| ID |
++------+
+| 1 |
+| 2 |
+| 3 |
+| 4 |
+| 5 |
++------+
+```
+
+***Crear un SP que, a partir del día `01/01/2017`, cuente el número de***
+***facturas hasta el día `10/01/2017`. Debe mostrar la fecha y el número***
+***de facturas día tras día***
+
+#### Solución propuesta
+
+```sql
+DROP PROCEDURE IF EXISTS loop_date;
+DELIMITER $$
+CREATE PROCEDURE loop_date(vfecha_inicial DATE, vfecha_final DATE)
+BEGIN
+ CREATE TEMPORARY TABLE informe (Fecha DATE, Facturas_Emitidas INT);
+ WHILE vfecha_inicial <= vfecha_final
+ DO
+ INSERT INTO informe
+ SELECT fecha_venta, COUNT(numero) FROM facturas
+ WHERE fecha_venta = vfecha_inicial;
+ SET vfecha_inicial = ADDDATE(vfecha_inicial, INTERVAL 1 DAY);
+ END WHILE;
+ SELECT * FROM informe;
+ DROP TABLE informe;
+END $$
+
+DELIMITER ;
+```
+
+```sql
+CALL loop_date('2017-01-01','2017-01-10');
++------------+-------------------+
+| Fecha | Facturas_Emitidas |
++------------+-------------------+
+| 2017-01-01 | 74 |
+| 2017-01-02 | 77 |
+| 2017-01-03 | 81 |
+| 2017-01-04 | 71 |
+| 2017-01-05 | 65 |
+| 2017-01-06 | 75 |
+| 2017-01-07 | 82 |
+| 2017-01-08 | 80 |
+| 2017-01-09 | 85 |
+| 2017-01-10 | 72 |
++------------+-------------------
+```
+
+#### Solución
+
+```sql
+DROP PROCEDURE IF EXISTS suma_dias_facturas;
+DELIMITER $$
+CREATE PROCEDURE suma_dias_facturas()
+BEGIN
+ DECLARE fecha_inicial DATE;
+ DECLARE fecha_final DATE;
+ DECLARE n_facturas INT;
+ SET fecha_inicial = '20170101';
+ SET fecha_final = '20170110';
+ WHILE fecha_inicial <= fecha_final
+ DO
+ SELECT COUNT(*) INTO n_facturas FROM facturas
+ WHERE FECHA_VENTA = fecha_inicial;
+ SELECT concat(
+ DATE_FORMAT(fecha_inicial, '%d/%m/%Y'),
+ '-' , CAST(n_facturas AS CHAR(50))
+ ) AS RESULTADO;
+ SELECT ADDDATE(fecha_inicial, INTERVAL 1 DAY) INTO fecha_inicial;
+ END WHILE;
+END $$
+DELIMITER ;
+```
+
+## Problemas con SELECT INTO
+
+```sql
+DROP PROCEDURE IF EXISTS problema_select_into;
+DELIMITER $$
+CREATE PROCEDURE problema_select_into()
+BEGIN
+ DECLARE vnombre VARCHAR(50);
+ SELECT nombre INTO vnombre FROM tabla_de_clientes;
+ SELECT vnombre;
+END $$
+DELIMITER ;
+
+CALL problema_select_into();
+ERROR 1172 (42000): Result consisted of more than one row
+```
+
+## CURSOR
+
+Un **CURSOR** es una estructura que permite la interación línea a línea
+mediante un orden determinado
+
+#### Fases para utilizar Cursor
+
+- **Declaración:** Definir la consulta que será depositada en el cursor
+- **Apertura:** Abrir el cursor para recorrerlo línea a línea
+- **Recorrido:** Línea a línea hasta el final
+- **Cierre:** Cierre del cursor
+- **Limpieza:** Limpiar el cursor de la memoria
+
+#### Tabla_1
+
+| Codigo | Nombre | Valor |
+| - | - | - |
+| 1 | Juan | 2 |
+| 2 | José | 67 |
+| 3 | María | 7 |
+| 4 | Lucia | 54 |
+
+```sql
+DECLARE @nombre VARCHAR(10)
+DECLARE CURSOR_1 CURSOR FOR
+ SELECT NOMBRE FROM tabla_1;
+ OPEN CURSOR_1;
+ FETCH CURSOR_1 INTO @nombre;
+ FETCH CURSOR_1 INTO @nombre;
+ FETCH CURSOR_1 INTO @nombre;
+ FETCH CURSOR_1 INTO @nombre;
+ CLOSE CURSOR_1;
+```
+
+#### Ejemplo CURSOR
+
+```sql
+DROP PROCEDURE IF EXISTS ejm_cursor;
+DELIMITER $$
+CREATE PROCEDURE ejm_cursor()
+BEGIN
+ DECLARE vnombre VARCHAR(50);
+ DECLARE c CURSOR FOR
+ SELECT nombre FROM tabla_de_clientes LIMIT 4;
+ OPEN c;
+ FETCH c INTO vnombre;
+ SELECT vnombre;
+ FETCH c INTO vnombre;
+ SELECT vnombre;
+ FETCH c INTO vnombre;
+ SELECT vnombre;
+ FETCH c INTO vnombre;
+ SELECT vnombre;
+ CLOSE c;
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL ejm_cursor;
++---------------+
+| vnombre |
++---------------+
+| Erica Carvajo |
++---------------+
+
++--------------+
+| vnombre |
++--------------+
+| Marcos Rosas |
++--------------+
+
++--------------+
+| vnombre |
++--------------+
+| Jorge Castro |
++--------------+
+
++-------------+
+| vnombre |
++-------------+
+| Abel Pintos |
++-------------+
+```
+
+### Iterando el CURSOR
+
+```sql
+DROP PROCEDURE IF EXISTS iter_cursor;
+DELIMITER $$
+CREATE PROCEDURE iter_cursor()
+BEGIN
+ DECLARE fin_c BIT(1) DEFAULT b'0';
+ DECLARE vnombre VARCHAR(50);
+ DECLARE c CURSOR FOR
+ SELECT nombre FROM tabla_de_clientes;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET fin_c = b'1';
+ OPEN c;
+ WHILE fin_c = b'0' DO
+ FETCH c INTO vnombre;
+ IF fin_c = 0 THEN
+ SELECT vnombre;
+ END IF;
+ END WHILE;
+ CLOSE c;
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL iter_cursor;
++---------------+
+| vnombre |
++---------------+
+| Erica Carvajo |
++---------------+
+
++--------------+
+| vnombre |
++--------------+
+| Marcos Rosas |
++--------------+
+
++--------------+
+| vnombre |
++--------------+
+| Jorge Castro |
++--------------+
+
+...
+```
+
+***Crear un SP usando un cursor para hallar el valor total de todos los***
+***créditos, de todos los clientes***
+
+```sql
+DROP PROCEDURE IF EXISTS total_creditos;
+DELIMITER $$
+CREATE PROCEDURE total_creditos()
+BEGIN
+ DECLARE fin_c BIT(1) DEFAULT b'0';
+ DECLARE vcred_total FLOAT DEFAULT 0;
+ DECLARE vcred_temp FLOAT DEFAULT 0;
+ DECLARE c CURSOR FOR
+ SELECT limite_de_credito FROM tabla_de_clientes;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET fin_c = b'1';
+ OPEN c;
+ WHILE fin_c = b'0' DO
+ FETCH c INTO vcred_temp;
+ IF fin_c = 0 THEN
+ SET vcred_total = vcred_total + vcred_temp;
+ END IF;
+ END WHILE;
+ CLOSE c;
+ SELECT vcred_total AS "Limite de Credito TOTAL";
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL total_creditos;
++-------------------------+
+| Limite de Credito TOTAL |
++-------------------------+
+| 1780000 |
++-------------------------+
+```
+
+### Asignación de multiples campos al CURSOR
+
+```sql
+DROP PROCEDURE IF EXISTS cursor_multicampo;
+DELIMITER $$
+CREATE PROCEDURE cursor_multicampo()
+BEGIN
+ DECLARE fin_c BIT(1) DEFAULT b'0';
+ DECLARE vbarrio, vciudad, vcodp VARCHAR(50);
+ DECLARE vnombre, vdireccion VARCHAR(150);
+ DECLARE c CURSOR FOR
+ SELECT nombre, direccion_1, barrio, ciudad, cp
+ FROM tabla_de_clientes;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET fin_c = b'1';
+ OPEN c;
+ WHILE fin_c = b'0' DO
+ FETCH c INTO vnombre, vdireccion, vbarrio, vciudad, vcodp;
+ IF fin_c = b'0' THEN
+ SELECT CONCAT(vnombre, ', ', vdireccion, ', ', vbarrio, ', ',
+ vciudad, ', ', vcodp) AS Muticampos;
+ END IF;
+ END WHILE;
+ CLOSE c;
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL cursor_multicampo;
++------------------------------------------------------------------------------+
+| Muticampos |
++------------------------------------------------------------------------------+
+| Erica Carvajo, Heriberto Frías 1107, Del Valle, Ciudad de México, 80012212 |
++------------------------------------------------------------------------------+
+
++-----------------------------------------------------------------------+
+| Muticampos |
++-----------------------------------------------------------------------+
+| Marcos Rosas, Av. Universidad, Del Valle, Ciudad de México, 22002012 |
++-----------------------------------------------------------------------+
+
++---------------------------------------------------------------------------------+
+| Muticampos |
++---------------------------------------------------------------------------------+
+| Jorge Castro, Federal México-Toluca 5690, Locaxco, Ciudad de México, 22012002 |
++---------------------------------------------------------------------------------+
+
+...
+```
+
+***Crear un SP usando un cursor para hallar el valor total de la***
+***facturación para un determinado mes y año.***
+
+#### Solución propuesta
+
+```sql
+DROP PROCEDURE IF EXISTS facturacion_mes_ano;
+DELIMITER $$
+CREATE PROCEDURE facturacion_mes_ano(ano_mes VARCHAR(10))
+BEGIN
+ DECLARE fin_c BIT(1) DEFAULT b'0';
+ DECLARE vcantidad INT;
+ DECLARE vprecio FLOAT;
+ DECLARE vtotal FLOAT DEFAULT 0;
+ DECLARE vfecha DATE;
+ DECLARE c CURSOR FOR
+ SELECT IFa.cantidad, IFa.precio
+ FROM items_facturas IFa
+ INNER JOIN facturas F ON F.numero = IFa.numero
+ WHERE MONTH(F.fecha_venta) = MONTH(vfecha)
+ AND YEAR(F.fecha_venta) = YEAR(vfecha);
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET fin_c = b'1';
+ SET vfecha = DATE(CONCAT(ano_mes, '-01'));
+ OPEN c;
+ WHILE fin_c = b'0' DO
+ FETCH c INTO vcantidad, vprecio;
+ IF fin_c = b'0' THEN
+ SET vtotal = vtotal+(vcantidad*vprecio);
+ END IF;
+ END WHILE;
+ CLOSE c;
+ SELECT ano_mes AS "Año-Mes",
+ CONCAT('$ ', vtotal, '.-') AS "Venta Total";
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL facturacion_mes_ano('2017-01');
++----------+-------------+
+| Año-Mes | Venta Total |
++----------+-------------+
+| 2017-01 | $3838320.- |
++----------+-------------+
+```
+
+#### Solución
+
+```sql
+DROP PROCEDURE IF EXISTS campo_adicional;
+DELIMITER $$
+CREATE PROCEDURE campo_adicional()
+BEGIN
+ DECLARE cantidad INT;
+ DECLARE precio FLOAT;
+ DECLARE facturacion_acumulada FLOAT;
+ DECLARE fin_cursor INT;
+ DECLARE c CURSOR FOR
+ SELECT IFa.CANTIDAD, IFa.PRECIO FROM items_facturas IFa
+ INNER JOIN facturas F ON F.NUMERO = IFa.NUMERO
+ WHERE MONTH(F.FECHA_VENTA) = 1
+ AND YEAR(F.FECHA_VENTA) = 2017;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET fin_cursor = 1;
+ OPEN c;
+ SET fin_cursor = 0;
+ SET facturacion_acumulada = 0;
+ WHILE fin_cursor = 0 DO
+ FETCH c INTO cantidad, precio;
+ IF fin_cursor = 0 THEN
+ SET facturacion_acumulada =
+ facturacion_acumulada + (cantidad * precio);
+ END IF;
+ END WHILE;
+ CLOSE c;
+ SELECT facturacion_acumulada;
+END $$
+DELIMITER ;
+```
+
+```sql
+CALL campo_adicional;
++-----------------------+
+| facturacion_acumulada |
++-----------------------+
+| 3838320 |
++-----------------------+
+```
+
+## FUNCTION
+
+```sql
+CREATE FUNCTION function_name (parameters)
+RETURNS datatype;
+BEGIN
+ DECLARE ;
+ (...)
+ ;
+ (...)
+ RETURN
+ (...)
+END;
+```
+
+```sql
+DROP FUNCTION IF EXISTS f_clas_sabores;
+DELIMITER $$
+CREATE FUNCTION f_clas_sabores(vsabor VARCHAR(20))
+RETURNS VARCHAR(40) COLLATE 'utf8mb4_uca1400_as_ci'
+BEGIN
+ DECLARE vretorno VARCHAR(40) DEFAULT '';
+ CASE vsabor
+ WHEN 'Maracuya' THEN SET vretorno = 'Rico';
+ WHEN 'Limón' THEN SET vretorno = 'Rico';
+ WHEN 'Frutilla' THEN SET vretorno = 'Rico';
+ WHEN 'Uva' THEN SET vretorno = 'Rico';
+ WHEN 'Sandía' THEN SET vretorno = 'Normal';
+ WHEN 'Mango' THEN SET vretorno = 'Normal';
+ ELSE SET vretorno = 'Común';
+ END CASE;
+ RETURN vretorno;
+END $$
+DELIMITER ;
+```
+
+```sql
+SELECT f_clas_sabores('Sandía') AS "Clasificación";
++----------------+
+| Clasificación |
++----------------+
+| Normal |
++----------------+
+```
+
+```sql
+SELECT nombre_del_producto, sabor, f_clas_sabores(sabor) AS "Clasificación"
+FROM tabla_de_productos;
++---------------------+-----------------+----------------+
+| nombre_del_producto | sabor | Clasificación |
++---------------------+-----------------+----------------+
+| Sabor del Mar | Naranja | Común |
+| Sabor del Mar | Naranja | Común |
+| Sabor da Montaña | Uva | Rico |
+...
+```
+
+```sql
+SELECT nombre_del_producto, sabor
+FROM tabla_de_productos
+WHERE f_clas_sabores(SABOR) = 'Normal';
++---------------------+---------+
+| nombre_del_producto | sabor |
++---------------------+---------+
+| Vida del Campo | Sandía |
+| Light | Sandía |
+| Verano | Mango |
+| Refrescante | Mango |
+| Refrescante | Mango |
+| Verano | Mango |
+| Vida del Campo | Sandía |
+| Refrescante | Mango |
+| Light | Sandía |
++---------------------+---------+
+```
+
+***Transformar el sgte. SP en una función que recibe como parámetro la***
+***fecha y retorna el número de facturas***
+
+```sql
+DELIMITER $$
+CREATE PROCEDURE `sp_numero_facturas` ()
+BEGIN
+ DECLARE n_facturas INT;
+ SELECT COUNT(*) INTO n_facturas FROM facturas
+ WHERE FECHA_VENTA = '20170101';
+ SELECT n_facturas;
+END $$
+```
+
+```sql
+DROP FUNCTION IF EXISTS f_cantidad_facturas;
+DELIMITER $$
+CREATE FUNCTION f_cantidad_facturas(fecha DATE)
+RETURNS INT
+BEGIN
+ DECLARE n_facturas INT;
+ SELECT COUNT(*) INTO n_facturas FROM facturas
+ WHERE FECHA_VENTA = fecha;
+ RETURN n_facturas;
+END $$
+DELIMITER ;
+```
+
+```sql
+SELECT f_cantidad_facturas('2018-01-01');
++-----------------------------------+
+| f_cantidad_facturas('2018-01-01') |
++-----------------------------------+
+| 87 |
++-----------------------------------+
+```
diff --git a/011_mysql/table_data_import_wizard.png b/011_mysql/table_data_import_wizard.png
new file mode 100644
index 0000000..8982645
Binary files /dev/null and b/011_mysql/table_data_import_wizard.png differ