From 2aa441fbfb94a83f314c85e3e4f159b3b08a5102 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 06:55:18 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 82 --- .../maria-montepeque/ejercicio-82/README.md | 80 +++++++++++++++++++ .../ejercicio-82/ddl/schema.sql | 33 ++++++++ .../ejercicio-82/dml/inserts.sql | 29 +++++++ .../ejercicio-82/dql/consultas.sql | 63 +++++++++++++++ .../ejercicio-82/evidencias/resultados.md | 56 +++++++++++++ 5 files changed, 261 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-82/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-82/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-82/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-82/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-82/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-82/README.md b/resoluciones/maria-montepeque/ejercicio-82/README.md new file mode 100644 index 00000000..1bf9d9dc --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-82/README.md @@ -0,0 +1,80 @@ +# Ejercicio 82: SELECT Nivel Aplicado + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +SELECT + +## Descripcion del problema + +Una biblioteca tecnica presta libros y necesita saber, en cualquier +momento, cuantos ejemplares de cada libro siguen disponibles: un caso +de negocio con reporte final, propio del nivel aplicado, que combina +varias tecnicas de `SELECT` en una sola consulta. + +## Tablas y relaciones + +- `autores`: catalogo de autores. +- `libros`: catalogo de libros, cada uno de un autor, con su cantidad + de ejemplares totales. +- `prestamos`: tabla principal, cada prestamo de un libro. + `fecha_devolucion` queda en `NULL` mientras el prestamo sigue + activo. `autores` 1—N `libros`; `libros` 1—N `prestamos`. + +## Uso de SELECT + +En `dql/consultas.sql`, la consulta 5 es el reporte final del caso de +negocio: + +1. Subconsulta correlacionada: por cada libro, cuenta cuantos + prestamos activos tiene (`WHERE p.id_libro = l.id_libro AND + p.fecha_devolucion IS NULL`). A diferencia de la subconsulta del + nivel intermedio (que se calculaba una sola vez), esta se vuelve a + evaluar por cada fila de `libros`. +2. Expresion calculada: `ejemplares_totales` menos esa subconsulta da + los ejemplares disponibles reales. +3. `CASE WHEN`: traduce el numero de disponibles en un estado legible + (`'disponible'` o `'agotado'`), sin que el usuario tenga que + interpretar un numero. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `libros.id_autor`, `prestamos.id_libro`. +- `NOT NULL` en todas las columnas obligatorias (excepto + `fecha_devolucion`, que es `NULL` a proposito mientras el prestamo + sigue activo). +- `UNIQUE`: `autores.nombre_autor`. +- `CHECK`: `libros.ejemplares_totales > 0`. +- `DEFAULT` en `prestamos.fecha_prestamo`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dql/consultas.sql`) + +`SELECT COUN(*) FROM libros;` falla porque `COUN` no es una funcion +valida (el nombre correcto es `COUNT`). Se valido con Python +(`sqlite3`): lanza `no such function: COUN`. + +## 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: 3 autores, 5 libros, 7 prestamos (2 devueltos, 5 + activos). Reporte final: Clean Architecture y The Art of Computer + Programming quedan `agotado`; los otros 3 libros quedan + `disponible`. + +## Como ejecutar + +```bash +sqlite3 ejercicio-82.db < ddl/schema.sql +sqlite3 ejercicio-82.db < dml/inserts.sql +sqlite3 ejercicio-82.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-82/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-82/ddl/schema.sql new file mode 100644 index 00000000..505abedc --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-82/ddl/schema.sql @@ -0,0 +1,33 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 82: SELECT Nivel Aplicado +-- Tema central: SELECT +-- Contexto: biblioteca tecnica, prestamos de libros. + +CREATE TABLE autores ( + id_autor INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_autor TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +CREATE TABLE libros ( + id_libro INTEGER PRIMARY KEY AUTOINCREMENT, + titulo TEXT NOT NULL, + id_autor INTEGER NOT NULL, + categoria TEXT NOT NULL, + ejemplares_totales INTEGER NOT NULL CHECK (ejemplares_totales > 0), + + FOREIGN KEY (id_autor) REFERENCES autores (id_autor) +); + +-- prestamos: fecha_devolucion queda NULL mientras el prestamo sigue +-- activo (el libro todavia no se devuelve). +CREATE TABLE prestamos ( + id_prestamo INTEGER PRIMARY KEY AUTOINCREMENT, + id_libro INTEGER NOT NULL, + nombre_prestatario TEXT NOT NULL, + fecha_prestamo TEXT NOT NULL DEFAULT (date('now')), + fecha_devolucion TEXT, + + FOREIGN KEY (id_libro) REFERENCES libros (id_libro) +); diff --git a/resoluciones/maria-montepeque/ejercicio-82/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-82/dml/inserts.sql new file mode 100644 index 00000000..b59c8947 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-82/dml/inserts.sql @@ -0,0 +1,29 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 82: SELECT Nivel Aplicado +-- Datos de prueba. + +INSERT INTO autores (nombre_autor, especialidad) VALUES + ('Robert C. Martin', 'Ingenieria de Software'), + ('Donald Knuth', 'Algoritmos'), + ('Martin Fowler', 'Arquitectura de Software'); + +INSERT INTO libros (titulo, id_autor, categoria, ejemplares_totales) VALUES + ('Clean Code', 1, 'Ingenieria', 2), + ('Clean Architecture', 1, 'Arquitectura', 1), + ('The Art of Computer Programming Vol. 1', 2, 'Algoritmos', 1), + ('Refactoring', 3, 'Arquitectura', 3), + ('Patterns of Enterprise Application Architecture', 3, 'Arquitectura', 2); + +-- Prestamos ya devueltos. +INSERT INTO prestamos (id_libro, nombre_prestatario, fecha_prestamo, fecha_devolucion) VALUES + (1, 'Karla Rivas', '2026-08-01', '2026-08-10'), + (4, 'Karla Rivas', '2026-08-04', '2026-08-12'); + +-- Prestamos todavia activos (fecha_devolucion en NULL). +INSERT INTO prestamos (id_libro, nombre_prestatario, fecha_prestamo) VALUES + (1, 'Bryan Solis', '2026-08-05'), + (2, 'Fernanda Lopez', '2026-08-02'), + (3, 'Jorge Cifuentes', '2026-08-03'), + (4, 'Priscila Ajanel', '2026-08-06'), + (5, 'Bryan Solis', '2026-08-07'); diff --git a/resoluciones/maria-montepeque/ejercicio-82/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-82/dql/consultas.sql new file mode 100644 index 00000000..122c3c4d --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-82/dql/consultas.sql @@ -0,0 +1,63 @@ +.headers on +.mode column + +-- Ejercicio 82: SELECT Nivel Aplicado +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales: prestamos con su libro y su +-- autor, con alias descriptivos. +SELECT p.id_prestamo, + l.titulo AS libro, + a.nombre_autor AS autor, + p.nombre_prestatario, + p.fecha_prestamo, + p.fecha_devolucion +FROM prestamos p +JOIN libros l ON l.id_libro = p.id_libro +JOIN autores a ON a.id_autor = l.id_autor; + +-- 2. Consulta con WHERE: solo los prestamos activos (todavia no se +-- devuelve el libro). +SELECT id_prestamo, id_libro, nombre_prestatario, fecha_prestamo +FROM prestamos +WHERE fecha_devolucion IS NULL; + +-- 3. Consulta con ORDER BY: prestamos ordenados por fecha. +SELECT id_prestamo, fecha_prestamo, fecha_devolucion +FROM prestamos +ORDER BY fecha_prestamo; + +-- 4. Conteo o resumen: total de prestamos por libro. +SELECT id_libro, COUNT(*) AS total_prestamos +FROM prestamos +GROUP BY id_libro; + +-- 5. Caso de negocio con reporte final (nivel aplicado): disponibi- +-- lidad real de cada libro, calculada con una subconsulta correla- +-- cionada (ejemplares totales menos prestamos activos de ese libro +-- especifico) y un CASE WHEN que traduce el numero a un estado +-- legible. Esto demuestra que SELECT puede combinar varias tecnicas +-- para responder una pregunta de negocio real: "que libros quedan +-- disponibles ahora mismo". +SELECT l.titulo, + l.ejemplares_totales, + l.ejemplares_totales - ( + SELECT COUNT(*) + FROM prestamos p + WHERE p.id_libro = l.id_libro AND p.fecha_devolucion IS NULL + ) AS ejemplares_disponibles, + CASE + WHEN l.ejemplares_totales - ( + SELECT COUNT(*) + FROM prestamos p + WHERE p.id_libro = l.id_libro AND p.fecha_devolucion IS NULL + ) > 0 THEN 'disponible' + ELSE 'agotado' + END AS estado_disponibilidad +FROM libros l +ORDER BY l.titulo; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: escribir mal el nombre de una funcion de agregacion +-- (COUN en vez de COUNT). +-- SELECT COUN(*) FROM libros; diff --git a/resoluciones/maria-montepeque/ejercicio-82/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-82/evidencias/resultados.md new file mode 100644 index 00000000..267256c2 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-82/evidencias/resultados.md @@ -0,0 +1,56 @@ +# Evidencias - Ejercicio 82 + +## Tema + +SELECT + +## 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-82.db < ddl/schema.sql +sqlite3 ejercicio-82.db < dml/inserts.sql +sqlite3 ejercicio-82.db < dql/consultas.sql +``` + +## Resultados + +**Caso comentado verificado:** + +- `SELECT COUN(*) FROM libros;` → `no such function: COUN` (funcion mal escrita, la correcta es `COUNT`). + +**5. Reporte final del caso de negocio (nivel aplicado): disponibilidad +real de cada libro:** + +```text +titulo ejemplares_totales ejemplares_disponibles estado_disponibilidad +Clean Architecture 1 0 agotado +Clean Code 2 1 disponible +Patterns of Enterprise Application Architecture 2 1 disponible +Refactoring 3 2 disponible +The Art of Computer Programming Vol. 1 1 0 agotado +``` + +Verificacion manual: Clean Architecture tiene 1 ejemplar total y 1 +prestamo activo (Fernanda Lopez) => 0 disponibles => agotado. The Art +of Computer Programming tiene 1 ejemplar total y 1 prestamo activo +(Jorge Cifuentes) => 0 disponibles => agotado. Los otros tres libros +tienen al menos 1 ejemplar disponible. + +## Aprendizaje + +La subconsulta correlacionada (`SELECT COUNT(*) FROM prestamos p +WHERE p.id_libro = l.id_libro AND p.fecha_devolucion IS NULL`) se +vuelve a ejecutar una vez por cada fila de `libros`, usando el +`id_libro` de esa fila especifica: es distinta de la subconsulta del +nivel intermedio, que se calculaba una sola vez para toda la +consulta. Combinada con `CASE WHEN`, permite traducir un numero +(ejemplares disponibles) en una palabra legible +(`'disponible'`/`'agotado'`), que es justo el tipo de reporte final +que un negocio real necesita para tomar una decision (por ejemplo, +cuales libros comprar mas ejemplares). El caso comentado recuerda que +un typo en el nombre de una funcion (`COUN` en vez de `COUNT`) tambien +hace fallar un `SELECT`. From 066df93231b98922843ee4a677ee3f08a9249221 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 06:55:19 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 082 (academia kickboxing) --- .../solicitudes-sql/ejercicio-082/README.md | 87 +++++++++++++++++ .../ejercicio-082/analisis/requerimiento.md | 95 +++++++++++++++++++ .../ejercicio-082/ddl/schema.sql | 67 +++++++++++++ .../ejercicio-082/diagramas/.gitkeep | 0 .../ejercicio-082/diagramas/diagrama-er.svg | 61 ++++++++++++ .../ejercicio-082/dml/inserts.sql | 47 +++++++++ .../ejercicio-082/dml/operaciones.sql | 22 +++++ .../ejercicio-082/dql/consultas.sql | 39 ++++++++ .../ejercicio-082/evidencias/resultados.md | 77 +++++++++++++++ 9 files changed, 495 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/README.md new file mode 100644 index 00000000..b81f06ef --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/README.md @@ -0,0 +1,87 @@ +# Solicitud SQL - Ejercicio 082: Academia Kickboxing + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Una academia gestiona alumnos, planes, entrenadores y asistencias. El +cliente pide saber quien compro, que compro, cuando ocurrio y cuanto +dinero representa cada movimiento. 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 + +A diferencia de otras solicitudes de esta serie donde esta misma +frase ("quien compro, que compro...") no encajaba directo con el +dominio, aqui si: un alumno paga un plan, en una fecha, por un monto. +Es un nivel 5 (solicitud profesional): ademas del modelo, se pide +interpretar ambiguedad, normalizar datos, documentar decisiones y +crear al menos una vista SQL. El detalle completo del analisis, +incluidas las decisiones de modelado, esta en +[analisis/requerimiento.md](analisis/requerimiento.md). + +## Que tablas cree y por que + +- `alumnos`: catalogo de alumnos inscritos. +- `planes`: catalogo de planes de membresia. +- `entrenadores`: catalogo de entrenadores. +- `asistencias`: tabla transaccional, cada clase a la que asiste un + alumno. `UNIQUE (id_alumno, id_entrenador, fecha_clase)` evita + registrar la misma clase dos veces. +- `pagos`: responde directamente la pregunta del cliente (quien pago, + que plan, cuando y cuanto). + +## Vista SQL + +`vista_pagos_alumnos` (definida en [ddl/schema.sql](ddl/schema.sql)) +junta pago, alumno y plan en una sola consulta legible, respondiendo +literalmente lo que pidio el cliente sin repetir el `JOIN` cada vez. + +## Como se relacionan + +`alumnos` 1:N `asistencias`; `entrenadores` 1:N `asistencias`; +`alumnos` 1:N `pagos`; `planes` 1:N `pagos`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +4 alumnos, 3 planes, 2 entrenadores, 7 asistencias (incluida una +cargada por error para un alumno que en realidad no asistio) y 4 +pagos (2 `pendiente`, 2 `pagado`). Tambien un `INSERT` comentado que +reproduce el problema de registrar la misma asistencia dos veces 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 la asistencia marcada por error (solo cuando es un error +de captura confirmado) y un `UPDATE` de estado (un pago pendiente se +confirma como pagado). + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): el resumen completo de +pagos usando la vista (responde literalmente quien, que, cuando y +cuanto), en que estado esta cada pago, que alumno tiene mas +asistencias, los pagos ordenados por fecha, y un reporte con +`GROUP BY` + `HAVING` (tambien sobre la vista) de ingresos totales por +plan, para decidir en cual invertir mas promocion. + +## 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-082.db < ddl/schema.sql +sqlite3 ejercicio-082.db < dml/inserts.sql +sqlite3 ejercicio-082.db < dml/operaciones.sql +sqlite3 ejercicio-082.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/analisis/requerimiento.md new file mode 100644 index 00000000..51722ca0 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/analisis/requerimiento.md @@ -0,0 +1,95 @@ +# Analisis del requerimiento - Ejercicio 082 + +## Solicitud entendida + +Una academia de kickboxing gestiona alumnos, planes de membresia, +entrenadores y asistencias a clases. El cliente pide saber quien +compro, que compro, cuando ocurrio y cuanto dinero representa cada +movimiento: en esta academia eso se traduce directo en la tabla de +pagos (quien = alumno, que = plan, cuando = fecha_pago, cuanto = +monto). Es un nivel 5 (solicitud profesional): ademas del modelo, se +pide interpretar ambiguedad, normalizar datos, documentar decisiones +y crear al menos una vista SQL. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| alumnos | Catalogo: cada alumno inscrito | nombre_alumno, telefono (unico) | +| planes | Catalogo: cada plan de membresia disponible | nombre_plan (unico), precio_mensual, clases_por_semana | +| entrenadores | Catalogo: cada entrenador de la academia | nombre_entrenador (unico), especialidad | +| asistencias | Tabla transaccional: cada clase a la que asiste un alumno | fecha_clase | +| pagos | Resultado: quien pago, que plan, cuando y cuanto (respuesta directa a la solicitud del cliente) | monto, fecha_pago, estado | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| alumnos -> asistencias | 1:N | Un alumno asiste a muchas clases. | +| entrenadores -> asistencias | 1:N | Un entrenador da muchas clases. | +| alumnos -> pagos | 1:N | Un alumno puede tener varios pagos (uno por mes, por ejemplo). | +| planes -> pagos | 1:N | Un plan se paga en muchos pagos distintos. | + +## Decisiones de modelado y ambiguedad interpretada + +- **Mapeo directo de la solicitud:** "quien compro, que compro, cuando + y cuanto dinero" se resolvio sin ambiguedad porque el negocio de la + academia ya tiene ese concepto exacto: un alumno paga + (`pagos.id_alumno`) un plan (`pagos.id_plan`), en una fecha + (`pagos.fecha_pago`), por un monto (`pagos.monto`). No hizo falta + forzar una interpretacion distinta, a diferencia de otras + solicitudes de esta serie donde la misma frase no encajaba + directamente con el dominio. +- **Normalizacion:** `planes.precio_mensual` vive en el catalogo, pero + el monto real cobrado se guarda aparte en `pagos.monto`, porque el + cliente podria pagar con descuento o un monto distinto al de lista + (el cliente no lo prohibio, asi que se deja abierto). +- **Registros repetidos (ambiguedad no explicita, pero relevante):** + el cliente no menciono el problema de datos duplicados en este + ejercicio, pero por buena practica se agrego + `UNIQUE (id_alumno, id_entrenador, fecha_clase)` en `asistencias`, + para que la misma asistencia no se pueda registrar dos veces por + error. +- **Vista SQL:** se crea `vista_pagos_alumnos`, que responde + literalmente la pregunta del cliente (quien, que, cuando, cuanto) en + una sola consulta legible, sin repetir el `JOIN` cada vez. + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): toda asistencia debe apuntar a un + alumno y a un entrenador reales; todo pago debe apuntar a un alumno + y a un plan reales (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `alumnos.telefono`, + `planes.nombre_plan` y `entrenadores.nombre_entrenador` no se + repiten (`UNIQUE`); una asistencia no se registra dos veces para el + mismo alumno, entrenador y fecha (`UNIQUE` compuesto). +- Regla 3 (valores fuera de rango): `planes.precio_mensual`, + `planes.clases_por_semana` y `pagos.monto` nunca negativos o cero + segun corresponda (`CHECK`). +- Regla 4: un pago nace `'pendiente'` y avanza a `'pagado'` o + `'vencido'` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: una asistencia se puede eliminar con `DELETE` solo cuando + fue un error de captura confirmado (por ejemplo, se marco + asistencia a un alumno que en realidad no fue). No se borra el + historico de asistencias reales, ni se usa `DELETE` para "dar de + baja" a un alumno que deja de venir (eso se maneja simplemente + dejando de registrar asistencias nuevas, sin tocar las anteriores). + +## Supuestos + +- El cliente no detallo si un alumno puede tener varios planes al + mismo tiempo; se asume que si, mientras cada pago referencia el plan + que corresponde a ese cobro especifico. +- No se detallo un limite de asistencias por semana segun el plan + (`clases_por_semana`); se asume que ese limite es informativo para + el negocio y no se valida automaticamente en este nivel. + +## Preguntas que responde la base de datos + +1. Quien pago, que plan, cuando y cuanto (via la vista + `vista_pagos_alumnos`). +2. Que pagos estan pendientes, pagados o vencidos. +3. Que alumno tiene mas asistencias (ranking de actividad). +4. Como se ordenan los pagos por fecha. +5. Que plan genero mas ingresos, para decidir en cual invertir mas + promocion. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/ddl/schema.sql new file mode 100644 index 00000000..a214981e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/ddl/schema.sql @@ -0,0 +1,67 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 082: Academia Kickboxing +-- Modelo: alumnos + entrenadores -> asistencias (1:N cada una); +-- alumnos + planes -> pagos (1:N cada una). + +CREATE TABLE alumnos ( + id_alumno INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_alumno TEXT NOT NULL, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE planes ( + id_plan INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_plan TEXT NOT NULL UNIQUE, + precio_mensual REAL NOT NULL CHECK (precio_mensual >= 0), + clases_por_semana INTEGER NOT NULL CHECK (clases_por_semana > 0) +); + +CREATE TABLE entrenadores ( + id_entrenador INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_entrenador TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +-- asistencias: el UNIQUE compuesto impide registrar la misma +-- asistencia dos veces (mismo alumno, mismo entrenador, misma fecha). +CREATE TABLE asistencias ( + id_asistencia INTEGER PRIMARY KEY AUTOINCREMENT, + id_alumno INTEGER NOT NULL, + id_entrenador INTEGER NOT NULL, + fecha_clase TEXT NOT NULL, + + FOREIGN KEY (id_alumno) REFERENCES alumnos (id_alumno), + FOREIGN KEY (id_entrenador) REFERENCES entrenadores (id_entrenador), + UNIQUE (id_alumno, id_entrenador, fecha_clase) +); + +-- pagos: responde directamente la solicitud del cliente (quien pago, +-- que plan, cuando y cuanto). +CREATE TABLE pagos ( + id_pago INTEGER PRIMARY KEY AUTOINCREMENT, + id_alumno INTEGER NOT NULL, + id_plan INTEGER NOT NULL, + monto REAL NOT NULL CHECK (monto >= 0), + fecha_pago TEXT NOT NULL DEFAULT (date('now')), + estado TEXT NOT NULL DEFAULT 'pendiente' + CHECK (estado IN ('pendiente', 'pagado', 'vencido')), + + FOREIGN KEY (id_alumno) REFERENCES alumnos (id_alumno), + FOREIGN KEY (id_plan) REFERENCES planes (id_plan) +); + +-- Vista SQL (requerida en nivel 5): responde literalmente la +-- pregunta del cliente en una sola consulta legible, sin repetir el +-- JOIN de 3 tablas cada vez. +CREATE VIEW vista_pagos_alumnos AS + SELECT + pg.id_pago, + al.nombre_alumno, + pl.nombre_plan, + pg.monto, + pg.fecha_pago, + pg.estado + FROM pagos pg + JOIN alumnos al ON al.id_alumno = pg.id_alumno + JOIN planes pl ON pl.id_plan = pg.id_plan; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/diagrama-er.svg new file mode 100644 index 00000000..c02e3aae --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/diagramas/diagrama-er.svg @@ -0,0 +1,61 @@ + + + + + alumnos + id_alumno PK + nombre_alumno + telefono UNIQUE + + + entrenadores + id_entrenador PK + nombre_entrenador UNIQUE + especialidad + + + asistencias + id_asistencia PK + id_alumno FK, id_entrenador FK + UNIQUE(alumno,entrenador,fecha) + + + planes + id_plan PK + nombre_plan UNIQUE + precio_mensual + + + pagos + id_pago PK + id_alumno FK, id_plan FK + monto, estado CHECK + + + vista_pagos_alumnos + JOIN pagos+alumnos+planes + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/inserts.sql new file mode 100644 index 00000000..7d460429 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/inserts.sql @@ -0,0 +1,47 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 082: Academia Kickboxing +-- Datos base: 4 alumnos, 3 planes, 2 entrenadores, 7 asistencias +-- (incluye 1 cargada por error para un alumno que en realidad no +-- fue) y 4 pagos. + +INSERT INTO alumnos (nombre_alumno, telefono) VALUES + ('Manuel Estrada', '5555-8001'), + ('Alejandra Chinchilla', '5555-8002'), + ('Byron Xicay', '5555-8003'), + ('Cristina Barrios', '5555-8004'); + +INSERT INTO planes (nombre_plan, precio_mensual, clases_por_semana) VALUES + ('Plan Basico', 250.00, 2), + ('Plan Intermedio', 400.00, 4), + ('Plan Premium', 600.00, 6); + +INSERT INTO entrenadores (nombre_entrenador, especialidad) VALUES + ('Coach Hugo Marroquin', 'Kickboxing basico'), + ('Coach Esteban Cifuentes', 'Kickboxing avanzado'); + +INSERT INTO asistencias (id_alumno, id_entrenador, fecha_clase) VALUES + (1, 1, '2026-08-01'), + (1, 1, '2026-08-03'), + (2, 2, '2026-08-01'), + (2, 2, '2026-08-04'), + (3, 1, '2026-08-02'), + (4, 2, '2026-08-02'); + +-- Asistencia cargada por error: el entrenador marco a Byron Xicay en +-- una clase a la que en realidad no asistio. Se corrige con DELETE en +-- dml/operaciones.sql. +INSERT INTO asistencias (id_alumno, id_entrenador, fecha_clase) VALUES + (3, 1, '2026-08-05'); + +INSERT INTO pagos (id_alumno, id_plan, monto, estado) VALUES + (1, 2, 400.00, 'pendiente'), + (2, 1, 250.00, 'pagado'), + (3, 3, 600.00, 'pendiente'), + (4, 1, 250.00, 'pagado'); + +-- Caso comentado que debe fallar (queda comentado): registrar de +-- nuevo la asistencia de Manuel Estrada con el mismo entrenador el +-- mismo dia, exactamente el tipo de duplicado que este UNIQUE esta +-- disenado para evitar. +-- INSERT INTO asistencias (id_alumno, id_entrenador, fecha_clase) VALUES (1, 1, '2026-08-01'); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/operaciones.sql new file mode 100644 index 00000000..cc7a7bb0 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dml/operaciones.sql @@ -0,0 +1,22 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 082: Academia Kickboxing +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 DELETE controlado: se confirmo que Byron Xicay no asistio a la +-- clase del 2026-08-05; el entrenador la habia marcado por error. +DELETE FROM asistencias +WHERE id_alumno = 3 AND id_entrenador = 1 AND fecha_clase = '2026-08-05'; + +-- 1 UPDATE de estado: el pago pendiente de Manuel Estrada se confirma +-- como pagado. +UPDATE pagos +SET estado = 'pagado' +WHERE id_alumno = 1 AND estado = 'pendiente'; + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar una asistencia real (no un error de captura confirmado), +-- por ejemplo la primera clase de Manuel Estrada. El historico de +-- asistencias reales no se borra; si un alumno deja la academia, +-- simplemente no se le registran clases nuevas. +-- DELETE FROM asistencias WHERE id_alumno = 1 AND fecha_clase = '2026-08-01'; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dql/consultas.sql new file mode 100644 index 00000000..4a3d76cf --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/dql/consultas.sql @@ -0,0 +1,39 @@ +.headers on +.mode column + +-- Ejercicio 082: Academia Kickboxing +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: se usa la vista +-- vista_pagos_alumnos (creada en ddl/schema.sql), que responde +-- directamente quien pago, que plan, cuando y cuanto. +SELECT * +FROM vista_pagos_alumnos; + +-- 2. Que pagos estan pendientes, pagados o vencidos. +SELECT id_pago, id_alumno, estado +FROM pagos +ORDER BY estado; + +-- 3. Que alumno tiene mas asistencias (ranking de actividad). +SELECT al.nombre_alumno, COUNT(*) AS total_asistencias +FROM alumnos al +JOIN asistencias a ON a.id_alumno = al.id_alumno +GROUP BY al.id_alumno, al.nombre_alumno +ORDER BY total_asistencias DESC, al.nombre_alumno; + +-- 4. Pagos ordenados por fecha. +SELECT id_pago, fecha_pago, monto +FROM pagos +ORDER BY fecha_pago; + +-- 5. Reporte para decision de negocio: ingresos totales por plan, +-- para decidir en cual invertir mas promocion (GROUP BY + HAVING, +-- usando la vista para no repetir el JOIN). +SELECT nombre_plan, + SUM(monto) AS ingresos_totales +FROM vista_pagos_alumnos +WHERE estado = 'pagado' +GROUP BY nombre_plan +HAVING SUM(monto) > 0 +ORDER BY ingresos_totales DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/evidencias/resultados.md new file mode 100644 index 00000000..f1045be0 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-082/evidencias/resultados.md @@ -0,0 +1,77 @@ +# Evidencias - Solicitudes SQL - Ejercicio 082 (Academia Kickboxing) + +## 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-082.db < ddl/schema.sql +sqlite3 ejercicio-082.db < dml/inserts.sql +sqlite3 ejercicio-082.db < dml/operaciones.sql +sqlite3 ejercicio-082.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 4 alumnos, 3 planes, 2 +entrenadores, 7 asistencias (incluye la cargada por error para Byron +Xicay) y 4 pagos (2 `pendiente`, 2 `pagado`). + +**Caso comentado verificado:** + +- `INSERT INTO asistencias (id_alumno, id_entrenador, fecha_clase) VALUES (1, 1, '2026-08-01');` (repetir la asistencia de Manuel Estrada) → `UNIQUE constraint failed: asistencias.id_alumno, asistencias.id_entrenador, asistencias.fecha_clase`. + +**1. Quien pago, que plan, cuando y cuanto, via `vista_pagos_alumnos` +(ya con el pago de Manuel Estrada confirmado):** + +```text +id_pago | nombre_alumno | nombre_plan | monto | fecha_pago | estado +1 | Manuel Estrada | Plan Intermedio | 400.0 | 2026-08-25 | pagado +2 | Alejandra Chinchilla | Plan Basico | 250.0 | 2026-08-25 | pagado +3 | Byron Xicay | Plan Premium | 600.0 | 2026-08-25 | pendiente +4 | Cristina Barrios | Plan Basico | 250.0 | 2026-08-25 | pagado +``` + +**3. Alumno con mas asistencias:** + +```text +nombre_alumno total_asistencias +Alejandra Chinchilla 2 +Manuel Estrada 2 +Byron Xicay 1 +Cristina Barrios 1 +``` + +(Byron Xicay quedo en 1 asistencia real, no 2: la que se le habia +marcado por error ya se elimino.) + +**5. Ingresos totales por plan (solo pagos `pagado`), para decidir en +cual invertir mas promocion:** + +```text +nombre_plan ingresos_totales +Plan Basico 500.0 +Plan Intermedio 400.0 +``` + +(Plan Premium no aparece: su unico pago sigue `pendiente`.) + +## Operaciones de mantenimiento verificadas + +- **DELETE controlado**: se elimino la asistencia que se le habia marcado por error a Byron Xicay el 2026-08-05. Total de asistencias: 7 -> 6. +- `UPDATE pagos SET estado = 'pagado' WHERE id_alumno = 1 ...;` → el pago de Manuel Estrada se confirmo. + +## Aprendizaje + +El `UNIQUE (id_alumno, id_entrenador, fecha_clase)` en `asistencias` +evita registrar la misma clase dos veces para el mismo alumno, aunque +el cliente no lo pidiera explicitamente: es una buena practica que se +aplico igual, y se documento como tal en el analisis. La vista +`vista_pagos_alumnos` responde literalmente la pregunta que trajo el +cliente (quien compro, que, cuando y cuanto), demostrando que a veces +la solicitud del cliente encaja de forma directa con el dominio del +negocio, sin necesitar una reinterpretacion forzada. El `DELETE` +controlado solo corrige errores de captura confirmados; el historico +real de asistencias nunca se borra.