diff --git a/resoluciones/maria-montepeque/ejercicio-77/README.md b/resoluciones/maria-montepeque/ejercicio-77/README.md new file mode 100644 index 00000000..aa144fa7 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-77/README.md @@ -0,0 +1,83 @@ +# Ejercicio 77: DELETE Nivel Basico + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +DELETE + +## Descripcion del problema + +Una bodega de dispositivos tecnologicos necesita corregir errores de +captura en su historial de movimientos, y tambien dar de baja +productos descontinuados sin perder el historial de lo que ya se +vendio o recibio de ellos. Este ejercicio compara ambos casos: cuando +`DELETE` fisico es seguro, y cuando conviene una baja logica en su +lugar. + +## Tablas y relaciones + +- `categorias`: catalogo de categorias de producto. +- `productos`: catalogo de productos, con una bandera `activo` para + la baja logica. +- `movimientos`: historial de entradas y salidas de bodega. + `categorias` 1—N `productos`; `productos` 1—N `movimientos`. + +## Uso de DELETE + +En `dml/inserts.sql`: + +1. `DELETE` real (baja fisica): un movimiento de Mouse Inalambrico se + cargo dos veces por error de digitacion. Como `movimientos` no + tiene dependientes, es seguro eliminar de verdad la fila duplicada + con `WHERE id_movimiento = 6`. +2. Baja logica (sin `DELETE`): Teclado Mecanico se descontinua, pero + ya tiene un movimiento asociado por `FOREIGN KEY`. En vez de + intentar borrarlo, se marca `activo = 0` con `UPDATE`, conservando + el historial. + +La consulta 5 en `dql/consultas.sql` confirma que el movimiento +duplicado ya no existe, y que el resto de movimientos del mismo +producto sigue intacto. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `productos.id_categoria`, `movimientos.id_producto`. +- `NOT NULL` en todas las columnas obligatorias. +- `UNIQUE`: `categorias.nombre_categoria`, `productos.nombre_producto`. +- `CHECK`: `productos.precio_unitario >= 0`, `productos.activo IN (0, 1)`, + `movimientos.tipo_movimiento IN (...)`, `movimientos.cantidad > 0`. +- `DEFAULT` en `productos.activo`, `movimientos.tipo_movimiento` y + `fecha_movimiento`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dml/inserts.sql`) + +`DELETE FROM productos WHERE id_producto = 4;` falla porque Teclado +Mecanico todavia tiene un movimiento en `movimientos` que depende de +el por `FOREIGN KEY`. Se valido con Python (`sqlite3`): lanza +`FOREIGN KEY constraint failed`. Esto es exactamente lo que justifica +usar baja logica (`UPDATE activo = 0`) en vez de `DELETE` para +productos que ya tienen historial. + +## Evidencias de ejecucion + +Scripts validados en orden (`ddl` -> `inserts` -> `consultas`) con +Python (modulo `sqlite3`), ya que no se tenia el binario `sqlite3` +disponible en el entorno. Detalle completo en +[`evidencias/resultados.md`](evidencias/resultados.md). + +- Datos finales: 5 movimientos (sin el duplicado), 4 productos + activos y 1 inactivo (Teclado Mecanico, dado de baja logica). + +## Como ejecutar + +```bash +sqlite3 ejercicio-77.db < ddl/schema.sql +sqlite3 ejercicio-77.db < dml/inserts.sql +sqlite3 ejercicio-77.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-77/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-77/ddl/schema.sql new file mode 100644 index 00000000..356728d8 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-77/ddl/schema.sql @@ -0,0 +1,37 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 77: DELETE Nivel Basico +-- Tema central: DELETE +-- Contexto: inventario de dispositivos tecnologicos en bodega. + +CREATE TABLE categorias ( + id_categoria INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_categoria TEXT NOT NULL UNIQUE +); + +-- productos: "activo" es la bandera de baja logica. Un producto +-- descontinuado no se borra (movimientos todavia lo referencia por +-- FOREIGN KEY), se marca como inactivo. +CREATE TABLE productos ( + id_producto INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_producto TEXT NOT NULL UNIQUE, + id_categoria INTEGER NOT NULL, + precio_unitario REAL NOT NULL CHECK (precio_unitario >= 0), + activo INTEGER NOT NULL DEFAULT 1 CHECK (activo IN (0, 1)), + + FOREIGN KEY (id_categoria) REFERENCES categorias (id_categoria) +); + +-- movimientos: historial de entradas y salidas. A diferencia de +-- productos, un movimiento sin dependientes si puede eliminarse de +-- verdad cuando es un error de captura (ver dml/inserts.sql). +CREATE TABLE movimientos ( + id_movimiento INTEGER PRIMARY KEY AUTOINCREMENT, + id_producto INTEGER NOT NULL, + tipo_movimiento TEXT NOT NULL DEFAULT 'entrada' + CHECK (tipo_movimiento IN ('entrada', 'salida')), + cantidad INTEGER NOT NULL CHECK (cantidad > 0), + fecha_movimiento TEXT NOT NULL DEFAULT (datetime('now')), + + FOREIGN KEY (id_producto) REFERENCES productos (id_producto) +); diff --git a/resoluciones/maria-montepeque/ejercicio-77/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-77/dml/inserts.sql new file mode 100644 index 00000000..4bfbca68 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-77/dml/inserts.sql @@ -0,0 +1,49 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 77: DELETE Nivel Basico +-- Datos de prueba y DELETE de validacion. + +INSERT INTO categorias (nombre_categoria) VALUES + ('Laptops'), + ('Perifericos'), + ('Almacenamiento'); + +INSERT INTO productos (nombre_producto, id_categoria, precio_unitario) VALUES + ('Laptop Pro 14', 1, 8500.00), + ('Laptop Air 13', 1, 6200.00), + ('Mouse Inalambrico', 2, 150.00), + ('Teclado Mecanico', 2, 320.00), + ('Disco SSD 1TB', 3, 480.00); + +INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES + (1, 'entrada', 10), + (2, 'entrada', 8), + (3, 'entrada', 50), + (4, 'entrada', 30), + (5, 'entrada', 20); + +-- Movimiento cargado dos veces por error de digitacion (misma entrada +-- de Mouse Inalambrico registrada dos veces). +INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES + (3, 'entrada', 50); + +-- 1. DELETE real (baja fisica): el movimiento duplicado no tiene +-- ningun dependiente y es un error de captura, asi que se elimina de +-- verdad, con WHERE por id especifico. +DELETE FROM movimientos +WHERE id_movimiento = 6; + +-- 2. Baja logica (no DELETE): Teclado Mecanico se descontinua, pero +-- no se puede borrar de verdad porque movimientos todavia lo +-- referencia por FOREIGN KEY. En vez de eso, se marca como inactivo. +UPDATE productos +SET activo = 0 +WHERE id_producto = 4; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: intentar el DELETE fisico de un producto que todavia +-- tiene movimientos asociados. SQLite, con +-- PRAGMA foreign_keys = ON, no lo permite. Esto es justo lo que +-- justifica usar baja logica (UPDATE activo = 0) en vez de DELETE +-- para productos. +-- DELETE FROM productos WHERE id_producto = 4; diff --git a/resoluciones/maria-montepeque/ejercicio-77/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-77/dql/consultas.sql new file mode 100644 index 00000000..a85e223f --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-77/dql/consultas.sql @@ -0,0 +1,44 @@ +.headers on +.mode column + +-- Ejercicio 77: DELETE Nivel Basico +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales (movimientos con producto y +-- categoria). +SELECT m.id_movimiento, + p.nombre_producto, + c.nombre_categoria, + m.tipo_movimiento, + m.cantidad +FROM movimientos m +JOIN productos p ON p.id_producto = m.id_producto +JOIN categorias c ON c.id_categoria = p.id_categoria; + +-- 2. Consulta con WHERE: solo los productos activos. +SELECT id_producto, nombre_producto, activo +FROM productos +WHERE activo = 1; + +-- 3. Consulta con ORDER BY: movimientos ordenados por fecha. +SELECT id_movimiento, fecha_movimiento, cantidad +FROM movimientos +ORDER BY fecha_movimiento; + +-- 4. Conteo o resumen: total de movimientos por producto. +SELECT id_producto, COUNT(*) AS total_movimientos +FROM movimientos +GROUP BY id_producto; + +-- 5. Validacion especifica de DELETE: el movimiento duplicado +-- (id_movimiento = 6) ya no existe, pero el resto de movimientos de +-- Mouse Inalambrico (id_producto = 3) sigue intacto. +SELECT id_movimiento +FROM movimientos +WHERE id_movimiento = 6; +-- Debe devolver 0 filas: el duplicado se elimino con DELETE. + +SELECT COUNT(*) AS movimientos_mouse +FROM movimientos +WHERE id_producto = 3; +-- Debe devolver 1: solo queda el movimiento real, no el duplicado. diff --git a/resoluciones/maria-montepeque/ejercicio-77/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-77/evidencias/resultados.md new file mode 100644 index 00000000..61c42a9c --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-77/evidencias/resultados.md @@ -0,0 +1,64 @@ +# Evidencias - Ejercicio 77 + +## Tema + +DELETE + +## Comandos ejecutados + +No se conto con el binario `sqlite3` en el entorno de trabajo, por lo que +la ejecucion se valido con Python (`sqlite3`), aplicando los mismos +scripts en el mismo orden: + +```bash +sqlite3 ejercicio-77.db < ddl/schema.sql +sqlite3 ejercicio-77.db < dml/inserts.sql +sqlite3 ejercicio-77.db < dql/consultas.sql +``` + +## Resultados + +Estado final tras `dml/inserts.sql` (que incluye el `DELETE` real y la +baja logica de validacion): + +```text +movimientos (5 filas, ya sin el duplicado): +id_movimiento | id_producto | tipo_movimiento | cantidad +1 | 1 | entrada | 10 +2 | 2 | entrada | 8 +3 | 3 | entrada | 50 +4 | 4 | entrada | 30 +5 | 5 | entrada | 20 + +productos: +id_producto | nombre_producto | activo +1 | Laptop Pro 14 | 1 +2 | Laptop Air 13 | 1 +3 | Mouse Inalambrico | 1 +4 | Teclado Mecanico | 0 +5 | Disco SSD 1TB | 1 +``` + +**Caso comentado verificado:** + +- `DELETE FROM productos WHERE id_producto = 4;` → `FOREIGN KEY constraint failed` (Teclado Mecanico todavia tiene un movimiento asociado). + +**5. Validacion especifica de DELETE:** + +```text +5a. Movimiento id_movimiento = 6: 0 filas -- el duplicado se elimino. +5b. Movimientos de Mouse Inalambrico (id_producto = 3): 1 -- solo el + movimiento real, no el duplicado que se borro. +``` + +## Aprendizaje + +`DELETE` con `WHERE` por id especifico elimina exactamente la fila +equivocada sin arriesgar el resto de movimientos del mismo producto. +Pero `DELETE` no siempre es la herramienta correcta: cuando otras +filas dependen de un registro por `FOREIGN KEY` (como los movimientos +de Teclado Mecanico), SQLite rechaza el `DELETE` fisico, tal como se +confirmo con el caso comentado. Ahi es donde tiene sentido la baja +logica: en vez de borrar el producto, se marca `activo = 0` con +`UPDATE`, conservando el historial de movimientos intacto mientras el +producto deja de aparecer como disponible. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/README.md new file mode 100644 index 00000000..2db43ece --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/README.md @@ -0,0 +1,82 @@ +# Solicitud SQL - Ejercicio 077: Taller de Motos + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un taller de motos recibe servicios, repuestos y mecanicos por orden +de trabajo. El cliente pide que el sistema permita corregir estados +sin borrar informacion importante. Pidio convertir esa operacion en +una base de datos que permita consultar datos, corregir estados, +registrar movimientos y sacar reportes utiles. + +## Que entendi de la solicitud + +La peticion central es sobre cuando SI y cuando NO usar `DELETE`: los +estados de una orden se corrigen siempre con `UPDATE`, y un repuesto +solo se puede quitar de una orden mientras esta sigue `recibida` +(antes de que sea parte del historial oficial del trabajo). El nivel +pedido (4, reportes y agrupaciones) exige ademas `JOIN`, `GROUP BY`, +`HAVING`, totales y ranking. El detalle completo del analisis esta en +[analisis/requerimiento.md](analisis/requerimiento.md). + +## Que tablas cree y por que + +- `clientes`: catalogo de duenos de motos. +- `motos`: catalogo de motos, cada una de un cliente. +- `ordenes_servicio`: tabla transaccional, cada trabajo sobre una + moto. Su `estado` es el dato que mas cambia y siempre se corrige con + `UPDATE`. +- `repuestos`: catalogo de repuestos disponibles. +- `detalle_repuestos`: detalle de cada orden. Aqui esta el + `UNIQUE (id_orden, id_repuesto)` que impide registrar el mismo + repuesto dos veces en la misma orden. + +## Como se relacionan + +`clientes` 1:N `motos`; `motos` 1:N `ordenes_servicio`; +`ordenes_servicio` 1:N `detalle_repuestos`; `repuestos` 1:N +`detalle_repuestos`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +3 clientes, 3 motos, 5 repuestos, 4 ordenes (2 `finalizada`, 1 +`en_reparacion`, 1 `recibida`) y 7 lineas de detalle, incluida una +linea cargada por error en una orden que todavia estaba `recibida`. +Tambien un `INSERT` comentado que reproduce el problema de duplicar +un repuesto en la misma orden y debe fallar. Detalle en +[dml/inserts.sql](dml/inserts.sql). + +## Que operaciones de mantenimiento incluyo + +En [dml/operaciones.sql](dml/operaciones.sql): un `DELETE` controlado +que corrige el repuesto agregado por error (solo posible porque esa +orden seguia `recibida`) y un `UPDATE` de estado (la orden pasa a +`en_reparacion` una vez que el mecanico empieza a trabajar). + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): que lineas de repuestos +existen (JOIN repuesto-orden-moto), en que estado esta cada orden, que +moto tiene mas ordenes de servicio, las lineas ordenadas por subtotal, +y un reporte con `GROUP BY` + `HAVING` de los repuestos mas usados, +para decidir cuales mantener siempre en stock. + +## Evidencias + +Resultados de ejecutar todo en orden, incluyendo la verificacion del +caso de duplicado y de las operaciones de mantenimiento, en +[evidencias/resultados.md](evidencias/resultados.md). + +## Como ejecutar + +```bash +sqlite3 ejercicio-077.db < ddl/schema.sql +sqlite3 ejercicio-077.db < dml/inserts.sql +sqlite3 ejercicio-077.db < dml/operaciones.sql +sqlite3 ejercicio-077.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/analisis/requerimiento.md new file mode 100644 index 00000000..4bd708ec --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/analisis/requerimiento.md @@ -0,0 +1,78 @@ +# Analisis del requerimiento - Ejercicio 077 + +## Solicitud entendida + +Un taller de motos recibe servicios, repuestos y mecanicos por orden +de trabajo. El cliente pide que el sistema permita corregir estados +sin borrar informacion importante: eso significa que el historial de +cada orden (que repuestos se usaron, en que estado quedo) no debe +desaparecer solo porque algo cambio. Se necesita una base de datos +que permita consultar datos, corregir estados, registrar movimientos +y sacar reportes utiles. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| clientes | Catalogo: quien es dueno de la moto | nombre_cliente, telefono (unico) | +| motos | Catalogo: cada moto que entra al taller | placa (unica), modelo | +| ordenes_servicio | Tabla transaccional: cada trabajo sobre una moto | descripcion, fecha_orden, estado | +| repuestos | Catalogo: cada repuesto disponible en el taller | nombre_repuesto (unico), precio_unitario | +| detalle_repuestos | Detalle de cada orden: que repuestos se usaron y cuantos | cantidad, precio_unitario | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| clientes -> motos | 1:N | Un cliente puede tener varias motos. | +| motos -> ordenes_servicio | 1:N | Una moto puede tener varias ordenes de servicio a lo largo del tiempo. | +| ordenes_servicio -> detalle_repuestos | 1:N | Una orden puede usar varios repuestos distintos. | +| repuestos -> detalle_repuestos | 1:N | Un repuesto se usa en muchas ordenes distintas. | + +## Reglas de negocio + +Esta es la regla central que pidio el cliente: corregir estados sin +borrar informacion importante. + +- Regla 1 (relaciones invalidas): toda moto debe apuntar a un cliente + real; toda orden debe apuntar a una moto real; todo detalle debe + apuntar a una orden y a un repuesto reales (`FOREIGN KEY` en + cadena). +- Regla 2 (registros repetidos): `clientes.telefono`, `motos.placa` y + `repuestos.nombre_repuesto` no se repiten (`UNIQUE`); un repuesto no + puede aparecer dos veces como linea separada en la misma orden + (`UNIQUE (id_orden, id_repuesto)`). +- Regla 3 (valores fuera de rango): `detalle_repuestos.cantidad` + siempre mayor que 0; `repuestos.precio_unitario` y + `detalle_repuestos.precio_unitario` nunca negativos (`CHECK`). +- Regla 4: una orden nace `'recibida'` y avanza a `'en_reparacion'`, + `'finalizada'` o `'cancelada'` (`CHECK`); se corrige siempre con + `UPDATE`, nunca se borra una orden para "reiniciarla". +- Regla 5: un repuesto solo se puede quitar de una orden con `DELETE` + mientras la orden sigue `'recibida'` (todavia no se empezo a + trabajar). Una vez que la orden pasa a `'en_reparacion'` o + `'finalizada'`, sus repuestos ya son parte del historial oficial del + trabajo y no se borran; si algo estuvo mal, se corrige con `UPDATE` + del estado de la orden, no eliminando el detalle. + +## Supuestos + +- El cliente no detallo si un mismo repuesto puede tener precio + distinto en ordenes distintas (por ejemplo, si sube el precio de + lista); se guarda `precio_unitario` tambien en `detalle_repuestos` + para conservar el precio real que se cobro en esa orden. +- No se detallo si una moto puede cambiar de dueno; se asume que no, + para el alcance de este nivel. +- Se asume que "corregir estados" se refiere principalmente al estado + de la orden de servicio, que es el dato que mas cambia durante el + proceso de reparacion. + +## Preguntas que responde la base de datos + +1. Que repuestos se usaron, en que orden y en que moto. +2. Que ordenes estan recibidas, en reparacion, finalizadas o + canceladas. +3. Que moto tiene mas ordenes de servicio (ranking de actividad). +4. Como se ordenan las lineas de repuestos por su subtotal. +5. Que repuestos son los mas usados, para decidir cuales mantener + siempre en stock. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/ddl/schema.sql new file mode 100644 index 00000000..616de18c --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/ddl/schema.sql @@ -0,0 +1,53 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 077: Taller de Motos +-- Modelo: clientes -> motos (1:N); motos -> ordenes_servicio (1:N); +-- ordenes_servicio + repuestos -> detalle_repuestos (1:N cada una). + +CREATE TABLE clientes ( + id_cliente INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_cliente TEXT NOT NULL, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE motos ( + id_moto INTEGER PRIMARY KEY AUTOINCREMENT, + id_cliente INTEGER NOT NULL, + placa TEXT NOT NULL UNIQUE, + modelo TEXT NOT NULL, + + FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente) +); + +CREATE TABLE repuestos ( + id_repuesto INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_repuesto TEXT NOT NULL UNIQUE, + precio_unitario REAL NOT NULL CHECK (precio_unitario >= 0) +); + +-- ordenes_servicio: el estado se corrige siempre con UPDATE. No se +-- borra una orden para "reiniciarla", tal como pidio el cliente. +CREATE TABLE ordenes_servicio ( + id_orden INTEGER PRIMARY KEY AUTOINCREMENT, + id_moto INTEGER NOT NULL, + descripcion TEXT NOT NULL, + fecha_orden TEXT NOT NULL DEFAULT (date('now')), + estado TEXT NOT NULL DEFAULT 'recibida' + CHECK (estado IN ('recibida', 'en_reparacion', 'finalizada', 'cancelada')), + + FOREIGN KEY (id_moto) REFERENCES motos (id_moto) +); + +-- detalle_repuestos: el UNIQUE compuesto impide que un repuesto quede +-- registrado dos veces como linea separada en la misma orden. +CREATE TABLE detalle_repuestos ( + id_detalle INTEGER PRIMARY KEY AUTOINCREMENT, + id_orden INTEGER NOT NULL, + id_repuesto INTEGER NOT NULL, + cantidad INTEGER NOT NULL CHECK (cantidad > 0), + precio_unitario REAL NOT NULL CHECK (precio_unitario >= 0), + + FOREIGN KEY (id_orden) REFERENCES ordenes_servicio (id_orden), + FOREIGN KEY (id_repuesto) REFERENCES repuestos (id_repuesto), + UNIQUE (id_orden, id_repuesto) +); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/diagramas/diagrama-er.svg new file mode 100644 index 00000000..3dbe491d --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/diagramas/diagrama-er.svg @@ -0,0 +1,56 @@ + + + + + clientes + id_cliente PK + nombre_cliente + telefono UNIQUE + + + motos + id_moto PK + id_cliente FK + placa UNIQUE, modelo + + + ordenes_servicio + id_orden PK + id_moto FK + estado CHECK + + + detalle_repuestos + id_detalle PK + id_orden FK, id_repuesto FK + cantidad, precio_unitario + UNIQUE(orden, repuesto) + + + repuestos + id_repuesto PK + nombre_repuesto UNIQUE + precio_unitario + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/inserts.sql new file mode 100644 index 00000000..24c5f447 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/inserts.sql @@ -0,0 +1,58 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 077: Taller de Motos +-- Datos base: 3 clientes, 3 motos, 5 repuestos, 4 ordenes (2 +-- finalizadas, 1 en reparacion, 1 recibida con una linea cargada por +-- error) y sus lineas de detalle. + +INSERT INTO clientes (nombre_cliente, telefono) VALUES + ('Manuel Estrada', '5555-6001'), + ('Alejandra Chinchilla', '5555-6002'), + ('Byron Xicay', '5555-6003'); + +INSERT INTO motos (id_cliente, placa, modelo) VALUES + (1, 'P-001ABC', 'Yamaha FZ 150'), + (2, 'P-002DEF', 'Honda CB 190'), + (3, 'P-003GHI', 'Suzuki GN 125'); + +INSERT INTO repuestos (nombre_repuesto, precio_unitario) VALUES + ('Aceite Motor', 85.00), + ('Filtro Aire', 45.00), + ('Pastillas Freno', 120.00), + ('Cadena Transmision', 210.00), + ('Bujia', 30.00); + +-- Orden 1: moto de Manuel, finalizada. +INSERT INTO ordenes_servicio (id_moto, descripcion, estado) VALUES + (1, 'Cambio de aceite y filtro', 'finalizada'); +INSERT INTO detalle_repuestos (id_orden, id_repuesto, cantidad, precio_unitario) VALUES + (1, 1, 1, 85.00), + (1, 2, 1, 45.00); + +-- Orden 2: moto de Alejandra, finalizada. +INSERT INTO ordenes_servicio (id_moto, descripcion, estado) VALUES + (2, 'Cambio de pastillas de freno y bujia', 'finalizada'); +INSERT INTO detalle_repuestos (id_orden, id_repuesto, cantidad, precio_unitario) VALUES + (2, 3, 2, 120.00), + (2, 5, 1, 30.00); + +-- Orden 3: moto de Byron, en reparacion. +INSERT INTO ordenes_servicio (id_moto, descripcion, estado) VALUES + (3, 'Cambio de cadena de transmision', 'en_reparacion'); +INSERT INTO detalle_repuestos (id_orden, id_repuesto, cantidad, precio_unitario) VALUES + (3, 4, 1, 210.00); + +-- Orden 4: moto de Manuel otra vez, recibida (todavia no se empieza a +-- trabajar). Se agrego por error una Cadena Transmision que esta moto +-- no necesita; se corrige con DELETE en dml/operaciones.sql mientras +-- la orden sigue 'recibida'. +INSERT INTO ordenes_servicio (id_moto, descripcion, estado) VALUES + (1, 'Revision de frenos', 'recibida'); +INSERT INTO detalle_repuestos (id_orden, id_repuesto, cantidad, precio_unitario) VALUES + (4, 3, 1, 120.00), + (4, 4, 1, 210.00); + +-- Caso comentado que debe fallar (queda comentado): registrar de +-- nuevo Pastillas Freno como otra linea separada en la orden 2, +-- exactamente el problema que este UNIQUE esta disenado para evitar. +-- INSERT INTO detalle_repuestos (id_orden, id_repuesto, cantidad, precio_unitario) VALUES (2, 3, 1, 120.00); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/operaciones.sql new file mode 100644 index 00000000..8de79979 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dml/operaciones.sql @@ -0,0 +1,25 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 077: Taller de Motos +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 DELETE controlado: la orden 4 todavia esta 'recibida' (no se +-- empezo a trabajar), asi que es seguro corregir la Cadena +-- Transmision que se agrego por error; esta moto solo necesitaba +-- revision de frenos. +DELETE FROM detalle_repuestos +WHERE id_orden = 4 AND id_repuesto = 4; + +-- 1 UPDATE de estado: la orden 4 pasa a 'en_reparacion' porque el +-- mecanico ya empezo a trabajar en ella. +UPDATE ordenes_servicio +SET estado = 'en_reparacion' +WHERE id_orden = 4 AND estado = 'recibida'; + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar un repuesto de la orden 1, que ya esta 'finalizada' (parte +-- del historial oficial del trabajo). El DELETE de arriba solo se +-- aplico mientras la orden 4 seguia 'recibida', por diseno: el +-- cliente pidio poder corregir estados sin borrar informacion +-- importante, y una orden finalizada ya es informacion importante. +-- DELETE FROM detalle_repuestos WHERE id_orden = 1 AND id_repuesto = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dql/consultas.sql new file mode 100644 index 00000000..75cac86a --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/dql/consultas.sql @@ -0,0 +1,50 @@ +.headers on +.mode column + +-- Ejercicio 077: Taller de Motos +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: todas las lineas de +-- repuestos con su repuesto, su orden y su moto. +SELECT dr.id_detalle, + r.nombre_repuesto, + o.id_orden, + m.placa, + dr.cantidad, + dr.precio_unitario, + (dr.cantidad * dr.precio_unitario) AS subtotal +FROM detalle_repuestos dr +JOIN repuestos r ON r.id_repuesto = dr.id_repuesto +JOIN ordenes_servicio o ON o.id_orden = dr.id_orden +JOIN motos m ON m.id_moto = o.id_moto; + +-- 2. Que ordenes estan recibidas, en reparacion, finalizadas o +-- canceladas. +SELECT id_orden, id_moto, estado +FROM ordenes_servicio +ORDER BY estado; + +-- 3. Que moto tiene mas ordenes de servicio (ranking de actividad). +SELECT m.placa, m.modelo, COUNT(*) AS total_ordenes +FROM motos m +JOIN ordenes_servicio o ON o.id_moto = m.id_moto +GROUP BY m.id_moto, m.placa, m.modelo +ORDER BY total_ordenes DESC, m.placa; + +-- 4. Lineas de repuestos ordenadas por subtotal, de mayor a menor. +SELECT r.nombre_repuesto, dr.cantidad, dr.precio_unitario, + (dr.cantidad * dr.precio_unitario) AS subtotal +FROM detalle_repuestos dr +JOIN repuestos r ON r.id_repuesto = dr.id_repuesto +ORDER BY subtotal DESC; + +-- 5. Reporte para decision de negocio: repuestos mas usados por +-- cantidad total, para decidir cuales mantener siempre en stock +-- (GROUP BY + HAVING). +SELECT r.nombre_repuesto, + SUM(dr.cantidad) AS unidades_usadas +FROM detalle_repuestos dr +JOIN repuestos r ON r.id_repuesto = dr.id_repuesto +GROUP BY r.id_repuesto, r.nombre_repuesto +HAVING SUM(dr.cantidad) >= 1 +ORDER BY unidades_usadas DESC, r.nombre_repuesto; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/evidencias/resultados.md new file mode 100644 index 00000000..0a1fd4d4 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-077/evidencias/resultados.md @@ -0,0 +1,62 @@ +# Evidencias - Solicitudes SQL - Ejercicio 077 (Taller de Motos) + +## Comandos ejecutados + +No se conto con el binario `sqlite3` en el entorno de trabajo, por lo que +la ejecucion se valido con Python (`sqlite3`), aplicando los mismos +scripts en el mismo orden: + +```bash +sqlite3 ejercicio-077.db < ddl/schema.sql +sqlite3 ejercicio-077.db < dml/inserts.sql +sqlite3 ejercicio-077.db < dml/operaciones.sql +sqlite3 ejercicio-077.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 3 clientes, 3 motos, 5 repuestos, +4 ordenes (2 `finalizada`, 1 `en_reparacion`, 1 `recibida`) y 7 lineas +de detalle (incluye la Cadena Transmision agregada por error en la +orden 4). + +**Caso comentado verificado:** + +- `INSERT INTO detalle_repuestos (id_orden, id_repuesto, ...) VALUES (2, 3, ...);` (segunda linea de Pastillas Freno en la orden 2) → `UNIQUE constraint failed: detalle_repuestos.id_orden, detalle_repuestos.id_repuesto`. + +**3. Moto con mas ordenes de servicio:** + +```text +placa modelo total_ordenes +P-001ABC Yamaha FZ 150 2 +P-002DEF Honda CB 190 1 +P-003GHI Suzuki GN 125 1 +``` + +**5. Repuestos mas usados (para decidir cuales mantener en stock):** + +```text +nombre_repuesto unidades_usadas +Pastillas Freno 3 +Aceite Motor 1 +Bujia 1 +Cadena Transmision 1 +Filtro Aire 1 +``` + +## Operaciones de mantenimiento verificadas + +- **DELETE controlado**: se elimino la Cadena Transmision agregada por error en la orden 4, mientras esta seguia `recibida` (todavia sin trabajo empezado). La orden 4 quedo solo con Pastillas Freno. +- `UPDATE ordenes_servicio SET estado = 'en_reparacion' WHERE id_orden = 4 ...;` → la orden de revision de frenos de Manuel Estrada paso a trabajo en curso. + +## Aprendizaje + +El `UNIQUE (id_orden, id_repuesto)` en `detalle_repuestos` evita +registrar el mismo repuesto dos veces como linea separada en la misma +orden. El `DELETE` controlado de este ejercicio solo se aplica +mientras una orden sigue `recibida`: una vez que pasa a +`en_reparacion` o `finalizada`, sus repuestos ya son parte del +historial oficial del trabajo y no se borran, tal como pidio el +cliente ("corregir estados sin borrar informacion importante"). Los +estados se corrigen siempre con `UPDATE`, nunca eliminando y +recreando la orden.