From 5be9a962cb1e0aacf1fff65a3bd0d55c40d21fa9 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 19:22:49 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 80 --- .../maria-montepeque/ejercicio-80/README.md | 79 +++++++++++++++++ .../ejercicio-80/ddl/schema.sql | 30 +++++++ .../ejercicio-80/dml/inserts.sql | 23 +++++ .../ejercicio-80/dql/consultas.sql | 48 +++++++++++ .../ejercicio-80/evidencias/resultados.md | 85 +++++++++++++++++++ 5 files changed, 265 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-80/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-80/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-80/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-80/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-80/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-80/README.md b/resoluciones/maria-montepeque/ejercicio-80/README.md new file mode 100644 index 00000000..a815213d --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-80/README.md @@ -0,0 +1,79 @@ +# Ejercicio 80: SELECT Nivel Basico + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +SELECT + +## Descripcion del problema + +Un sistema de registro de campers necesita mostrar la informacion de +campers, rutas e inscripciones de forma legible: con nombres de +columna claros, valores calculados (como la edad o un descuento) y +resultados ordenados, no solo los datos crudos de las tablas. + +## Tablas y relaciones + +- `campers`: catalogo de campers, con su fecha de nacimiento y nivel. +- `rutas`: catalogo de rutas, con distancia y costo de inscripcion. +- `inscripciones`: relaciona un camper con una ruta. `campers` 1—N + `inscripciones`; `rutas` 1—N `inscripciones`. + +## Uso de SELECT + +En `dql/consultas.sql`: + +1. Alias de columnas (`AS`): `nombre AS camper`, + `nivel AS nivel_experiencia`, para que el resultado se lea mas + claro que los nombres tecnicos de la tabla. +2. Expresion calculada: la edad aproximada de cada camper se calcula + a partir de `fecha_nacimiento` con `julianday('now')`, sin que esa + edad este guardada en ninguna columna. +3. `WHERE`, `ORDER BY` y `GROUP BY` con `COUNT`, para filtrar, ordenar + y resumir. +4. La consulta 5 combina todo: `JOIN` entre `inscripciones`, + `campers` y `rutas`, con alias descriptivos y una segunda expresion + calculada (costo con 10% de descuento), armando un reporte legible + de una sola vez. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `inscripciones.id_camper`, `inscripciones.id_ruta`. +- `NOT NULL` en todas las columnas obligatorias. +- `UNIQUE`: `rutas.nombre_ruta`. +- `CHECK`: `campers.nivel IN (...)`, `rutas.distancia_km > 0`, + `rutas.costo_inscripcion >= 0`. +- `DEFAULT` en `campers.nivel` e `inscripciones.fecha_inscripcion`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dql/consultas.sql`) + +`SELECT nombre, apellido FROM campers;` falla porque la tabla +`campers` no tiene ninguna columna `apellido` (typo). Se valido con +Python (`sqlite3`): lanza `no such column: apellido`. Recuerda que +`SELECT` tambien puede fallar por errores simples de escritura, no +solo por restricciones de la base de datos. + +## Evidencias de ejecucion + +Scripts validados en orden (`ddl` -> `inserts` -> `consultas`) con +Python (modulo `sqlite3`), ya que no se tenia el binario `sqlite3` +disponible en el entorno. Detalle completo en +[`evidencias/resultados.md`](evidencias/resultados.md). + +- Datos finales: 5 campers, 3 rutas, 5 inscripciones. Reporte final + con costo y descuento calculado correctamente para las 5 + inscripciones. + +## Como ejecutar + +```bash +sqlite3 ejercicio-80.db < ddl/schema.sql +sqlite3 ejercicio-80.db < dml/inserts.sql +sqlite3 ejercicio-80.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-80/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-80/ddl/schema.sql new file mode 100644 index 00000000..24792b22 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-80/ddl/schema.sql @@ -0,0 +1,30 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 80: SELECT Nivel Basico +-- Tema central: SELECT +-- Contexto: registro de campers inscritos en rutas de entrenamiento. + +CREATE TABLE campers ( + id_camper INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + fecha_nacimiento TEXT NOT NULL, + nivel TEXT NOT NULL DEFAULT 'principiante' + CHECK (nivel IN ('principiante', 'intermedio', 'avanzado')) +); + +CREATE TABLE rutas ( + id_ruta INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_ruta TEXT NOT NULL UNIQUE, + distancia_km REAL NOT NULL CHECK (distancia_km > 0), + costo_inscripcion REAL NOT NULL CHECK (costo_inscripcion >= 0) +); + +CREATE TABLE inscripciones ( + id_inscripcion INTEGER PRIMARY KEY AUTOINCREMENT, + id_camper INTEGER NOT NULL, + id_ruta INTEGER NOT NULL, + fecha_inscripcion TEXT NOT NULL DEFAULT (date('now')), + + FOREIGN KEY (id_camper) REFERENCES campers (id_camper), + FOREIGN KEY (id_ruta) REFERENCES rutas (id_ruta) +); diff --git a/resoluciones/maria-montepeque/ejercicio-80/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-80/dml/inserts.sql new file mode 100644 index 00000000..f6deadf9 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-80/dml/inserts.sql @@ -0,0 +1,23 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 80: SELECT Nivel Basico +-- Datos de prueba. + +INSERT INTO campers (nombre, fecha_nacimiento, nivel) VALUES + ('Karen Solis', '2000-03-15', 'avanzado'), + ('Mario Ixtabalan', '2003-07-22', 'intermedio'), + ('Ana Gomez', '2005-11-02', 'principiante'), + ('Luis Marroquin', '1998-01-30', 'avanzado'), + ('Rosa Chavez', '2001-09-18', 'intermedio'); + +INSERT INTO rutas (nombre_ruta, distancia_km, costo_inscripcion) VALUES + ('Cumbre Extrema', 18.5, 250.00), + ('Sendero del Canon', 9.2, 120.00), + ('Ruta del Volcan', 25.0, 300.00); + +INSERT INTO inscripciones (id_camper, id_ruta) VALUES + (1, 1), + (2, 1), + (3, 2), + (4, 3), + (5, 3); diff --git a/resoluciones/maria-montepeque/ejercicio-80/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-80/dql/consultas.sql new file mode 100644 index 00000000..2aaa47b2 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-80/dql/consultas.sql @@ -0,0 +1,48 @@ +.headers on +.mode column + +-- Ejercicio 80: SELECT Nivel Basico +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales, con alias de columnas +-- (AS) y una expresion calculada (edad aproximada a partir de la +-- fecha de nacimiento). +SELECT nombre AS camper, + nivel AS nivel_experiencia, + CAST((julianday('now') - julianday(fecha_nacimiento)) / 365.25 AS INTEGER) AS edad_aproximada +FROM campers; + +-- 2. Consulta con WHERE: solo los campers de nivel avanzado. +SELECT nombre, nivel +FROM campers +WHERE nivel = 'avanzado'; + +-- 3. Consulta con ORDER BY: rutas ordenadas por costo de +-- inscripcion, de mas barata a mas cara. +SELECT nombre_ruta, costo_inscripcion +FROM rutas +ORDER BY costo_inscripcion; + +-- 4. Conteo o resumen: total de campers por nivel. +SELECT nivel, COUNT(*) AS total +FROM campers +GROUP BY nivel; + +-- 5. Validacion especifica de SELECT: un reporte legible que combina +-- columnas de dos tablas (JOIN), alias descriptivos y una expresion +-- calculada (costo con 10% de descuento por inscripcion anticipada), +-- demostrando que SELECT no solo trae datos, tambien los presenta +-- listos para leer. +SELECT c.nombre AS camper, + r.nombre_ruta AS ruta, + r.costo_inscripcion AS costo_normal, + ROUND(r.costo_inscripcion * 0.9, 2) AS costo_con_descuento +FROM inscripciones i +JOIN campers c ON c.id_camper = i.id_camper +JOIN rutas r ON r.id_ruta = i.id_ruta +ORDER BY camper; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: seleccionar una columna que no existe en la tabla +-- (typo), en vez de verificar el nombre real de la columna primero. +-- SELECT nombre, apellido FROM campers; diff --git a/resoluciones/maria-montepeque/ejercicio-80/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-80/evidencias/resultados.md new file mode 100644 index 00000000..b31630ee --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-80/evidencias/resultados.md @@ -0,0 +1,85 @@ +# Evidencias - Ejercicio 80 + +## 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-80.db < ddl/schema.sql +sqlite3 ejercicio-80.db < dml/inserts.sql +sqlite3 ejercicio-80.db < dql/consultas.sql +``` + +## Resultados + +**1. Todos los campers, con alias de columnas y edad calculada:** + +```text +camper nivel_experiencia edad_aproximada +Karen Solis avanzado 26 +Mario Ixtabalan intermedio 23 +Ana Gomez principiante 20 +Luis Marroquin avanzado 28 +Rosa Chavez intermedio 24 +``` + +**2. Campers de nivel avanzado:** + +```text +nombre nivel +Karen Solis avanzado +Luis Marroquin avanzado +``` + +**3. Rutas ordenadas por costo de inscripcion:** + +```text +nombre_ruta costo_inscripcion +Sendero del Canon 120.00 +Cumbre Extrema 250.00 +Ruta del Volcan 300.00 +``` + +**4. Resumen: campers por nivel:** + +```text +nivel total +avanzado 2 +intermedio 2 +principiante 1 +``` + +**5. Reporte legible con JOIN, alias y expresion calculada (costo con +10% de descuento):** + +```text +camper ruta costo_normal costo_con_descuento +Ana Gomez Sendero del Canon 120.0 108.0 +Karen Solis Cumbre Extrema 250.0 225.0 +Luis Marroquin Ruta del Volcan 300.0 270.0 +Mario Ixtabalan Cumbre Extrema 250.0 225.0 +Rosa Chavez Ruta del Volcan 300.0 270.0 +``` + +**Caso comentado verificado:** + +- `SELECT nombre, apellido FROM campers;` → `no such column: apellido` (la tabla `campers` no tiene esa columna). + +## Aprendizaje + +`SELECT` no solo trae datos, tambien los presenta de forma legible: +un alias (`AS`) le da a una columna un nombre mas claro que el nombre +tecnico de la tabla, y una expresion calculada (como la edad a partir +de `fecha_nacimiento`, o el costo con descuento) permite mostrar un +dato util que no esta guardado directamente en ninguna columna. Al +combinar columnas de varias tablas con `JOIN`, esos mismos alias y +expresiones ayudan a que el resultado final se lea como un reporte, +no como una tabla cruda. El caso comentado recuerda que `SELECT` +tambien puede fallar por errores simples, como escribir mal el nombre +de una columna. From d9eb91ae12c055f5a3c474bb8e1970c522ccf7b1 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Mon, 24 Aug 2026 19:22:50 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 080 (cine horror nights) --- .../solicitudes-sql/ejercicio-080/README.md | 80 +++++++++++++++++++ .../ejercicio-080/analisis/requerimiento.md | 77 ++++++++++++++++++ .../ejercicio-080/ddl/schema.sql | 59 ++++++++++++++ .../ejercicio-080/diagramas/.gitkeep | 0 .../ejercicio-080/diagramas/diagrama-er.svg | 56 +++++++++++++ .../ejercicio-080/dml/inserts.sql | 64 +++++++++++++++ .../ejercicio-080/dml/operaciones.sql | 24 ++++++ .../ejercicio-080/dql/consultas.sql | 50 ++++++++++++ .../ejercicio-080/evidencias/resultados.md | 61 ++++++++++++++ 9 files changed, 471 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/README.md new file mode 100644 index 00000000..7ae73975 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/README.md @@ -0,0 +1,80 @@ +# Solicitud SQL - Ejercicio 080: Cine Horror Nights + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un cine organiza funciones de peliculas de miedo, salas y ventas de +boletos. 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. + +## Que entendi de la solicitud + +En un cine, el "registro repetido" mas clasico es vender el mismo +asiento dos veces para la misma funcion; eso es lo primero que el +modelo debe impedir. 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 + +- `peliculas`: catalogo de peliculas de terror. +- `salas`: catalogo de salas del cine. +- `funciones`: tabla transaccional, una pelicula proyectada en una + sala, en fecha y hora. +- `boletos`: detalle de cada funcion. Aqui esta el + `UNIQUE (id_funcion, asiento)` que impide vender el mismo asiento + dos veces. +- `pagos`: resultado de un boleto. El `UNIQUE (id_boleto)` garantiza + un solo pago oficial por boleto. + +## Como se relacionan + +`peliculas` 1:N `funciones`; `salas` 1:N `funciones`; `funciones` 1:N +`boletos`; `boletos` 1:1 `pagos`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +2 peliculas, 2 salas, 4 funciones (3 marcadas `finalizada` en algun +momento, 1 `programada`), 6 boletos (incluido uno vendido por error en +una funcion que despues se descubrio que habia que cancelar, todavia +sin pago) y 5 pagos. Tambien un `INSERT` comentado que reproduce el +problema de vender dos veces el mismo asiento 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 funcion con falla de proyector pasa a `cancelada`) y un `DELETE` +controlado que elimina el boleto sin pagar de esa funcion, sin tocar +ningun boleto ya pagado. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): que boletos existen (JOIN +pelicula-sala-funcion), en que estado esta cada funcion, que pelicula +vendio mas boletos, los boletos ordenados por precio, y un reporte con +`GROUP BY` + `HAVING` de ingresos totales por pelicula, para decidir +cual mantener en cartelera. + +## 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-080.db < ddl/schema.sql +sqlite3 ejercicio-080.db < dml/inserts.sql +sqlite3 ejercicio-080.db < dml/operaciones.sql +sqlite3 ejercicio-080.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/analisis/requerimiento.md new file mode 100644 index 00000000..d49d7711 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/analisis/requerimiento.md @@ -0,0 +1,77 @@ +# Analisis del requerimiento - Ejercicio 080 + +## Solicitud entendida + +Un cine organiza funciones de peliculas de miedo, en distintas salas, +con venta de boletos por asiento. El cliente quiere detectar tres +tipos de error: registros repetidos, relaciones invalidas y valores +fuera de rango. En un cine, el error clasico de "registro repetido" +es vender el mismo asiento dos veces para la misma funcion. Se +necesita una base de datos que permita consultar datos, corregir +estados, registrar movimientos y sacar reportes utiles. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| peliculas | Catalogo: cada pelicula de terror disponible | titulo (unico), clasificacion, duracion_minutos | +| salas | Catalogo: cada sala del cine | nombre_sala (unico), capacidad | +| funciones | Tabla transaccional: una pelicula proyectada en una sala, en fecha y hora | fecha_funcion, hora_funcion, estado | +| boletos | Detalle de cada funcion: cada asiento vendido | asiento, precio | +| pagos | Resultado: el pago de un boleto, uno por boleto | monto, metodo_pago | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| peliculas -> funciones | 1:N | Una pelicula se proyecta en varias funciones. | +| salas -> funciones | 1:N | Una sala tiene varias funciones a lo largo del tiempo. | +| funciones -> boletos | 1:N | Una funcion tiene un boleto por cada asiento vendido. | +| boletos -> pagos | 1:1 | Cada boleto tiene, como mucho, un pago oficial. | + +## Reglas de negocio + +Cada regla ataca uno de los tres errores que el cliente quiere +detectar: + +- Regla 1 (relaciones invalidas): toda funcion debe apuntar a una + pelicula y a una sala reales; todo boleto debe apuntar a una funcion + real; todo pago debe apuntar a un boleto real (`FOREIGN KEY` en + cadena). +- Regla 2 (registros repetidos): `peliculas.titulo` y + `salas.nombre_sala` no se repiten (`UNIQUE`); el mismo asiento no se + puede vender dos veces para la misma funcion + (`UNIQUE (id_funcion, asiento)`); un boleto no puede tener mas de un + pago (`UNIQUE (id_boleto)` en `pagos`). +- Regla 3 (valores fuera de rango): `peliculas.duracion_minutos` y + `salas.capacidad` siempre mayores que 0; `boletos.precio` y + `pagos.monto` nunca negativos (`CHECK`). +- Regla 4: una funcion nace `'programada'` y avanza a `'en_curso'`, + `'finalizada'` o `'cancelada'` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: un boleto solo se puede eliminar con `DELETE` mientras + todavia no tiene pago registrado (reserva sin cobrar). Un boleto ya + pagado es un resultado oficial y no se borra; si la funcion se + cancela, se corrige el estado de la funcion, no se elimina el + boleto pagado. + +## Supuestos + +- El cliente no detallo si un boleto puede reembolsarse; se asume que + el alcance de este nivel solo cubre boletos sin pagar (reservas), y + que un reembolso seria un caso futuro fuera de este modelo. +- No se detallo si una funcion puede repetirse en la misma sala el + mismo dia; se asume que si, siempre que sea en horarios distintos + (no se valida solapamiento de horario en este nivel). +- Se asume que el precio del boleto puede variar entre funciones de la + misma pelicula (por ejemplo, funciones nocturnas mas caras), por eso + `precio` se guarda en `boletos` y no en `peliculas`. + +## Preguntas que responde la base de datos + +1. Que boletos existen, con que pelicula, que sala y que funcion. +2. Que funciones estan programadas, en curso, finalizadas o + canceladas. +3. Que pelicula vendio mas boletos (ranking de actividad). +4. Como se ordenan los boletos por precio. +5. Que pelicula genero mas ingresos, para decidir cual mantener en + cartelera. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/ddl/schema.sql new file mode 100644 index 00000000..a0bbc786 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/ddl/schema.sql @@ -0,0 +1,59 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 080: Cine Horror Nights +-- Modelo: peliculas + salas -> funciones (1:N cada una); funciones +-- -> boletos (1:N); boletos -> pagos (1:1). 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 peliculas ( + id_pelicula INTEGER PRIMARY KEY AUTOINCREMENT, + titulo TEXT NOT NULL UNIQUE, + clasificacion TEXT NOT NULL CHECK (clasificacion IN ('PG-13', 'R', 'NC-17')), + duracion_minutos INTEGER NOT NULL CHECK (duracion_minutos > 0) +); + +CREATE TABLE salas ( + id_sala INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_sala TEXT NOT NULL UNIQUE, + capacidad INTEGER NOT NULL CHECK (capacidad > 0) +); + +CREATE TABLE funciones ( + id_funcion INTEGER PRIMARY KEY AUTOINCREMENT, + id_pelicula INTEGER NOT NULL, + id_sala INTEGER NOT NULL, + fecha_funcion TEXT NOT NULL, + hora_funcion TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'en_curso', 'finalizada', 'cancelada')), + + FOREIGN KEY (id_pelicula) REFERENCES peliculas (id_pelicula), + FOREIGN KEY (id_sala) REFERENCES salas (id_sala) +); + +-- boletos: el UNIQUE compuesto impide vender el mismo asiento dos +-- veces para la misma funcion. Es la restriccion que ataca +-- directamente el problema de "registros repetidos" en un cine. +CREATE TABLE boletos ( + id_boleto INTEGER PRIMARY KEY AUTOINCREMENT, + id_funcion INTEGER NOT NULL, + asiento TEXT NOT NULL, + precio REAL NOT NULL CHECK (precio >= 0), + + FOREIGN KEY (id_funcion) REFERENCES funciones (id_funcion), + UNIQUE (id_funcion, asiento) +); + +-- pagos: el UNIQUE sobre id_boleto garantiza como maximo un pago +-- oficial por boleto (relacion 1:1). +CREATE TABLE pagos ( + id_pago INTEGER PRIMARY KEY AUTOINCREMENT, + id_boleto INTEGER NOT NULL UNIQUE, + monto REAL NOT NULL CHECK (monto >= 0), + metodo_pago TEXT NOT NULL CHECK (metodo_pago IN ('efectivo', 'tarjeta')), + fecha_pago TEXT NOT NULL DEFAULT (datetime('now')), + + FOREIGN KEY (id_boleto) REFERENCES boletos (id_boleto) +); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/diagrama-er.svg new file mode 100644 index 00000000..37b59287 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/diagramas/diagrama-er.svg @@ -0,0 +1,56 @@ + + + + + peliculas + id_pelicula PK + titulo UNIQUE + clasificacion, duracion_minutos + + + salas + id_sala PK + nombre_sala UNIQUE, capacidad + + + funciones + id_funcion PK + id_pelicula FK, id_sala FK + fecha_funcion, hora_funcion + estado CHECK + + + boletos + id_boleto PK + id_funcion FK + asiento, precio + UNIQUE(funcion, asiento) + + + pagos + id_pago PK + id_boleto FK UNIQUE (1:1) + monto, metodo_pago + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:1 + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/inserts.sql new file mode 100644 index 00000000..46bc3581 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/inserts.sql @@ -0,0 +1,64 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 080: Cine Horror Nights +-- Datos base: 2 peliculas, 2 salas, 4 funciones (2 finalizadas, 1 +-- finalizada-por-error que se corrige despues, 1 programada), 6 +-- boletos (incluye 1 sin pagar en la funcion que se cancela) y 5 +-- pagos. + +INSERT INTO peliculas (titulo, clasificacion, duracion_minutos) VALUES + ('La Noche del Espanto', 'R', 105), + ('Grito Eterno', 'PG-13', 98); + +INSERT INTO salas (nombre_sala, capacidad) VALUES + ('Sala Terror 1', 50), + ('Sala Terror 2', 80); + +-- Funcion 1: La Noche del Espanto, Sala Terror 1, finalizada. +INSERT INTO funciones (id_pelicula, id_sala, fecha_funcion, hora_funcion, estado) VALUES + (1, 1, '2026-08-01', '20:00', 'finalizada'); + +-- Funcion 2: Grito Eterno, Sala Terror 2, finalizada. +INSERT INTO funciones (id_pelicula, id_sala, fecha_funcion, hora_funcion, estado) VALUES + (2, 2, '2026-08-01', '22:00', 'finalizada'); + +-- Funcion 3: La Noche del Espanto, Sala Terror 1. Se marco +-- 'finalizada' y se vendio un boleto, pero el proyector fallo y la +-- funcion se cancelo despues. Se corrige en dml/operaciones.sql. +INSERT INTO funciones (id_pelicula, id_sala, fecha_funcion, hora_funcion, estado) VALUES + (1, 1, '2026-08-02', '20:00', 'finalizada'); + +-- Funcion 4: Grito Eterno, Sala Terror 2, todavia no se proyecta. +INSERT INTO funciones (id_pelicula, id_sala, fecha_funcion, hora_funcion, estado) VALUES + (2, 2, '2026-08-03', '20:00', 'programada'); + +-- Boletos de la funcion 1. +INSERT INTO boletos (id_funcion, asiento, precio) VALUES + (1, 'A1', 45.00), + (1, 'A2', 45.00), + (1, 'A3', 45.00); + +-- Boletos de la funcion 2. +INSERT INTO boletos (id_funcion, asiento, precio) VALUES + (2, 'B1', 50.00), + (2, 'B2', 50.00); + +-- Boleto de la funcion 3, vendido antes de saber que el proyector +-- habia fallado. Todavia no tiene pago registrado; se elimina en +-- dml/operaciones.sql cuando la funcion se cancela. +INSERT INTO boletos (id_funcion, asiento, precio) VALUES + (3, 'A1', 45.00); + +-- Pagos de los 5 boletos de las funciones 1 y 2 (el boleto de la +-- funcion 3 se queda sin pago, por diseno). +INSERT INTO pagos (id_boleto, monto, metodo_pago) VALUES + (1, 45.00, 'tarjeta'), + (2, 45.00, 'efectivo'), + (3, 45.00, 'tarjeta'), + (4, 50.00, 'tarjeta'), + (5, 50.00, 'efectivo'); + +-- Caso comentado que debe fallar (queda comentado): vender de nuevo +-- el asiento A1 de la funcion 1, exactamente el problema que este +-- UNIQUE esta disenado para evitar. +-- INSERT INTO boletos (id_funcion, asiento, precio) VALUES (1, 'A1', 45.00); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/operaciones.sql new file mode 100644 index 00000000..992bf1d8 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dml/operaciones.sql @@ -0,0 +1,24 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 080: Cine Horror Nights +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que el proyector fallo durante la +-- funcion 3 y se cancela. +UPDATE funciones +SET estado = 'cancelada' +WHERE id_funcion = 3 AND estado = 'finalizada'; + +-- 1 DELETE controlado: el boleto de la funcion 3 todavia no tenia +-- pago registrado, asi que es seguro eliminarlo ahora que la funcion +-- se cancelo. Un boleto ya pagado (como los de las funciones 1 y 2) +-- nunca se borraria por este mismo motivo. +DELETE FROM boletos +WHERE id_funcion = 3 + AND id_boleto NOT IN (SELECT id_boleto FROM pagos); + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar un boleto que ya tiene pago registrado (resultado oficial +-- del cine). El DELETE de arriba solo alcanza boletos sin pago, por +-- diseno. +-- DELETE FROM boletos WHERE id_boleto = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dql/consultas.sql new file mode 100644 index 00000000..fd4d0aff --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/dql/consultas.sql @@ -0,0 +1,50 @@ +.headers on +.mode column + +-- Ejercicio 080: Cine Horror Nights +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: todos los boletos con su +-- pelicula, su sala y su funcion. +SELECT b.id_boleto, + p.titulo, + s.nombre_sala, + f.fecha_funcion, + b.asiento, + b.precio +FROM boletos b +JOIN funciones f ON f.id_funcion = b.id_funcion +JOIN peliculas p ON p.id_pelicula = f.id_pelicula +JOIN salas s ON s.id_sala = f.id_sala; + +-- 2. Que funciones estan programadas, en curso, finalizadas o +-- canceladas. +SELECT id_funcion, fecha_funcion, hora_funcion, estado +FROM funciones +ORDER BY estado; + +-- 3. Que pelicula vendio mas boletos (ranking de actividad). +SELECT p.titulo, COUNT(*) AS boletos_vendidos +FROM peliculas p +JOIN funciones f ON f.id_pelicula = p.id_pelicula +JOIN boletos b ON b.id_funcion = f.id_funcion +GROUP BY p.id_pelicula, p.titulo +ORDER BY boletos_vendidos DESC, p.titulo; + +-- 4. Boletos ordenados por precio, de mayor a menor. +SELECT p.titulo, b.asiento, b.precio +FROM boletos b +JOIN funciones f ON f.id_funcion = b.id_funcion +JOIN peliculas p ON p.id_pelicula = f.id_pelicula +ORDER BY b.precio DESC; + +-- 5. Reporte para decision de negocio: ingresos totales por pelicula, +-- para decidir cual mantener en cartelera (GROUP BY + HAVING). +SELECT p.titulo, + SUM(b.precio) AS ingresos_totales +FROM peliculas p +JOIN funciones f ON f.id_pelicula = p.id_pelicula +JOIN boletos b ON b.id_funcion = f.id_funcion +GROUP BY p.id_pelicula, p.titulo +HAVING SUM(b.precio) > 0 +ORDER BY ingresos_totales DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/evidencias/resultados.md new file mode 100644 index 00000000..74b60b7f --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-080/evidencias/resultados.md @@ -0,0 +1,61 @@ +# Evidencias - Solicitudes SQL - Ejercicio 080 (Cine Horror Nights) + +## 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-080.db < ddl/schema.sql +sqlite3 ejercicio-080.db < dml/inserts.sql +sqlite3 ejercicio-080.db < dml/operaciones.sql +sqlite3 ejercicio-080.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 2 peliculas, 2 salas, 4 funciones +(3 marcadas `finalizada` en algun momento, 1 `programada`), 6 boletos +(incluye el vendido por error en la funcion que se debia cancelar) y +5 pagos. + +**Caso comentado verificado** (el problema central del cliente): + +- `INSERT INTO boletos (id_funcion, asiento, ...) VALUES (1, 'A1', ...);` (vender de nuevo el asiento A1 de la funcion 1) → `UNIQUE constraint failed: boletos.id_funcion, boletos.asiento`. + +**2. Funciones por estado (la funcion 3 ya aparece `cancelada`, se +corrigio con el `UPDATE` de `dml/operaciones.sql`).** + +**3. Pelicula con mas boletos vendidos:** + +```text +titulo boletos_vendidos +La Noche del Espanto 3 +Grito Eterno 2 +``` + +**5. Ingresos totales por pelicula (para decidir cual mantener en +cartelera):** + +```text +titulo ingresos_totales +La Noche del Espanto 135.0 +Grito Eterno 100.0 +``` + +## Operaciones de mantenimiento verificadas + +- `UPDATE funciones SET estado = 'cancelada' WHERE id_funcion = 3 ...;` → la funcion del 2026-08-02 se anulo despues de confirmarse la falla del proyector. +- **DELETE controlado**: se elimino el unico boleto sin pago de la funcion 3 (asiento A1), apenas se marco `cancelada`. Total de boletos: 6 -> 5. Ningun boleto ya pagado (funciones 1 y 2) se toco. + +## Aprendizaje + +El `UNIQUE (id_funcion, asiento)` en `boletos` es la restriccion que +resuelve directamente el problema mas clasico de un cine: vender el +mismo asiento dos veces para la misma funcion. El `DELETE` controlado +solo alcanza boletos sin pago; uno ya pagado es un resultado oficial y +nunca se borra, aunque su funcion se cancele. El reporte de ingresos +por pelicula (`GROUP BY` + `HAVING`) confirma que, con un modelo sin +registros repetidos, es facil responder una pregunta real de negocio: +cual pelicula mantener en cartelera.