From 731fee3df0e466fb68f77dc71b0fd6eec8df9bc3 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 16:39:29 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 75 --- .../maria-montepeque/ejercicio-75/README.md | 80 +++++++++++++++++++ .../ejercicio-75/ddl/schema.sql | 37 +++++++++ .../ejercicio-75/dml/inserts.sql | 48 +++++++++++ .../ejercicio-75/dql/consultas.sql | 40 ++++++++++ .../ejercicio-75/evidencias/resultados.md | 73 +++++++++++++++++ 5 files changed, 278 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-75/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-75/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-75/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-75/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-75/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-75/README.md b/resoluciones/maria-montepeque/ejercicio-75/README.md new file mode 100644 index 00000000..a5a8895f --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-75/README.md @@ -0,0 +1,80 @@ +# Ejercicio 75: UPDATE Nivel Intermedio + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +UPDATE + +## Descripcion del problema + +Una bodega de dispositivos tecnologicos necesita mantener actualizado +el stock y el precio de sus productos a medida que ocurren +movimientos y cambios de mercado, sin perder el registro de cada +movimiento individual. + +## Tablas y relaciones + +- `categorias`: catalogo de categorias de producto. +- `productos`: catalogo de productos, con su propio `stock_actual` + como columna (a diferencia de un modelo basado solo en historial). +- `movimientos`: historial de entradas y salidas de bodega. + `categorias` 1—N `productos`; `productos` 1—N `movimientos`. + +## Uso de UPDATE + +En `dml/inserts.sql`: + +1. `UPDATE` de una sola fila con expresion: al registrar un + reabastecimiento de Laptop Pro 14, `stock_actual = stock_actual + 5` + suma la cantidad recibida al stock existente. +2. `UPDATE` de una sola fila con expresion: al registrar una venta de + Mouse Inalambrico, `stock_actual = stock_actual - 12` resta la + cantidad vendida. +3. `UPDATE` multiple: un ajuste de precios del 10% se aplica con un + solo `UPDATE` a todos los productos de la categoria Laptops + (`WHERE id_categoria = 1`), sin listar cada `id_producto` a mano. + +La consulta 5 en `dql/consultas.sql` confirma el `stock_actual` y el +`precio_unitario` finales de los 3 productos afectados. + +## 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.stock_actual >= 0`, `movimientos.tipo_movimiento IN (...)`, + `movimientos.cantidad > 0`. +- `DEFAULT` en `productos.stock_actual`, `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`) + +`UPDATE productos SET stock_actual = stock_actual - 999 WHERE id_producto = 2;` +falla porque el resultado quedaria negativo, y eso viola el `CHECK` de +`stock_actual >= 0`. Se valido con Python (`sqlite3`): lanza +`CHECK constraint failed`. + +## 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: Laptop Pro 14 con stock 15 y precio $9,350.00; Mouse + Inalambrico con stock 38; Laptop Air 13 con precio $6,820.00. + +## Como ejecutar + +```bash +sqlite3 ejercicio-75.db < ddl/schema.sql +sqlite3 ejercicio-75.db < dml/inserts.sql +sqlite3 ejercicio-75.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-75/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-75/ddl/schema.sql new file mode 100644 index 00000000..b8267ffc --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-75/ddl/schema.sql @@ -0,0 +1,37 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 75: UPDATE Nivel Intermedio +-- Tema central: UPDATE +-- Contexto: inventario de dispositivos tecnologicos en bodega. + +CREATE TABLE categorias ( + id_categoria INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_categoria TEXT NOT NULL UNIQUE +); + +-- productos: aqui si se guarda stock_actual como columna (a +-- diferencia de un modelo basado solo en historial), justo para +-- poder practicar UPDATE sobre ella. +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), + stock_actual INTEGER NOT NULL DEFAULT 0 CHECK (stock_actual >= 0), + + FOREIGN KEY (id_categoria) REFERENCES categorias (id_categoria) +); + +-- movimientos: historial de entradas y salidas de bodega, como +-- respaldo. El stock_actual de productos se corrige con UPDATE cada +-- vez que se registra un movimiento nuevo. +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-75/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-75/dml/inserts.sql new file mode 100644 index 00000000..7d0333e6 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-75/dml/inserts.sql @@ -0,0 +1,48 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 75: UPDATE Nivel Intermedio +-- Datos de prueba y UPDATE de validacion. + +INSERT INTO categorias (nombre_categoria) VALUES + ('Laptops'), + ('Perifericos'), + ('Almacenamiento'); + +-- Productos con su stock inicial ya conocido de bodega. +INSERT INTO productos (nombre_producto, id_categoria, precio_unitario, stock_actual) VALUES + ('Laptop Pro 14', 1, 8500.00, 10), + ('Laptop Air 13', 1, 6200.00, 8), + ('Mouse Inalambrico', 2, 150.00, 50), + ('Teclado Mecanico', 2, 320.00, 30), + ('Disco SSD 1TB', 3, 480.00, 20); + +-- 1. Llega un reabastecimiento de Laptop Pro 14: se registra el +-- movimiento y, con un UPDATE de una sola fila y una expresion (no un +-- numero fijo), se suma la cantidad al stock actual. +INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES + (1, 'entrada', 5); + +UPDATE productos +SET stock_actual = stock_actual + 5 +WHERE id_producto = 1; + +-- 2. Se vende Mouse Inalambrico: se registra el movimiento y se resta +-- del stock con la misma tecnica. +INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES + (3, 'salida', 12); + +UPDATE productos +SET stock_actual = stock_actual - 12 +WHERE id_producto = 3; + +-- 3. UPDATE multiple: el proveedor de laptops subio precios un 10%. +-- Un solo UPDATE, con WHERE por categoria, ajusta el precio de todos +-- los productos de esa categoria a la vez (2 filas). +UPDATE productos +SET precio_unitario = ROUND(precio_unitario * 1.10, 2) +WHERE id_categoria = 1; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: restar mas unidades de las que hay en stock dejaria un +-- numero negativo, lo que viola el CHECK de stock_actual. +-- UPDATE productos SET stock_actual = stock_actual - 999 WHERE id_producto = 2; diff --git a/resoluciones/maria-montepeque/ejercicio-75/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-75/dql/consultas.sql new file mode 100644 index 00000000..e99ffb8d --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-75/dql/consultas.sql @@ -0,0 +1,40 @@ +.headers on +.mode column + +-- Ejercicio 75: UPDATE Nivel Intermedio +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales (productos con su +-- categoria). +SELECT p.id_producto, + p.nombre_producto, + c.nombre_categoria, + p.precio_unitario, + p.stock_actual +FROM productos p +JOIN categorias c ON c.id_categoria = p.id_categoria; + +-- 2. Consulta con WHERE: solo los movimientos de tipo salida. +SELECT id_movimiento, id_producto, cantidad +FROM movimientos +WHERE tipo_movimiento = 'salida'; + +-- 3. Consulta con ORDER BY: productos ordenados por stock actual, de +-- mayor a menor. +SELECT nombre_producto, stock_actual +FROM productos +ORDER BY stock_actual DESC; + +-- 4. Conteo o resumen: total de movimientos por tipo. +SELECT tipo_movimiento, COUNT(*) AS total +FROM movimientos +GROUP BY tipo_movimiento; + +-- 5. Validacion especifica de UPDATE: Laptop Pro 14 quedo con +-- stock_actual = 15 (broto 10, sumo 5 con el reabastecimiento) y +-- Mouse Inalambrico con stock_actual = 38 (broto 50, resto 12 por la +-- venta). Ademas, las dos laptops quedaron con el precio ya +-- actualizado un 10%. +SELECT nombre_producto, precio_unitario, stock_actual +FROM productos +WHERE id_producto IN (1, 2, 3); diff --git a/resoluciones/maria-montepeque/ejercicio-75/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-75/evidencias/resultados.md new file mode 100644 index 00000000..449fda2b --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-75/evidencias/resultados.md @@ -0,0 +1,73 @@ +# Evidencias - Ejercicio 75 + +## Tema + +UPDATE + +## 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-75.db < ddl/schema.sql +sqlite3 ejercicio-75.db < dml/inserts.sql +sqlite3 ejercicio-75.db < dql/consultas.sql +``` + +## Resultados + +Estado final de `productos` tras `dml/inserts.sql` (que incluye los 3 +`UPDATE` de validacion): + +```text +id_producto | nombre_producto | id_categoria | precio_unitario | stock_actual +1 | Laptop Pro 14 | 1 | 9350.0 | 15 +2 | Laptop Air 13 | 1 | 6820.0 | 8 +3 | Mouse Inalambrico | 2 | 150.0 | 38 +4 | Teclado Mecanico | 2 | 320.0 | 30 +5 | Disco SSD 1TB | 3 | 480.0 | 20 +``` + +**Caso comentado verificado:** + +- `UPDATE productos SET stock_actual = stock_actual - 999 WHERE id_producto = 2;` → `CHECK constraint failed: stock_actual >= 0`. + +**4. Resumen: movimientos por tipo:** + +```text +tipo_movimiento total +entrada 1 +salida 1 +``` + +**5. Validacion especifica de UPDATE:** + +```text +nombre_producto precio_unitario stock_actual +Laptop Pro 14 9350.0 15 +Laptop Air 13 6820.0 8 +Mouse Inalambrico 150.0 38 +``` + +Laptop Pro 14: broto con `stock_actual = 10`, el `UPDATE` con +`stock_actual = stock_actual + 5` lo dejo en 15. Mouse Inalambrico: +broto con `stock_actual = 50`, el `UPDATE` con +`stock_actual = stock_actual - 12` lo dejo en 38. Las dos laptops +(Laptop Pro 14 y Laptop Air 13) subieron su precio un 10% con un solo +`UPDATE` multiple filtrado por `id_categoria = 1` +(8500.00 -> 9350.00 y 6200.00 -> 6820.00). + +## Aprendizaje + +`UPDATE` con una expresion (`columna = columna + n` o +`columna = columna * 1.10`) permite corregir un valor a partir de si +mismo, sin tener que calcular el resultado final antes de escribir la +sentencia. Un `WHERE` sobre una llave foranea +(`WHERE id_categoria = 1`) hace que un solo `UPDATE` afecte a todos +los productos de esa categoria a la vez, sin importar cuantos sean, lo +que es distinto a listar ids especificos con `IN (...)`. El `CHECK` de +`stock_actual >= 0` protege el modelo incluso durante un `UPDATE`: si +el nuevo valor calculado violara la restriccion, la fila no se +modifica, tal como se confirmo con el caso comentado. From 97705d9b77f5ca9ad516fa4a08bea84397f5119f Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 16:39:30 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 075 (track day hiperdeportivos) --- .../solicitudes-sql/ejercicio-075/README.md | 81 +++++++++++++++++++ .../ejercicio-075/analisis/requerimiento.md | 77 ++++++++++++++++++ .../ejercicio-075/ddl/schema.sql | 54 +++++++++++++ .../ejercicio-075/diagramas/.gitkeep | 0 .../ejercicio-075/diagramas/diagrama-er.svg | 59 ++++++++++++++ .../ejercicio-075/dml/inserts.sql | 71 ++++++++++++++++ .../ejercicio-075/dml/operaciones.sql | 32 ++++++++ .../ejercicio-075/dql/consultas.sql | 49 +++++++++++ .../ejercicio-075/evidencias/resultados.md | 67 +++++++++++++++ 9 files changed, 490 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/README.md new file mode 100644 index 00000000..83c7a299 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/README.md @@ -0,0 +1,81 @@ +# Solicitud SQL - Ejercicio 075: Track Day Hiperdeportivos + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Una pista organiza sesiones con vehiculos hiperdeportivos, pilotos y +tiempos. El cliente no sabe hablar en terminos de tablas: solo +describe su operacion diaria y espera que se traduzca a SQL. 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 + +Detras de la descripcion informal hay una operacion clara: pilotos +que corren vueltas cronometradas en sesiones, con un vehiculo +distinto en cada una, y que pagan por participar. 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 + +- `pilotos`: catalogo de pilotos inscritos. +- `vehiculos`: catalogo de vehiculos hiperdeportivos disponibles. +- `sesiones`: tabla transaccional, cada dia de track day. +- `tiempos`: detalle de cada sesion (tiempo de cada vuelta de cada + piloto). Aqui esta el `UNIQUE (id_sesion, id_piloto, vuelta)` que + impide cargar dos veces la misma vuelta del mismo piloto. +- `pagos`: lo que cada piloto pago por participar en una sesion. + +## Como se relacionan + +`sesiones` 1:N `tiempos`; `pilotos` 1:N `tiempos`; `vehiculos` 1:N +`tiempos`; `pilotos` 1:N `pagos`; `sesiones` 1:N `pagos`. El diagrama +esta en [diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +3 pilotos, 3 vehiculos, 3 sesiones (marcadas `finalizada` en algun +momento), 13 tiempos (incluye uno cargado por error para una sesion +que despues se descubrio que habia que cancelar) y 6 pagos, ademas de +un `INSERT` comentado que reproduce el problema de cargar dos veces la +misma vuelta del mismo piloto y debe fallar. Detalle en +[dml/inserts.sql](dml/inserts.sql). + +## Que operaciones de mantenimiento incluyo + +En [dml/operaciones.sql](dml/operaciones.sql): un `UPDATE` de estado +(la sesion con falla de cronometraje pasa a `cancelada`), un `DELETE` +controlado que limpia el tiempo huerfano de esa sesion, y un `UPDATE` +multiple que confirma como `pagado` los 6 pagos pendientes de las +sesiones ya finalizadas. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): que tiempos existen (JOIN +piloto-vehiculo-sesion), en que estado esta cada sesion, que piloto +tiene mas vueltas registradas, los tiempos ordenados de mas rapido a +mas lento, y un reporte con `GROUP BY` + `HAVING` de que pilotos +tienen el mejor tiempo promedio, para decidir a quienes invitar al +siguiente evento. + +## 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-075.db < ddl/schema.sql +sqlite3 ejercicio-075.db < dml/inserts.sql +sqlite3 ejercicio-075.db < dml/operaciones.sql +sqlite3 ejercicio-075.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/analisis/requerimiento.md new file mode 100644 index 00000000..36c4d839 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/analisis/requerimiento.md @@ -0,0 +1,77 @@ +# Analisis del requerimiento - Ejercicio 075 + +## Solicitud entendida + +Una pista organiza sesiones de track day con vehiculos +hiperdeportivos, pilotos y tiempos por vuelta. El cliente no habla en +terminos de tablas, solo describe su operacion diaria: pilotos que se +inscriben, corren varias vueltas en una sesion, y pagan por +participar. Se necesita una base de datos que permita consultar +datos, corregir estados, registrar movimientos y sacar reportes +utiles, como que pilotos tienen el mejor tiempo promedio para +invitarlos al siguiente evento. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| pilotos | Catalogo: cada piloto inscrito en la pista | nombre_piloto (unico), licencia (unica) | +| vehiculos | Catalogo: cada vehiculo hiperdeportivo disponible | modelo (unico), categoria | +| sesiones | Tabla transaccional: cada dia de track day, en una fecha y una pista | fecha_sesion, pista, estado | +| tiempos | Detalle de cada sesion: el tiempo de cada vuelta de cada piloto con su vehiculo | vuelta, tiempo_segundos | +| pagos | Registro de lo que cada piloto pago por participar en una sesion | monto, estado | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| sesiones -> tiempos | 1:N | Una sesion tiene muchas vueltas registradas. | +| pilotos -> tiempos | 1:N | Un piloto corre muchas vueltas a lo largo de varias sesiones. | +| vehiculos -> tiempos | 1:N | Un vehiculo se usa en muchas vueltas distintas. | +| pilotos -> pagos | 1:N | Un piloto puede tener varios pagos (uno por sesion). | +| sesiones -> pagos | 1:N | Una sesion tiene un pago por cada piloto inscrito. | + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): todo tiempo debe apuntar a una + sesion, un piloto y un vehiculo reales; todo pago debe apuntar a un + piloto y a una sesion reales (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `pilotos.nombre_piloto`, + `pilotos.licencia` y `vehiculos.modelo` no se repiten (`UNIQUE`); un + piloto no puede tener dos tiempos para la misma vuelta de la misma + sesion (`UNIQUE (id_sesion, id_piloto, vuelta)`). +- Regla 3 (valores fuera de rango): `tiempos.tiempo_segundos` y + `pagos.monto` nunca pueden ser negativos o cero (`CHECK`); + `tiempos.vuelta` siempre debe ser 1 o mayor (`CHECK`). +- Regla 4: una sesion nace `'programada'` y avanza a `'en_curso'`, + `'finalizada'` o `'cancelada'` (`CHECK`); un pago nace + `'pendiente'` y avanza a `'pagado'` o `'reembolsado'` (`CHECK`); + ambos se corrigen con `UPDATE`. +- Regla 5: los tiempos solo tienen sentido para una sesion que + realmente se corrio. Si una sesion se cancela y ya tenia tiempos + cargados por error, esas filas se eliminan; nunca se borra un + tiempo de una sesion `'finalizada'`, porque ya es un resultado + oficial. + +## Supuestos + +- El cliente no detallo cuantas vueltas corre cada piloto por sesion; + se asume un numero variable, registrado vuelta por vuelta en + `tiempos`. +- No se detallo si un piloto puede usar vehiculos distintos en + sesiones distintas; se asume que si, porque asi funciona la mayoria + de dias de pista con flota compartida. +- Se asume que el pago es por sesion completa (no por vuelta), ya que + el cliente hablo de "cuanto dinero representa cada movimiento" en + terminos de su operacion diaria, no de cada vuelta individual. + +## Preguntas que responde la base de datos + +1. Que tiempos existen, con que piloto, que vehiculo y que sesion. +2. Que sesiones estan programadas, en curso, finalizadas o + canceladas. +3. Que piloto tiene mas vueltas registradas (ranking de actividad). +4. Como se ordenan los tiempos, de la vuelta mas rapida a la mas + lenta. +5. Que pilotos tienen el mejor tiempo promedio, para decidir a + quienes invitar al siguiente evento. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/ddl/schema.sql new file mode 100644 index 00000000..1b7dee3e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/ddl/schema.sql @@ -0,0 +1,54 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 075: Track Day Hiperdeportivos +-- Modelo: sesiones + pilotos + vehiculos -> tiempos (1:N cada una); +-- pilotos + sesiones -> pagos (1:N cada una). + +CREATE TABLE pilotos ( + id_piloto INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_piloto TEXT NOT NULL UNIQUE, + licencia TEXT NOT NULL UNIQUE +); + +CREATE TABLE vehiculos ( + id_vehiculo INTEGER PRIMARY KEY AUTOINCREMENT, + modelo TEXT NOT NULL UNIQUE, + categoria TEXT NOT NULL CHECK (categoria IN ('hipercar', 'superdeportivo', 'gt')) +); + +CREATE TABLE sesiones ( + id_sesion INTEGER PRIMARY KEY AUTOINCREMENT, + fecha_sesion TEXT NOT NULL, + pista TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'en_curso', 'finalizada', 'cancelada')) +); + +-- tiempos: el UNIQUE compuesto impide que un piloto quede cargado dos +-- veces en la misma vuelta de la misma sesion. +CREATE TABLE tiempos ( + id_tiempo INTEGER PRIMARY KEY AUTOINCREMENT, + id_sesion INTEGER NOT NULL, + id_piloto INTEGER NOT NULL, + id_vehiculo INTEGER NOT NULL, + vuelta INTEGER NOT NULL CHECK (vuelta >= 1), + tiempo_segundos REAL NOT NULL CHECK (tiempo_segundos > 0), + + FOREIGN KEY (id_sesion) REFERENCES sesiones (id_sesion), + FOREIGN KEY (id_piloto) REFERENCES pilotos (id_piloto), + FOREIGN KEY (id_vehiculo) REFERENCES vehiculos (id_vehiculo), + UNIQUE (id_sesion, id_piloto, vuelta) +); + +CREATE TABLE pagos ( + id_pago INTEGER PRIMARY KEY AUTOINCREMENT, + id_piloto INTEGER NOT NULL, + id_sesion INTEGER NOT NULL, + monto REAL NOT NULL CHECK (monto > 0), + estado TEXT NOT NULL DEFAULT 'pendiente' + CHECK (estado IN ('pendiente', 'pagado', 'reembolsado')), + fecha_pago TEXT, + + FOREIGN KEY (id_piloto) REFERENCES pilotos (id_piloto), + FOREIGN KEY (id_sesion) REFERENCES sesiones (id_sesion) +); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/diagrama-er.svg new file mode 100644 index 00000000..cd915890 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/diagramas/diagrama-er.svg @@ -0,0 +1,59 @@ + + + + + pilotos + id_piloto PK + nombre_piloto UNIQUE + licencia UNIQUE + + + pagos + id_pago PK + id_piloto FK, id_sesion FK + + + vehiculos + id_vehiculo PK + modelo UNIQUE + categoria CHECK + + + sesiones + id_sesion PK + fecha_sesion, estado CHECK + + + tiempos + id_tiempo PK + id_sesion FK, id_piloto FK + id_vehiculo FK + vuelta, tiempo_segundos + UNIQUE(sesion,piloto,vuelta) + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/inserts.sql new file mode 100644 index 00000000..20b35b0d --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/inserts.sql @@ -0,0 +1,71 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 075: Track Day Hiperdeportivos +-- Datos base: 3 pilotos, 3 vehiculos, 3 sesiones (2 finalizadas, 1 +-- marcada 'finalizada' por error que se corrige despues), 13 tiempos +-- (2 vueltas por piloto en cada una de las 2 sesiones reales, mas 1 +-- vuelta cargada por error en la sesion que se cancela) y 6 pagos. + +INSERT INTO pilotos (nombre_piloto, licencia) VALUES + ('Fernanda Lopez', 'LIC-001'), + ('Bryan Solis', 'LIC-002'), + ('Karla Rivas', 'LIC-003'); + +INSERT INTO vehiculos (modelo, categoria) VALUES + ('Ferrari SF90', 'hipercar'), + ('McLaren 720S', 'superdeportivo'), + ('Porsche 911 GT3', 'gt'); + +-- Sesion 1: Circuito Norte, finalizada. +INSERT INTO sesiones (fecha_sesion, pista, estado) VALUES + ('2026-08-01', 'Circuito Norte', 'finalizada'); + +-- Sesion 2: Circuito Sur, finalizada. +INSERT INTO sesiones (fecha_sesion, pista, estado) VALUES + ('2026-08-03', 'Circuito Sur', 'finalizada'); + +-- Sesion 3: Circuito Norte. Se marco 'finalizada' y se cargo un +-- tiempo, pero el sistema de cronometraje fallo y la sesion se anulo +-- despues. Se corrige en dml/operaciones.sql. +INSERT INTO sesiones (fecha_sesion, pista, estado) VALUES + ('2026-08-05', 'Circuito Norte', 'finalizada'); + +-- Tiempos de la sesion 1 (2 vueltas por piloto, cada uno con su +-- vehiculo). +INSERT INTO tiempos (id_sesion, id_piloto, id_vehiculo, vuelta, tiempo_segundos) VALUES + (1, 1, 1, 1, 92.345), + (1, 1, 1, 2, 91.870), + (1, 2, 2, 1, 93.120), + (1, 2, 2, 2, 92.560), + (1, 3, 3, 1, 95.400), + (1, 3, 3, 2, 94.980); + +-- Tiempos de la sesion 2. +INSERT INTO tiempos (id_sesion, id_piloto, id_vehiculo, vuelta, tiempo_segundos) VALUES + (2, 1, 1, 1, 90.800), + (2, 1, 1, 2, 90.250), + (2, 2, 2, 1, 92.900), + (2, 2, 2, 2, 92.100), + (2, 3, 3, 1, 94.100), + (2, 3, 3, 2, 93.750); + +-- Tiempo cargado por error para la sesion 3, antes de saber que el +-- cronometraje habia fallado. Quedara huerfano cuando la sesion se +-- marque 'cancelada' en dml/operaciones.sql, y se elimina ahi mismo. +INSERT INTO tiempos (id_sesion, id_piloto, id_vehiculo, vuelta, tiempo_segundos) VALUES + (3, 1, 1, 1, 91.000); + +-- Pagos de las sesiones 1 y 2 (una fila por piloto), todavia +-- pendientes. Se confirman con UPDATE en dml/operaciones.sql. +INSERT INTO pagos (id_piloto, id_sesion, monto) VALUES + (1, 1, 500.00), + (2, 1, 500.00), + (3, 1, 500.00), + (1, 2, 500.00), + (2, 2, 500.00), + (3, 2, 500.00); + +-- Caso comentado que debe fallar (queda comentado): cargar de nuevo a +-- Fernanda Lopez en la vuelta 1 de la sesion 1, exactamente el +-- problema que este UNIQUE esta disenado para evitar. +-- INSERT INTO tiempos (id_sesion, id_piloto, id_vehiculo, vuelta, tiempo_segundos) VALUES (1, 1, 1, 1, 92.000); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/operaciones.sql new file mode 100644 index 00000000..b026aa0b --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dml/operaciones.sql @@ -0,0 +1,32 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 075: Track Day Hiperdeportivos +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que el sistema de cronometraje de +-- la sesion 3 fallo y la sesion se anula. +UPDATE sesiones +SET estado = 'cancelada' +WHERE id_sesion = 3 AND estado = 'finalizada'; + +-- 1 DELETE controlado: el tiempo de la sesion 3 quedo huerfano apenas +-- se marco 'cancelada' (ya no representa una vuelta oficial). Solo se +-- borran tiempos de sesiones 'cancelada'; una sesion 'finalizada' +-- nunca pierde sus tiempos por este DELETE. +DELETE FROM tiempos +WHERE id_sesion IN ( + SELECT id_sesion FROM sesiones WHERE estado = 'cancelada' +); + +-- 1 UPDATE multiple de estado: se confirman todos los pagos +-- pendientes de las sesiones 1 y 2 (ya finalizadas) como 'pagado', +-- con un solo UPDATE. +UPDATE pagos +SET estado = 'pagado', fecha_pago = date('now') +WHERE id_sesion IN (1, 2) AND estado = 'pendiente'; + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar tiempos de una sesion que ya quedo 'finalizada' (resultado +-- oficial). El DELETE de arriba solo alcanza sesiones 'cancelada' por +-- diseno. +-- DELETE FROM tiempos WHERE id_sesion = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dql/consultas.sql new file mode 100644 index 00000000..c82f6a58 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/dql/consultas.sql @@ -0,0 +1,49 @@ +.headers on +.mode column + +-- Ejercicio 075: Track Day Hiperdeportivos +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: todos los tiempos con su +-- piloto, su vehiculo y su sesion. +SELECT t.id_tiempo, + p.nombre_piloto, + v.modelo, + s.fecha_sesion, + t.vuelta, + t.tiempo_segundos +FROM tiempos t +JOIN pilotos p ON p.id_piloto = t.id_piloto +JOIN vehiculos v ON v.id_vehiculo = t.id_vehiculo +JOIN sesiones s ON s.id_sesion = t.id_sesion; + +-- 2. Que sesiones estan programadas, en curso, finalizadas o +-- canceladas. +SELECT id_sesion, fecha_sesion, pista, estado +FROM sesiones +ORDER BY estado; + +-- 3. Que piloto tiene mas vueltas registradas (ranking de actividad). +SELECT p.nombre_piloto, COUNT(*) AS total_vueltas +FROM pilotos p +JOIN tiempos t ON t.id_piloto = p.id_piloto +GROUP BY p.id_piloto, p.nombre_piloto +ORDER BY total_vueltas DESC, p.nombre_piloto; + +-- 4. Tiempos ordenados de la vuelta mas rapida a la mas lenta. +SELECT p.nombre_piloto, v.modelo, t.tiempo_segundos +FROM tiempos t +JOIN pilotos p ON p.id_piloto = t.id_piloto +JOIN vehiculos v ON v.id_vehiculo = t.id_vehiculo +ORDER BY t.tiempo_segundos; + +-- 5. Reporte para decision de negocio: tiempo promedio por piloto, +-- para decidir a quienes invitar al siguiente evento (candidatos: +-- promedio menor a 93 segundos). +SELECT p.nombre_piloto, + ROUND(AVG(t.tiempo_segundos), 3) AS promedio_segundos +FROM tiempos t +JOIN pilotos p ON p.id_piloto = t.id_piloto +GROUP BY p.id_piloto, p.nombre_piloto +HAVING AVG(t.tiempo_segundos) < 93 +ORDER BY promedio_segundos; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/evidencias/resultados.md new file mode 100644 index 00000000..29315ad8 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-075/evidencias/resultados.md @@ -0,0 +1,67 @@ +# Evidencias - Solicitudes SQL - Ejercicio 075 (Track Day Hiperdeportivos) + +## 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-075.db < ddl/schema.sql +sqlite3 ejercicio-075.db < dml/inserts.sql +sqlite3 ejercicio-075.db < dml/operaciones.sql +sqlite3 ejercicio-075.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 3 pilotos, 3 vehiculos, 3 sesiones +(las 3 marcadas `finalizada` en algun momento), 13 tiempos (incluye +el cargado por error para la sesion que se debia cancelar) y 6 pagos +`pendiente`. + +**Caso comentado verificado:** + +- `INSERT INTO tiempos (id_sesion, id_piloto, ...) VALUES (1, 1, ..., 1, ...);` (repetir a Fernanda Lopez en la vuelta 1 de la sesion 1) → `UNIQUE constraint failed: tiempos.id_sesion, tiempos.id_piloto, tiempos.vuelta`. + +**3. Piloto con mas vueltas registradas:** + +```text +nombre_piloto total_vueltas +Bryan Solis 4 +Fernanda Lopez 4 +Karla Rivas 4 +``` + +(Los tres corrieron 2 vueltas en cada una de las 2 sesiones reales; el +tiempo huerfano de la sesion cancelada ya no cuenta.) + +**5. Pilotos con mejor tiempo promedio (candidatos a invitar al +siguiente evento, promedio menor a 93 segundos):** + +```text +nombre_piloto promedio_segundos +Fernanda Lopez 91.316 +Bryan Solis 92.67 +``` + +Karla Rivas quedo fuera del reporte con un promedio de 94.558 +segundos, por encima del umbral. + +## Operaciones de mantenimiento verificadas + +- `UPDATE sesiones SET estado = 'cancelada' WHERE id_sesion = 3 ...;` → la sesion del 2026-08-05 se anulo despues de confirmarse la falla del cronometraje. +- **DELETE controlado**: se elimino el unico tiempo que habia quedado huerfano (el de la sesion 3), apenas se marco `cancelada`. Total de tiempos: 13 -> 12. Ningun tiempo de una sesion `finalizada` se toco. +- **UPDATE multiple de pagos**: los 6 pagos de las sesiones 1 y 2 pasaron de `pendiente` a `pagado` con un solo `UPDATE`, con `fecha_pago` registrada. + +## Aprendizaje + +El `UNIQUE (id_sesion, id_piloto, vuelta)` en `tiempos` evita que un +piloto quede cargado dos veces en la misma vuelta de la misma sesion. +El `DELETE` controlado solo alcanza tiempos de sesiones `cancelada`, +protegiendo cualquier resultado ya oficial (`finalizada`). El reporte +de tiempo promedio (`GROUP BY` + `HAVING`) responde directamente la +necesidad del cliente, aunque el describio su operacion en lenguaje +cotidiano y no en terminos de tablas: traducir "quiero saber a quien +invitar al siguiente evento" en una consulta con umbral de tiempo +promedio es justo el trabajo de analisis que pedia la solicitud.