Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-80/README.md
Original file line number Diff line number Diff line change
@@ -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`.
30 changes: 30 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-80/ddl/schema.sql
Original file line number Diff line number Diff line change
@@ -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)
);
23 changes: 23 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-80/dml/inserts.sql
Original file line number Diff line number Diff line change
@@ -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);
48 changes: 48 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-80/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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`.
Loading
Loading