From 1d1bcc07206021b216442ed627e354731c19b6d7 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 15:51:31 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 70 --- .../maria-montepeque/ejercicio-70/README.md | 99 +++++++++++++++++ .../ejercicio-70/ddl/schema.sql | 101 ++++++++++++++++++ .../ejercicio-70/dml/inserts.sql | 13 +++ .../ejercicio-70/dql/consultas.sql | 79 ++++++++++++++ .../ejercicio-70/evidencias/resultados.md | 79 ++++++++++++++ 5 files changed, 371 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-70/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-70/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-70/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-70/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-70/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-70/README.md b/resoluciones/maria-montepeque/ejercicio-70/README.md new file mode 100644 index 00000000..0debf643 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-70/README.md @@ -0,0 +1,99 @@ +# Ejercicio 70: DROP Nivel Aplicado + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +DROP + +## Descripcion del problema + +Un torneo de videojuegos registra equipos, jugadores y partidas con su +puntaje. Los resultados de la primera jornada llegaron en una tabla +temporal de importacion que, una vez migrada a la tabla definitiva, ya +no sirve para nada. Ademas, el torneo necesita generar un reporte +oficial de posiciones (caso de negocio con validacion final, propio del +nivel aplicado) usando objetos de apoyo que se descartan despues de +usarlos. + +## Tablas y relaciones + +- `equipos`: catalogo de equipos participantes, tabla definitiva y + permanente. +- `jugadores`: catalogo de jugadores, cada uno ligado a un equipo. +- `partidas`: relaciona un equipo local con un equipo visitante en una + fecha, con puntaje y estado. `equipos` 1—N `jugadores`; `equipos` + 1—N `partidas` (dos veces: como local y como visitante). + +## Uso de DROP + +En `ddl/schema.sql`, despues de crear las 3 tablas y migrar los +resultados de la primera jornada desde una tabla temporal de +importacion: + +1. `DROP TABLE partidas_temporal;`: elimina la tabla temporal una vez + que sus datos ya se copiaron a `partidas`. El riesgo real de `DROP` + se explica en un comentario: ejecutarlo antes de migrar los datos + habria perdido esos resultados para siempre. + +En `dql/consultas.sql` (consulta 5), el caso de negocio del nivel +aplicado: + +2. `CREATE INDEX idx_partidas_estado` y + `CREATE VIEW vista_tabla_posiciones ...`: se arma un reporte oficial + de la jornada (tabla de posiciones por equipo). Se genera el reporte + consultando la vista, y una vez entregado, `DROP VIEW` y + `DROP INDEX` los eliminan porque ya cumplieron su proposito puntual. + Ninguno de los dos afecta los datos de `equipos`, `jugadores` ni + `partidas`. + +La ultima parte de la consulta 5 confirma, consultando `sqlite_master`, +que la tabla temporal, la vista y el indice ya no existen, mientras que +las 5 partidas reales (con las que se armo el reporte) siguen +disponibles. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `jugadores.id_equipo`, `partidas.id_equipo_local`, + `partidas.id_equipo_visitante`. +- `NOT NULL` en todas las columnas obligatorias. +- `UNIQUE`: `equipos.nombre_equipo`. +- `CHECK`: `partidas.estado IN (...)`, `puntaje_local >= 0`, + `puntaje_visitante >= 0`. +- `DEFAULT` en `partidas.puntaje_local`, `puntaje_visitante` y + `estado`. +- `PRAGMA foreign_keys = ON;` activado al inicio de cada script. + +## Caso que falla / no recomendable (comentado en `ddl/schema.sql`) + +`DROP TABLE equipos;` falla porque `jugadores` y `partidas` todavia +tienen filas que dependen de `equipos` por `FOREIGN KEY`, y SQLite (con +`PRAGMA foreign_keys = ON`) no permite eliminar una tabla que sigue +siendo referenciada. Se valido con Python (`sqlite3`): lanza +`IntegrityError: FOREIGN KEY constraint failed`. Para poder eliminar +`equipos` habria que primero eliminar o reasignar los jugadores y +partidas que dependen de el. + +## 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: 4 equipos, 6 jugadores, 5 partidas (3 jugadas, 2 + programadas). +- Reporte final: Dragones del Norte lidera la tabla de posiciones con + 2 partidas ganadas de 2 jugadas. + +## Como ejecutar + +```bash +sqlite3 ejercicio-70.db < ddl/schema.sql +sqlite3 ejercicio-70.db < dml/inserts.sql +sqlite3 ejercicio-70.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-70/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-70/ddl/schema.sql new file mode 100644 index 00000000..e18c0c87 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-70/ddl/schema.sql @@ -0,0 +1,101 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 70: DROP Nivel Aplicado +-- Tema central: DROP +-- Contexto: torneo de videojuegos, partidas y puntajes por equipo. + +-- Tablas principales, permanentes. +CREATE TABLE equipos ( + id_equipo INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_equipo TEXT NOT NULL UNIQUE, + region TEXT NOT NULL +); + +CREATE TABLE jugadores ( + id_jugador INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + id_equipo INTEGER NOT NULL, + + FOREIGN KEY (id_equipo) REFERENCES equipos (id_equipo) +); + +CREATE TABLE partidas ( + id_partida INTEGER PRIMARY KEY AUTOINCREMENT, + id_equipo_local INTEGER NOT NULL, + id_equipo_visitante INTEGER NOT NULL, + fecha_partida TEXT NOT NULL, + puntaje_local INTEGER NOT NULL DEFAULT 0 CHECK (puntaje_local >= 0), + puntaje_visitante INTEGER NOT NULL DEFAULT 0 CHECK (puntaje_visitante >= 0), + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'jugada', 'cancelada')), + + FOREIGN KEY (id_equipo_local) REFERENCES equipos (id_equipo), + FOREIGN KEY (id_equipo_visitante) REFERENCES equipos (id_equipo) +); + +-- Tabla temporal de importacion: el sistema del torneo exporto los +-- resultados de la primera jornada en un formato plano, sin las +-- restricciones finales, y se usa solo para migrar esos resultados a +-- `partidas`. +CREATE TABLE partidas_temporal ( + equipo_local_bruto TEXT, + equipo_visitante_bruto TEXT, + fecha_bruta TEXT, + marcador_local INTEGER, + marcador_visitante INTEGER +); + +INSERT INTO equipos (nombre_equipo, region) VALUES + ('Dragones del Norte', 'Norte'), + ('Lobos del Sur', 'Sur'), + ('Halcones del Centro', 'Centro'), + ('Tigres del Oeste', 'Oeste'); + +INSERT INTO jugadores (nombre, id_equipo) VALUES + ('Kevin Us', 1), + ('Diana Perez', 1), + ('Oscar Tzul', 2), + ('Melissa Ordonez', 3), + ('Sergio Batz', 4); + +INSERT INTO partidas_temporal (equipo_local_bruto, equipo_visitante_bruto, fecha_bruta, marcador_local, marcador_visitante) VALUES + ('Dragones del Norte', 'Lobos del Sur', '2026-08-01', 3, 1), + ('Halcones del Centro', 'Tigres del Oeste', '2026-08-02', 2, 2); + +-- Migracion: se traduce cada nombre bruto a su id_equipo real y el +-- resultado queda como partida ya jugada. +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida, puntaje_local, puntaje_visitante, estado) +SELECT + eloc.id_equipo, + evis.id_equipo, + pt.fecha_bruta, + pt.marcador_local, + pt.marcador_visitante, + 'jugada' +FROM partidas_temporal pt +JOIN equipos eloc ON eloc.nombre_equipo = pt.equipo_local_bruto +JOIN equipos evis ON evis.nombre_equipo = pt.equipo_visitante_bruto; + +-- Partidas de la siguiente jornada, cargadas directo en la tabla +-- definitiva (todavia no se jugaron). +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida, estado) VALUES + (2, 3, '2026-08-08', 'programada'), + (4, 1, '2026-08-08', 'programada'); + +-- DROP TABLE: la tabla de importacion ya cumplio su proposito (los +-- resultados ya viven en `partidas`) y se elimina para no dejar datos +-- duplicados. Este es el riesgo de DROP: si se ejecutara antes de +-- migrar los datos, esos resultados se perderian para siempre. +DROP TABLE partidas_temporal; + +-- El ciclo completo crear -> usar para el reporte oficial -> eliminar +-- de un indice y una vista de apoyo (tabla de posiciones) se hace en +-- dql/consultas.sql (consulta 5), despues de que ya existen datos +-- suficientes para que el reporte tenga sentido. + +-- Caso que debe fallar / no recomendable (queda comentado): intentar +-- eliminar una tabla que todavia esta referenciada por FOREIGN KEY +-- desde otra tabla con filas. SQLite, con PRAGMA foreign_keys = ON, no +-- permite este DROP mientras existan jugadores o partidas que dependan +-- de equipos: hay que eliminar o reasignar esas filas primero. +-- DROP TABLE equipos; diff --git a/resoluciones/maria-montepeque/ejercicio-70/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-70/dml/inserts.sql new file mode 100644 index 00000000..4b7061ad --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-70/dml/inserts.sql @@ -0,0 +1,13 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 70: DROP Nivel Aplicado +-- Se ejecuta despues de que ddl/schema.sql migro los resultados de la +-- primera jornada y elimino la tabla temporal. Aqui solo se agregan +-- registros nuevos directamente a las tablas definitivas. + +INSERT INTO jugadores (nombre, id_equipo) VALUES + ('Paola Coy', 2); + +-- Resultado real de una partida que estaba programada: ya se jugo. +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida, puntaje_local, puntaje_visitante, estado) VALUES + (1, 3, '2026-08-15', 4, 2, 'jugada'); diff --git a/resoluciones/maria-montepeque/ejercicio-70/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-70/dql/consultas.sql new file mode 100644 index 00000000..da2e4294 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-70/dql/consultas.sql @@ -0,0 +1,79 @@ +.headers on +.mode column + +-- Ejercicio 70: DROP Nivel Aplicado +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales (partidas con nombre de +-- equipo local y visitante). +SELECT p.id_partida, + eloc.nombre_equipo AS equipo_local, + evis.nombre_equipo AS equipo_visitante, + p.fecha_partida, + p.puntaje_local, + p.puntaje_visitante, + p.estado +FROM partidas p +JOIN equipos eloc ON eloc.id_equipo = p.id_equipo_local +JOIN equipos evis ON evis.id_equipo = p.id_equipo_visitante; + +-- 2. Consulta con WHERE: partidas ya jugadas. +SELECT id_partida, fecha_partida, puntaje_local, puntaje_visitante +FROM partidas +WHERE estado = 'jugada'; + +-- 3. Consulta con ORDER BY: partidas ordenadas por fecha. +SELECT id_partida, fecha_partida, estado +FROM partidas +ORDER BY fecha_partida; + +-- 4. Conteo o resumen: total de partidas por estado. +SELECT estado, COUNT(*) AS total +FROM partidas +GROUP BY estado; + +-- 5. Caso de negocio con reporte final (nivel aplicado): se crea un +-- indice de apoyo y una vista de tabla de posiciones, se usan para +-- generar el reporte oficial de la jornada, y se eliminan enseguida +-- porque solo servian para esa validacion puntual. +CREATE INDEX idx_partidas_estado ON partidas (estado); + +CREATE VIEW vista_tabla_posiciones AS + SELECT + e.id_equipo, + e.nombre_equipo, + COUNT(*) AS partidas_jugadas, + SUM( + CASE + WHEN (p.id_equipo_local = e.id_equipo AND p.puntaje_local > p.puntaje_visitante) + OR (p.id_equipo_visitante = e.id_equipo AND p.puntaje_visitante > p.puntaje_local) + THEN 1 ELSE 0 + END + ) AS partidas_ganadas + FROM equipos e + JOIN partidas p + ON (p.id_equipo_local = e.id_equipo OR p.id_equipo_visitante = e.id_equipo) + AND p.estado = 'jugada' + GROUP BY e.id_equipo, e.nombre_equipo; + +-- Reporte oficial de la jornada: tabla de posiciones. +SELECT nombre_equipo, partidas_jugadas, partidas_ganadas +FROM vista_tabla_posiciones +ORDER BY partidas_ganadas DESC, nombre_equipo; + +-- Una vez entregado el reporte, se eliminan la vista y el indice: ya +-- cumplieron su proposito puntual. +DROP VIEW vista_tabla_posiciones; +DROP INDEX idx_partidas_estado; + +-- Validacion especifica de DROP: ni la tabla temporal de la migracion +-- (eliminada en ddl/schema.sql) ni la vista/indice del reporte (recien +-- eliminados) existen ya en el catalogo, pero los datos de partidas y +-- equipos que se usaron para generarlos siguen intactos. +SELECT name, type +FROM sqlite_master +WHERE name IN ('partidas_temporal', 'vista_tabla_posiciones', 'idx_partidas_estado'); +-- Debe devolver 0 filas: los 3 objetos se eliminaron con DROP. + +SELECT COUNT(*) AS total_partidas FROM partidas; +-- Las partidas reales (con las que se armo el reporte) siguen ahi. diff --git a/resoluciones/maria-montepeque/ejercicio-70/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-70/evidencias/resultados.md new file mode 100644 index 00000000..7c749729 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-70/evidencias/resultados.md @@ -0,0 +1,79 @@ +# Evidencias - Ejercicio 70 + +## Tema + +DROP + +## 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-70.db < ddl/schema.sql +sqlite3 ejercicio-70.db < dml/inserts.sql +sqlite3 ejercicio-70.db < dql/consultas.sql +``` + +## Resultados + +Estado justo despues de `ddl/schema.sql` (migracion de la primera +jornada desde `partidas_temporal` + `DROP TABLE`): + +```text +equipos: 4 registros (Dragones del Norte, Lobos del Sur, Halcones del Centro, Tigres del Oeste) +partidas: 4 (2 jugadas migradas, 2 programadas) +sqlite_master (partidas_temporal): [] -- ya no existe +``` + +Caso que debe fallar (comentado en `ddl/schema.sql`): + +```text +DROP TABLE equipos; +Fallo como se esperaba: FOREIGN KEY constraint failed +``` + +Estado final (despues de `dml/inserts.sql`), 5 partidas en total: + +```text +estado total +jugada 3 +programada 2 +``` + +Reporte oficial de la jornada (consulta 5, con la vista +`vista_tabla_posiciones` antes de eliminarla): + +```text +nombre_equipo partidas_jugadas partidas_ganadas +Dragones del Norte 2 2 +Halcones del Centro 2 0 +Lobos del Sur 1 0 +Tigres del Oeste 1 0 +``` + +Validacion especifica de DROP (consulta 5, despues de `DROP VIEW` y +`DROP INDEX`): + +```text +5a. sqlite_master para partidas_temporal, vista_tabla_posiciones e + idx_partidas_estado: sin filas -- los 3 objetos se eliminaron. +5b. total_partidas: 5 -- los datos reales con los que se armo el + reporte siguen intactos. +``` + +## Aprendizaje + +Ademas de lo visto en el nivel intermedio (migrar y eliminar una tabla +temporal, o crear y eliminar una vista de apoyo), este ejercicio de +nivel aplicado agrego un caso de negocio completo: un indice y una +vista se crean, se usan para generar un reporte oficial (la tabla de +posiciones del torneo) y se eliminan enseguida porque ya cumplieron su +proposito puntual. `DROP` no es solo "borrar algo que sobra": tambien +es parte normal del ciclo de vida de un objeto de apoyo que se crea +para una tarea especifica y se descarta una vez terminada, sin tocar +los datos reales (`equipos`, `jugadores`, `partidas`) que ese objeto +consultaba. El caso comentado (`DROP TABLE equipos`) confirma que las +`FOREIGN KEY` protegen a las tablas permanentes de un DROP accidental +mientras tengan filas dependientes. From 44c66104909d6a9deb99c97d35a4418bdeff8633 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 15:51:32 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 070 (soldadura industrial) --- .../solicitudes-sql/ejercicio-070/README.md | 91 +++++++++++++++++ .../ejercicio-070/analisis/requerimiento.md | 74 ++++++++++++++ .../ejercicio-070/ddl/schema.sql | 58 +++++++++++ .../ejercicio-070/diagramas/.gitkeep | 0 .../ejercicio-070/diagramas/diagrama-er.svg | 58 +++++++++++ .../ejercicio-070/dml/inserts.sql | 54 +++++++++++ .../ejercicio-070/dml/operaciones.sql | 44 +++++++++ .../ejercicio-070/dql/consultas.sql | 54 +++++++++++ .../ejercicio-070/evidencias/resultados.md | 97 +++++++++++++++++++ 9 files changed, 530 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/README.md new file mode 100644 index 00000000..4ac606e1 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/README.md @@ -0,0 +1,91 @@ +# Solicitud SQL - Ejercicio 070: Soldadura Industrial + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un taller de soldadura industrial controla ordenes, materiales, +tecnicos, inspecciones y costos. El cliente pidio detectar tres tipos +de error: registros repetidos, relaciones invalidas y valores fuera de +rango. Ademas queria poder consultar datos, corregir estados, +registrar movimientos y sacar reportes utiles (no solo guardar texto). + +## Que entendi de la solicitud + +El nivel pedido (4, reportes y agrupaciones) exige, ademas del modelo +base con `CHECK`/`FOREIGN KEY`/`UNIQUE`, consultas con `JOIN`, +`GROUP BY`, `HAVING`, totales y ranking. El detalle completo del +analisis (entidades, relaciones, reglas de negocio y supuestos) esta en +[analisis/requerimiento.md](analisis/requerimiento.md). + +## Que tablas cree y por que + +- `clientes`: catalogo de quien encarga cada orden de trabajo. +- `tecnicos`: catalogo de quien ejecuta el trabajo de soldadura. +- `ordenes`: tabla transaccional, ligada a un cliente y a un tecnico + reales (`FOREIGN KEY`). +- `materiales`: detalle de costos de cada orden (`cantidad` y + `costo_unitario`, ambos protegidos con `CHECK` para que nunca queden + fuera de rango). +- `inspecciones`: historico de calidad de cada orden; nunca se borra, + solo se agrega una inspeccion nueva cuando se corrige algo. + +Aqui es donde se detectan y corrigen los tres errores que pidio el +cliente: + +- Registros repetidos -> `UNIQUE` en `clientes.nombre_cliente`, + `clientes.telefono` y `tecnicos.nombre_tecnico`. +- Relaciones invalidas -> `FOREIGN KEY` en cadena (orden -> cliente, + orden -> tecnico, material -> orden, inspeccion -> orden). +- Valores fuera de rango -> `CHECK` (`cantidad > 0`, + `costo_unitario >= 0`, `estado`/`resultado` dentro de una lista + cerrada de valores). + +## Como se relacionan + +`clientes` 1:N `ordenes`, `tecnicos` 1:N `ordenes`, `ordenes` 1:N +`materiales`, `ordenes` 1:N `inspecciones`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +3 clientes, 3 tecnicos, 4 ordenes, 6 materiales (uno duplicado a +proposito por error de digitacion) y 3 inspecciones, ademas de tres +`INSERT` comentados que deben fallar (uno por cada tipo de error que +pidio detectar el cliente). Detalle en +[dml/inserts.sql](dml/inserts.sql). + +## Que operaciones de mantenimiento incluyo + +En [dml/operaciones.sql](dml/operaciones.sql): un `UPDATE` de estado +(una orden que pasa de `pendiente` a `en_proceso`), una nueva +inspeccion que corrige una rechazada sin borrar el historico original, +y un `DELETE` que elimina el material duplicado, permitido solo porque +esa orden todavia no tenia ninguna inspeccion asociada. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): que ordenes existen (JOIN +cliente-tecnico), que ordenes siguen abiertas, que tecnico tiene mas +ordenes asignadas (ranking), los materiales ordenados por costo total, +y un reporte con `GROUP BY` + `HAVING` de que ordenes tienen +inspecciones rechazadas, para saber cuales corregir antes de +entregarlas. + +## Evidencias + +Resultados de ejecutar todo en orden, incluyendo la verificacion de los +tres casos de error y de las operaciones de mantenimiento, en +[evidencias/resultados.md](evidencias/resultados.md). + +## Como ejecutar + +```bash +sqlite3 ejercicio-070.db < ddl/schema.sql +sqlite3 ejercicio-070.db < dml/inserts.sql +sqlite3 ejercicio-070.db < dml/operaciones.sql +sqlite3 ejercicio-070.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/analisis/requerimiento.md new file mode 100644 index 00000000..651c9b04 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/analisis/requerimiento.md @@ -0,0 +1,74 @@ +# Analisis del requerimiento - Ejercicio 070 + +## Solicitud entendida + +Un taller de soldadura industrial controla ordenes de trabajo, +materiales usados, tecnicos asignados e inspecciones de calidad. El +cliente quiere detectar tres tipos de error: registros repetidos, +relaciones invalidas y valores fuera de rango. Ademas quiere poder +consultar datos, corregir estados, registrar movimientos y sacar +reportes utiles (no solo guardar texto). + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| clientes | Catalogo: quien encarga cada orden de trabajo | nombre_cliente (unico), telefono (unico) | +| tecnicos | Catalogo: quien ejecuta el trabajo de soldadura | nombre_tecnico (unico), especialidad | +| ordenes | Tabla transaccional: trabajo de soldadura para un cliente, a cargo de un tecnico | descripcion, estado, fecha_orden | +| materiales | Detalle de la orden: cada material usado en un trabajo, con su costo | nombre_material, cantidad, costo_unitario | +| inspecciones | Historico de calidad: cada inspeccion de una orden se conserva con su fecha y resultado | resultado, fecha_inspeccion | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| clientes -> ordenes | 1:N | Un cliente puede encargar varias ordenes de trabajo. | +| tecnicos -> ordenes | 1:N | Un tecnico puede tener asignadas varias ordenes. | +| ordenes -> materiales | 1:N | Una orden puede usar varios materiales distintos. | +| ordenes -> inspecciones | 1:N | Una orden puede pasar por varias inspecciones (la primera puede rechazar, y se vuelve a inspeccionar despues de corregir). | + +## Reglas de negocio + +Cada regla ataca uno de los tres errores que el cliente quiere +detectar: + +- Regla 1 (relaciones invalidas): toda orden debe apuntar a un cliente + y a un tecnico reales; todo material y toda inspeccion deben apuntar + a una orden real (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `nombre_cliente`, `telefono` y + `nombre_tecnico` no se repiten (`UNIQUE`). +- Regla 3 (valores fuera de rango): `materiales.cantidad` debe ser + mayor que 0 y `materiales.costo_unitario` no puede ser negativo + (`CHECK`). +- Regla 4: `ordenes.estado` solo puede ser `pendiente`, `en_proceso`, + `finalizada` o `cancelada` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: `inspecciones.resultado` solo puede ser `aprobada`, + `rechazada` o `pendiente` (`CHECK`). +- Regla 6: Solo se permite `DELETE` de un material cuando es un + registro duplicado por error de captura y la orden todavia no fue + inspeccionada; nunca se borra un material de una orden ya + inspeccionada, porque cambiaria el costo que ya se audito. + +## Supuestos + +- El cliente no detallo si el costo de la orden se calcula + automaticamente; se asume que el costo total de una orden es la suma + de `cantidad * costo_unitario` de sus materiales, y se responde con + una consulta (no se guarda como columna redundante). +- No se detallo si un tecnico puede tener varias especialidades; se + asume una especialidad principal por tecnico para el alcance de este + modelo. +- Se asume que una orden `cancelada` puede seguir teniendo materiales e + inspecciones en su historico (no se elimina nada al cancelarla, solo + cambia su estado). + +## Preguntas que responde la base de datos + +1. Que ordenes existen, con que cliente y que tecnico. +2. Que ordenes estan pendientes, en proceso, finalizadas o canceladas. +3. Que tecnico tiene mas ordenes asignadas (ranking de actividad). +4. Como se ordenan los materiales por costo total (cantidad x costo + unitario). +5. Que ordenes tienen inspecciones rechazadas, para saber cuales + necesitan corregirse antes de entregarse al cliente. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/ddl/schema.sql new file mode 100644 index 00000000..a23c2239 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/ddl/schema.sql @@ -0,0 +1,58 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 070: Soldadura Industrial +-- Modelo: clientes -> ordenes (1:N), tecnicos -> ordenes (1:N), y +-- ordenes -> materiales / ordenes -> inspecciones (1:N cada una). +-- Cada restriccion ataca uno de los tres errores que pidio detectar +-- el cliente: repetidos (UNIQUE), relaciones invalidas +-- (FOREIGN KEY) y valores fuera de rango (CHECK). + +CREATE TABLE clientes ( + id_cliente INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_cliente TEXT NOT NULL UNIQUE, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE tecnicos ( + id_tecnico INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_tecnico TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +CREATE TABLE ordenes ( + id_orden INTEGER PRIMARY KEY AUTOINCREMENT, + id_cliente INTEGER NOT NULL, + id_tecnico INTEGER NOT NULL, + descripcion TEXT NOT NULL, + fecha_orden TEXT NOT NULL DEFAULT (date('now')), + estado TEXT NOT NULL DEFAULT 'pendiente' + CHECK (estado IN ('pendiente', 'en_proceso', 'finalizada', 'cancelada')), + + FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente), + FOREIGN KEY (id_tecnico) REFERENCES tecnicos (id_tecnico) +); + +-- materiales: detalle de costos de cada orden. cantidad y +-- costo_unitario nunca pueden ser valores fuera de rango. +CREATE TABLE materiales ( + id_material INTEGER PRIMARY KEY AUTOINCREMENT, + id_orden INTEGER NOT NULL, + nombre_material TEXT NOT NULL, + cantidad INTEGER NOT NULL CHECK (cantidad > 0), + costo_unitario REAL NOT NULL CHECK (costo_unitario >= 0), + + FOREIGN KEY (id_orden) REFERENCES ordenes (id_orden) +); + +-- inspecciones: historico de calidad. No se borra ninguna fila de +-- esta tabla en operaciones normales. +CREATE TABLE inspecciones ( + id_inspeccion INTEGER PRIMARY KEY AUTOINCREMENT, + id_orden INTEGER NOT NULL, + fecha_inspeccion TEXT NOT NULL DEFAULT (datetime('now')), + resultado TEXT NOT NULL DEFAULT 'pendiente' + CHECK (resultado IN ('aprobada', 'rechazada', 'pendiente')), + observaciones TEXT, + + FOREIGN KEY (id_orden) REFERENCES ordenes (id_orden) +); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/diagrama-er.svg new file mode 100644 index 00000000..551187e2 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/diagramas/diagrama-er.svg @@ -0,0 +1,58 @@ + + + + + clientes + id_cliente PK + nombre_cliente UNIQUE + telefono UNIQUE + + + tecnicos + id_tecnico PK + nombre_tecnico UNIQUE + especialidad + + + ordenes + id_orden PK + id_cliente FK, id_tecnico FK + descripcion, fecha_orden + estado CHECK + + + materiales + id_material PK + id_orden FK + cantidad CHECK > 0 + costo_unitario CHECK >= 0 + + + inspecciones + id_inspeccion PK + id_orden FK + fecha_inspeccion + resultado CHECK + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/inserts.sql new file mode 100644 index 00000000..ce14ed15 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/inserts.sql @@ -0,0 +1,54 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 070: Soldadura Industrial +-- Datos de prueba. + +INSERT INTO clientes (nombre_cliente, telefono) VALUES + ('Constructora Los Pinos', '5555-9001'), + ('Metalurgica Sur', '5555-9002'), + ('Talleres Andrade', '5555-9003'); + +INSERT INTO tecnicos (nombre_tecnico, especialidad) VALUES + ('Hugo Marroquin', 'soldadura MIG'), + ('Cristina Barrios', 'soldadura TIG'), + ('Esteban Cifuentes', 'soldadura por arco'); + +INSERT INTO ordenes (id_cliente, id_tecnico, descripcion, fecha_orden, estado) VALUES + (1, 1, 'Reparacion de estructura metalica de bodega', '2026-08-01', 'finalizada'), + (2, 2, 'Fabricacion de portones industriales', '2026-08-03', 'en_proceso'), + (3, 1, 'Refuerzo de vigas de techo', '2026-08-05', 'pendiente'), + (1, 3, 'Soldadura de tuberia de gas', '2026-08-06', 'cancelada'); + +INSERT INTO materiales (id_orden, nombre_material, cantidad, costo_unitario) VALUES + (1, 'Electrodo 6011', 20, 3.50), + (1, 'Lamina de acero 3mm', 4, 45.00), + (2, 'Electrodo 7018', 30, 4.20), + (2, 'Tubo cuadrado 2x2', 6, 28.75), + (3, 'Lamina de acero 5mm', 2, 60.00); + +-- Material cargado dos veces por error de digitacion en la orden 2 +-- (mismo material, misma cantidad, mismo costo): es el unico caso de +-- este modelo donde un DELETE real es aceptable, porque la orden 2 +-- todavia no tiene ninguna inspeccion asociada (se corrige en +-- dml/operaciones.sql). +INSERT INTO materiales (id_orden, nombre_material, cantidad, costo_unitario) VALUES + (2, 'Electrodo 7018', 30, 4.20); + +-- inspecciones: historico de calidad de cada orden. +INSERT INTO inspecciones (id_orden, resultado, observaciones) VALUES + (1, 'aprobada', 'Estructura verificada, sin observaciones'), + (1, 'aprobada', 'Segunda revision de cierre de proyecto'), + (3, 'rechazada', 'Falta reforzar dos uniones antes de continuar'); + +-- Casos comentados que deben fallar (no ser recomendables), dejar +-- comentados. Cada uno demuestra uno de los tres errores que pidio +-- detectar el cliente: + +-- 1) Registro repetido: nombre_cliente ya existe, viola el UNIQUE. +-- INSERT INTO clientes (nombre_cliente, telefono) VALUES ('Constructora Los Pinos', '5555-9099'); + +-- 2) Relacion invalida: id_tecnico = 99 no existe, viola el FOREIGN KEY. +-- INSERT INTO ordenes (id_cliente, id_tecnico, descripcion) VALUES (1, 99, 'Orden con tecnico inexistente'); + +-- 3) Valor fuera de rango: cantidad negativa, viola el CHECK. +-- INSERT INTO materiales (id_orden, nombre_material, cantidad, costo_unitario) VALUES (1, 'Electrodo 6011', -5, 3.50); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/operaciones.sql new file mode 100644 index 00000000..10635d96 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dml/operaciones.sql @@ -0,0 +1,44 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 070: Soldadura Industrial +-- Operaciones de mantenimiento: UPDATE de estado y DELETE controlado. + +-- 1. La orden 3 (refuerzo de vigas) fue rechazada en su inspeccion y +-- el tecnico ya empezo a corregir las uniones: pasa de +-- 'pendiente' a 'en_proceso'. +UPDATE ordenes +SET estado = 'en_proceso' +WHERE id_orden = 3 AND estado = 'pendiente'; + +-- 2. Se corrigio la union rechazada de la orden 3: se registra una +-- nueva inspeccion de seguimiento en vez de borrar o modificar la +-- inspeccion original, para conservar el historico de calidad tal +-- como lo necesita el cliente. +INSERT INTO inspecciones (id_orden, resultado, observaciones) VALUES + (3, 'aprobada', 'Uniones reforzadas, se aprueba en segunda revision'); + +-- 3. DELETE controlado: se elimina el material duplicado por error de +-- digitacion en la orden 2 (mismo material, misma cantidad, mismo +-- costo, sin ninguna inspeccion asociada a esa orden todavia). +-- Solo se permite este DELETE porque la orden 2 no tiene +-- inspecciones registradas; si ya las tuviera, borrar un material +-- cambiaria un costo que ya se audito. +DELETE FROM materiales +WHERE id_material = ( + SELECT MAX(m2.id_material) + FROM materiales m2 + WHERE m2.id_orden = 2 + AND m2.nombre_material = 'Electrodo 7018' + AND m2.cantidad = 30 + AND m2.costo_unitario = 4.20 +) +AND NOT EXISTS ( + SELECT 1 FROM inspecciones i WHERE i.id_orden = 2 +); + +-- Caso que debe fallar (queda comentado): eliminar un material de la +-- orden 1, que ya tiene inspecciones asociadas. La condicion de arriba +-- (NOT EXISTS inspecciones) ya evita que esto pase de forma automatica, +-- pero ademas seria un error de negocio: cambiaria el costo de una +-- orden que ya se audito. +-- DELETE FROM materiales WHERE id_orden = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dql/consultas.sql new file mode 100644 index 00000000..d5361af4 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/dql/consultas.sql @@ -0,0 +1,54 @@ +.headers on +.mode column + +-- Ejercicio 070: Soldadura Industrial +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: todas las ordenes con su +-- cliente y su tecnico. +SELECT o.id_orden, + c.nombre_cliente, + t.nombre_tecnico, + o.descripcion, + o.fecha_orden, + o.estado +FROM ordenes o +JOIN clientes c ON c.id_cliente = o.id_cliente +JOIN tecnicos t ON t.id_tecnico = o.id_tecnico; + +-- 2. Que ordenes estan pendientes, en proceso, finalizadas o +-- canceladas (filtro por estado: ejemplo con las que siguen abiertas). +SELECT id_orden, descripcion, estado +FROM ordenes +WHERE estado IN ('pendiente', 'en_proceso'); + +-- 3. Que tecnico tiene mas ordenes asignadas (ranking de actividad). +SELECT t.nombre_tecnico, COUNT(*) AS total_ordenes +FROM tecnicos t +JOIN ordenes o ON o.id_tecnico = t.id_tecnico +GROUP BY t.id_tecnico, t.nombre_tecnico +ORDER BY total_ordenes DESC, t.nombre_tecnico; + +-- 4. Materiales ordenados por costo total (cantidad x costo unitario), +-- de mayor a menor. +SELECT o.id_orden, + m.nombre_material, + m.cantidad, + m.costo_unitario, + (m.cantidad * m.costo_unitario) AS costo_total +FROM materiales m +JOIN ordenes o ON o.id_orden = m.id_orden +ORDER BY costo_total DESC; + +-- 5. Reporte para decision de negocio: ordenes con al menos una +-- inspeccion rechazada, para saber cuales necesitan corregirse antes +-- de entregarse al cliente (GROUP BY + HAVING). +SELECT o.id_orden, + o.descripcion, + COUNT(*) AS inspecciones_rechazadas +FROM inspecciones i +JOIN ordenes o ON o.id_orden = i.id_orden +WHERE i.resultado = 'rechazada' +GROUP BY o.id_orden, o.descripcion +HAVING COUNT(*) >= 1 +ORDER BY inspecciones_rechazadas DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/evidencias/resultados.md new file mode 100644 index 00000000..65fb3919 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-070/evidencias/resultados.md @@ -0,0 +1,97 @@ +# Evidencias - Solicitudes SQL - Ejercicio 070 (Soldadura Industrial) + +## 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-070.db < ddl/schema.sql +sqlite3 ejercicio-070.db < dml/inserts.sql +sqlite3 ejercicio-070.db < dml/operaciones.sql +sqlite3 ejercicio-070.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 3 clientes, 3 tecnicos, 4 ordenes, +6 materiales (incluye el duplicado por error) y 3 inspecciones. + +**Casos comentados verificados** (descomentados y ejecutados por +separado para confirmar que cada uno falla, uno por cada tipo de error +que pidio detectar el cliente): + +- Registro repetido: `INSERT INTO clientes (nombre_cliente, ...) VALUES ('Constructora Los Pinos', ...);` → `UNIQUE constraint failed: clientes.nombre_cliente`. +- Relacion invalida: `INSERT INTO ordenes (id_cliente, id_tecnico, ...) VALUES (1, 99, ...);` → `FOREIGN KEY constraint failed`. +- Valor fuera de rango: `INSERT INTO materiales (..., cantidad, ...) VALUES (..., -5, ...);` → `CHECK constraint failed: cantidad > 0`. + +**1. Todas las ordenes, con JOIN a clientes y tecnicos:** + +```text +id_orden | nombre_cliente | nombre_tecnico | descripcion | fecha_orden | estado +1 | Constructora Los Pinos | Hugo Marroquin | Reparacion de estructura metalica de bodega | 2026-08-01 | finalizada +2 | Metalurgica Sur | Cristina Barrios | Fabricacion de portones industriales | 2026-08-03 | en_proceso +3 | Talleres Andrade | Hugo Marroquin | Refuerzo de vigas de techo | 2026-08-05 | en_proceso +4 | Constructora Los Pinos | Esteban Cifuentes | Soldadura de tuberia de gas | 2026-08-06 | cancelada +``` + +(La orden 3 ya aparece como `en_proceso`: paso de `pendiente` con el +`UPDATE` de `dml/operaciones.sql`.) + +**2. Ordenes pendientes o en proceso:** + +```text +id_orden | descripcion | estado +2 | Fabricacion de portones industriales | en_proceso +3 | Refuerzo de vigas de techo | en_proceso +``` + +**3. Tecnico con mas ordenes asignadas:** + +```text +nombre_tecnico | total_ordenes +Hugo Marroquin | 2 +Cristina Barrios | 1 +Esteban Cifuentes | 1 +``` + +**4. Materiales ordenados por costo total (cantidad x costo unitario), +de mayor a menor:** + +```text +id_orden | nombre_material | cantidad | costo_unitario | costo_total +1 | Lamina de acero 3mm | 4 | 45.00 | 180.00 +2 | Tubo cuadrado 2x2 | 6 | 28.75 | 172.50 +2 | Electrodo 7018 | 30 | 4.20 | 126.00 +3 | Lamina de acero 5mm | 2 | 60.00 | 120.00 +1 | Electrodo 6011 | 20 | 3.50 | 70.00 +``` + +Quedan 5 materiales (empezaron 6, se elimino el duplicado exacto de +`Electrodo 7018` en la orden 2). + +**5. Ordenes con inspecciones rechazadas (para saber cuales corregir +antes de entregar):** + +```text +id_orden | descripcion | inspecciones_rechazadas +3 | Refuerzo de vigas de techo | 1 +``` + +## Operaciones de mantenimiento verificadas + +- `UPDATE ordenes SET estado = 'en_proceso' WHERE id_orden = 3 ...;` → la orden de refuerzo de vigas paso de `pendiente` a `en_proceso` porque el tecnico ya empezo a corregir. +- Nueva inspeccion registrada para la orden 3 (`aprobada`, "Uniones reforzadas..."): la inspeccion `rechazada` original **no se borro ni se modifico**, sigue en el historico junto a la nueva. +- **DELETE controlado**: se elimino el material duplicado (`id_material` mas alto que coincidia en material, cantidad y costo, y solo porque la orden 2 no tenia ninguna inspeccion todavia). Materiales de la orden 2 despues del `DELETE`: solo `Electrodo 7018` (fila original) y `Tubo cuadrado 2x2`. Conteo final: 5 materiales (empezaron 6). + +## Aprendizaje + +Los tres errores que preocupaban al cliente (repetidos, relaciones +invalidas, valores fuera de rango) se bloquean desde el `INSERT` +gracias a `UNIQUE`, `FOREIGN KEY` y `CHECK`. El modelo ademas protege +el historico de calidad: una inspeccion rechazada nunca se borra ni se +sobrescribe, se corrige registrando una inspeccion nueva, igual que en +una auditoria real. El `DELETE` controlado de materiales solo se +permite cuando el error es de captura y la orden todavia no tiene +ningun historico de inspeccion que dependa de ese costo.