Laboratorio 7: Completar, Operar y Consultar TechStyle
Semana 7 · W07 · Unidad II · Transacciones y Consultas · Universidad San Sebastián
1 Contexto
En W06 construiste la estructura de TechStyle y cargaste datos en 5 de las 9 tablas. Hoy completarás la base de datos cargando las 4 tablas restantes, practicarás transacciones para garantizar operaciones atómicas, y ejecutarás tus primeras consultas con JOIN para extraer información útil para el negocio.
Al terminar este laboratorio tendrás la base de datos completamente poblada, con operaciones transaccionales probadas y un primer vistazo a cómo se responden preguntas de negocio con SQL.
2 Instrucciones generales
- Trabajarás en los mismos grupos de los laboratorios anteriores.
- Duración total: 1 hora 25 minutos.
- Necesitas la base de datos
techstylecon los datos del laboratorio W06 ya cargados.
Herramientas necesarias:
| Herramienta | Uso |
|---|---|
| MySQL Workbench 8.0 | Ejecución de scripts e inspección de resultados |
| MySQL Server 8.0 | Motor donde está la base de datos techstyle |
Estructura del laboratorio:
| Parte | Actividad | Tiempo |
|---|---|---|
| 1 | Cargar las tablas faltantes | 40 min |
| 2 | Primer vistazo a consultas con JOIN | 25 min |
| 3 | Síntesis y reflexión | 20 min |
3 Parte 1 — Cargar las Tablas Faltantes desde CSV
Duración: 40 minutos
En W06 cargaste categorias, clientes, productos, ventas y detalle_ventas fila por fila con INSERT. Hoy cargarás las 4 tablas restantes desde archivos CSV usando LOAD DATA LOCAL INFILE — la forma estándar de cargar volúmenes de datos en MySQL sin escribir un INSERT por cada fila.
Los archivos CSV están disponibles en la carpeta labs/data/ del repositorio del curso:
| Archivo | Tabla destino | Filas |
|---|---|---|
proveedores.csv |
proveedores |
4 |
proveedor_producto.csv |
proveedor_producto |
10 |
despachos.csv |
despachos |
3 |
devoluciones.csv |
devoluciones |
1 |
Descárgalos y guárdalos en una carpeta local antes de continuar.
3.1 Paso 1: Habilitar la carga desde archivos locales
LOAD DATA LOCAL INFILE está deshabilitado por defecto en MySQL 8. Debes activarlo en dos lugares:
1a. En el servidor — ejecuta esto una sola vez como root:
SET GLOBAL local_infile = 1;
-- Verifica que quedó activado:
SHOW GLOBAL VARIABLES LIKE 'local_infile';
-- Debe mostrar: Value = ON1b. En la conexión de Workbench — cierra la sesión actual y edita tu conexión:
- Database → Manage Connections → selecciona tu conexión → pestaña Advanced
- En el campo “Others”, agrega la línea:
OPT_LOCAL_INFILE=1 - Guarda y reconecta.
Si no activas ambas opciones, MySQL retornará el error Loading local data is disabled. Ambas configuraciones son necesarias.
3.2 Paso 2: Cargar proveedores
USE techstyle;
LOAD DATA LOCAL INFILE '/ruta/a/proveedores.csv'
INTO TABLE proveedores
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(proveedor_id, nombre_proveedor, rut, contacto, email, telefono);
SELECT COUNT(*) AS filas_cargadas FROM proveedores;
-- Resultado esperado: 4Ajusta la ruta según tu sistema operativo:
- Windows:
'C:/Users/tu_usuario/Downloads/proveedores.csv' - Mac:
'/Users/tu_usuario/Downloads/proveedores.csv' - Linux:
'/home/tu_usuario/Downloads/proveedores.csv'
En Windows usa barras hacia adelante (/), no hacia atrás (\).
Adapta los nombres de columnas si tu modelo de W05 usa nombres distintos. El criterio de evaluación valorará la coherencia con tu propio diseño.
3.3 Paso 3: Cargar proveedor_producto
LOAD DATA LOCAL INFILE '/ruta/a/proveedor_producto.csv'
INTO TABLE proveedor_producto
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(proveedor_id, producto_id, precio_compra, plazo_entrega_dias, proveedor_principal);
SELECT COUNT(*) AS filas_cargadas FROM proveedor_producto;
-- Resultado esperado: 10Pregunta: ¿Por qué precio_compra se guarda en proveedor_producto y no directamente en productos? ¿Qué pasaría si un producto tuviera dos proveedores con precios distintos?
3.4 Paso 4: Cargar despachos (con valores NULL)
El archivo despachos.csv tiene filas donde fecha_despacho, fecha_entrega_estimada y fecha_entrega_real están vacíos — representan un despacho aún no procesado o no entregado. MySQL no convierte campos vacíos a NULL automáticamente, por lo que se necesita una cláusula SET con NULLIF:
LOAD DATA LOCAL INFILE '/ruta/a/despachos.csv'
INTO TABLE despachos
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(venta_id, direccion_entrega, @fecha_despacho, @fecha_entrega_estimada, @fecha_entrega_real, estado)
SET
fecha_despacho = NULLIF(@fecha_despacho, ''),
fecha_entrega_estimada = NULLIF(@fecha_entrega_estimada, ''),
fecha_entrega_real = NULLIF(@fecha_entrega_real, '');
SELECT * FROM despachos;¿Qué hace NULLIF(@variable, '')? Lee el campo como texto en una variable temporal (@fecha_despacho), y luego lo convierte a NULL si el texto es una cadena vacía. Si tiene valor, lo inserta tal cual.
Esto es distinto al NULL en claves foráneas — aquí NULL representa información genuinamente desconocida (el despacho aún no ha salido), no una referencia rota.
3.5 Paso 5: Cargar devoluciones
LOAD DATA LOCAL INFILE '/ruta/a/devoluciones.csv'
INTO TABLE devoluciones
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(venta_id, producto_id, fecha_devolucion, cantidad_devuelta, motivo, estado, @monto_reembolsado)
SET
monto_reembolsado = NULLIF(@monto_reembolsado, '');
SELECT COUNT(*) AS filas_cargadas FROM devoluciones;
-- Resultado esperado: 1-- Proveedores
INSERT INTO proveedores (proveedor_id, nombre_proveedor, rut, contacto, email, telefono) VALUES
(1, 'Nike Distribuciones Chile', '76.543.210-1', 'Felipe Morales', 'contacto@nikedist.cl', '+56 2 2345 6789'),
(2, 'Adidas Importaciones Sur', '76.234.567-8', 'Carolina Vidal', 'ventas@adidassur.com.ar', '+54 11 4567 8901'),
(3, 'Puma Latam Distribuidora', '76.345.678-9', 'Lucas Ferreira', 'pedidos@pumalatam.com', '+55 11 9876 5432'),
(4, 'Multimarca Sport Ltda.', '76.456.789-0', 'Andrea Salinas', 'info@multimarcasport.cl', '+56 9 8765 4321');
-- Proveedor–producto
INSERT INTO proveedor_producto (proveedor_id, producto_id, precio_compra, plazo_entrega_dias, proveedor_principal) VALUES
(1, 1, 42000.00, 14, 1), (1, 5, 13500.00, 14, 1),
(1, 9, 6500.00, 14, 1), (1,10, 8500.00, 14, 1),
(2, 2, 62000.00, 21, 1), (2, 4, 11000.00, 21, 1),
(3, 3, 26000.00, 21, 1), (3, 8, 18500.00, 21, 1),
(4, 6, 20000.00, 7, 1), (4, 7, 47000.00, 7, 1);
-- Despachos
INSERT INTO despachos (venta_id, direccion_entrega, fecha_despacho, fecha_entrega_estimada, fecha_entrega_real, estado) VALUES
(1, 'Av. Providencia 1234, Providencia, Santiago', '2026-01-16', '2026-01-19', '2026-01-18', 'Entregado'),
(2, 'Cerro Alegre 567, Valparaíso', '2026-01-21', '2026-01-24', NULL, 'En tránsito'),
(3, 'Av. O''Higgins 890, Concepción', NULL, NULL, NULL, 'Pendiente');
-- Devoluciones
INSERT INTO devoluciones (venta_id, producto_id, fecha_devolucion, cantidad_devuelta, motivo, estado) VALUES
(1, 4, '2026-01-25', 1, 'Talla incorrecta', 'Pendiente');3.6 Paso 6: Verificar el estado completo de la base de datos
Ejecuta esta consulta resumen para confirmar que las 9 tablas tienen datos:
SELECT 'categorias' AS tabla, COUNT(*) AS filas FROM categorias UNION ALL
SELECT 'clientes', COUNT(*) FROM clientes UNION ALL
SELECT 'proveedores', COUNT(*) FROM proveedores UNION ALL
SELECT 'productos', COUNT(*) FROM productos UNION ALL
SELECT 'ventas', COUNT(*) FROM ventas UNION ALL
SELECT 'detalle_ventas', COUNT(*) FROM detalle_ventas UNION ALL
SELECT 'proveedor_producto', COUNT(*) FROM proveedor_producto UNION ALL
SELECT 'despachos', COUNT(*) FROM despachos UNION ALL
SELECT 'devoluciones', COUNT(*) FROM devoluciones;Completa la tabla con los resultados:
| Tabla | Filas esperadas | Filas reales | ¿Coincide? |
|---|---|---|---|
categorias |
10 | ||
clientes |
6 | ||
proveedores |
4 | ||
productos |
10 | ||
ventas |
3 | ||
detalle_ventas |
5 | ||
proveedor_producto |
10 | ||
despachos |
3 | ||
devoluciones |
1 |
4 Parte 2 — Primer Vistazo a Consultas con JOIN
Duración: 25 minutos
Hasta ahora has usado INSERT y UPDATE. Ahora harás las primeras preguntas a la base de datos. Los JOIN que ves aquí se estudiarán en profundidad en W08 — por ahora, úsalos para leer los resultados y conectarlos con decisiones de negocio.
4.1 Paso 10: ¿De dónde vienen nuestros productos?
-- ¿Qué productos provee cada proveedor y a qué precio?
SELECT pv.nombre_proveedor,
pr.nombre_producto,
pp.precio_compra,
pr.precio_venta,
ROUND(pr.precio_venta - pp.precio_compra, 0) AS margen_bruto
FROM proveedores pv
JOIN proveedor_producto pp ON pv.proveedor_id = pp.proveedor_id
JOIN productos pr ON pp.producto_id = pr.producto_id
ORDER BY pv.nombre_proveedor, margen_bruto DESC;Pregunta: ¿Cuál es el producto con mayor margen bruto? ¿Qué decisión de negocio podría tomar Roberto con esta información?
4.2 Paso 11: ¿Cuál es el estado de los despachos?
-- Estado de todos los despachos con nombre del cliente
SELECT d.despacho_id,
c.nombre,
c.apellido,
c.region,
d.direccion_entrega,
d.fecha_despacho,
d.fecha_entrega_estimada,
d.fecha_entrega_real,
d.estado
FROM despachos d
JOIN ventas v ON d.venta_id = v.venta_id
JOIN clientes c ON v.cliente_id = c.cliente_id
ORDER BY d.estado;Pregunta: ¿Qué debería hacer el equipo de logística con los despachos en estado 'Pendiente'?
4.3 Paso 12: ¿Hay devoluciones que atender?
-- Devoluciones pendientes con detalle del cliente y producto
SELECT dev.devolucion_id,
c.nombre,
c.apellido,
p.nombre_producto,
dev.motivo,
dev.estado,
dev.fecha_devolucion
FROM devoluciones dev
JOIN ventas v ON dev.venta_id = v.venta_id
JOIN clientes c ON v.cliente_id = c.cliente_id
JOIN productos p ON dev.producto_id = p.producto_id
WHERE dev.estado = 'Pendiente';Observa la estructura: siempre hay un FROM (tabla principal), y luego JOIN ... ON para cada relación que quieres cruzar. No necesitas memorizar la sintaxis hoy — en W08 la aprenderás formalmente. Hoy el objetivo es leer el resultado y conectarlo con el negocio.
Pregunta grupal: De las tres consultas anteriores (proveedores, despachos, devoluciones), ¿cuál respondería una pregunta que no era posible responder con una hoja de Excel simple? ¿Por qué?
5 Parte 3 — Síntesis y Reflexión
Duración: 20 minutos
5.1 Paso 13: Comparar el modelo diseñado vs. el modelo implementado
| Dimensión | Modelo en papel (W05) | Base de datos real (W06–W07) |
|---|---|---|
| ¿Se puede saber qué proveedor abastece un producto? | Sí (diagrama) | |
| ¿Las FK impiden ventas de clientes inexistentes? | En diseño | |
| ¿Un producto puede tener stock negativo? | Según diseño, no | |
| ¿Qué pasa si se borra un cliente con ventas? | RESTRICT (diseño) |
5.2 Paso 14: Preguntas de diseño e implementación
Responde en grupo:
| Pregunta | Respuesta |
|---|---|
¿Por qué se usa SET @variable = LAST_INSERT_ID() en lugar de poner el número directamente (ej. venta_id = 3)? |
|
| ¿Qué pasaría si dos vendedores insertan una venta para el mismo cliente al mismo tiempo sin transacciones? | |
¿Por qué precio_unitario se guarda en detalle_ventas y no se calcula desde productos.precio_venta en cada consulta? |
|
| Si TechStyle aplica un descuento general del 20% en una promoción, ¿qué operación SQL ejecutarías? ¿Dentro de una transacción? | |
Un cliente quiere borrar su cuenta. ¿Qué debe hacer Juan antes de ejecutar DELETE FROM clientes WHERE cliente_id = 1? |
5.3 Paso 15: Análisis de impacto — del modelo a las decisiones
Para cada capacidad implementada, identifica la decisión de negocio que habilita:
| Capacidad implementada | Decisión de negocio que habilita |
|---|---|
FK con ON DELETE RESTRICT en ventas.cliente_id |
Ej: “Garantizamos que el historial de ventas nunca pierde la referencia al cliente” |
CHECK stock >= 0 en productos |
|
Índice idx_ventas_fecha en ventas(fecha) |
|
CHECK segmento IN ('Premium', 'Estándar', 'Básico') |
5.4 Paso 16: Reporte de implementación
Redacta un párrafo breve (6–8 líneas) como si fueras Juan informando a Roberto sobre el estado del sistema al cierre del día:
Para: Roberto Fernández, CEO TechStyle
Asunto: Base de datos completamente operativa — estado al 2026-04-28
La base de datos
techstyleestá completamente poblada en MySQL Server. Contiene… (continúa)
Incluye: - Cuántas tablas están operativas y cuántos registros tiene cada una. - Qué restricciones de integridad garantizan la consistencia de los datos. - Qué puede hacerse ahora que no era posible con Excel. - Qué falta aún para que el sistema sea completamente operativo.
6 Entregable
Sube a la plataforma los siguientes archivos comprimidos en W07_lab_[apellido1]_[apellido2].zip:
| Archivo | Contenido |
|---|---|
W07_carga.sql |
Script con los comandos LOAD DATA LOCAL INFILE de la Parte 1 (ajustados a tu ruta real) |
W07_reflexion.pdf o .docx |
Respuestas de las preguntas de reflexión + reporte para Roberto |
7 Criterios de Evaluación
| Criterio | Puntaje |
|---|---|
| Carga completa de las 4 tablas faltantes, verificación de filas (Parte 1) | 40 pts |
| Consultas con JOIN: resultados correctos, respuestas a preguntas de negocio (Parte 2) | 30 pts |
| Síntesis: análisis de impacto, preguntas de diseño y reporte para Roberto (Parte 3) | 30 pts |
| Total | 100 pts |