Skip to content
Open
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
85 changes: 85 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-76/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Ejercicio 76: UPDATE Nivel Aplicado

**Nombre:** Maria Jose Montepeque
**Fecha:** 2026-08-24

## Tema central

UPDATE

## Descripcion del problema

Un sistema de registro de campers administra inscripciones a rutas de
entrenamiento con cupo limitado. Cada vez que un camper se inscribe o
cancela, el cupo disponible de la ruta debe corregirse de inmediato
con `UPDATE`, y al final se necesita poder confirmar que ese cupo
guardado sigue siendo confiable: un caso de negocio con validacion
final, propio del nivel aplicado.

## Tablas y relaciones

- `campers`: catalogo de campers registrados.
- `rutas`: catalogo de rutas, cada una con `cupo_maximo` y
`cupo_disponible` (este ultimo se corrige con `UPDATE`).
- `inscripciones`: relaciona un camper con una ruta. `campers` 1—N
`inscripciones`; `rutas` 1—N `inscripciones`.

## Uso de UPDATE

En `dml/inserts.sql`, por cada inscripcion nueva:

1. `UPDATE` con expresion: `cupo_disponible = cupo_disponible - 1` en
la ruta correspondiente, repetido 6 veces (una por cada
inscripcion), demostrando que el mismo patron de `UPDATE` se aplica
de forma consistente cada vez que ocurre el evento de negocio.
2. Cancelacion: dos `UPDATE` en cadena, uno por tabla. Primero
`inscripciones.estado = 'cancelada'`, despues
`cupo_disponible = cupo_disponible + 1` en la ruta, para devolver
el cupo liberado.

La consulta 5 en `dql/consultas.sql` es el reporte final: compara el
`cupo_disponible` que quedo guardado contra un calculo independiente
(`cupo_maximo` menos el conteo de inscripciones `activa`), confirmando
que los `UPDATE` mantuvieron la columna consistente.

## 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`: `campers.email`, `rutas.nombre_ruta`.
- `CHECK`: `rutas.cupo_maximo > 0`, `rutas.cupo_disponible >= 0`,
`inscripciones.estado IN (...)`.
- `DEFAULT` en `rutas.cupo_maximo`, `inscripciones.estado` y
`fecha_inscripcion`.
- `PRAGMA foreign_keys = ON;` activado al inicio del script.

## Caso que falla / no recomendable (comentado en `dml/inserts.sql`)

Cumbre Extrema tiene cupo para solo 3 campers; una vez lleno
(`cupo_disponible = 0`), inscribir a un cuarto camper y restar 1 al
cupo dejaria la columna en -1, lo que viola el `CHECK` de
`cupo_disponible >= 0`. Se valido con Python (`sqlite3`), reproduciendo
el estado exacto de la secuencia en ese punto: 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: 6 campers, 3 rutas, 6 inscripciones (5 activas, 1
cancelada). Reporte final: cupo guardado y cupo calculado coinciden
en las 3 rutas.

## Como ejecutar

```bash
sqlite3 ejercicio-76.db < ddl/schema.sql
sqlite3 ejercicio-76.db < dml/inserts.sql
sqlite3 ejercicio-76.db < dql/consultas.sql
```

No suba archivos `.db`, `.sqlite` ni `.sqlite3`.
34 changes: 34 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-76/ddl/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
PRAGMA foreign_keys = ON;

-- Ejercicio 76: UPDATE Nivel Aplicado
-- Tema central: UPDATE
-- Contexto: registro de campers inscritos en rutas de entrenamiento,
-- con cupo limitado por ruta.

CREATE TABLE campers (
id_camper INTEGER PRIMARY KEY AUTOINCREMENT,
nombre TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);

-- rutas: cupo_disponible es el campo que se corrige con UPDATE cada
-- vez que alguien se inscribe o cancela. Nunca puede ser negativo
-- (eso significaria mas inscritos que cupo).
CREATE TABLE rutas (
id_ruta INTEGER PRIMARY KEY AUTOINCREMENT,
nombre_ruta TEXT NOT NULL UNIQUE,
cupo_maximo INTEGER NOT NULL DEFAULT 10 CHECK (cupo_maximo > 0),
cupo_disponible INTEGER NOT NULL CHECK (cupo_disponible >= 0)
);

CREATE TABLE inscripciones (
id_inscripcion INTEGER PRIMARY KEY AUTOINCREMENT,
id_camper INTEGER NOT NULL,
id_ruta INTEGER NOT NULL,
estado TEXT NOT NULL DEFAULT 'activa'
CHECK (estado IN ('activa', 'completada', 'cancelada')),
fecha_inscripcion TEXT NOT NULL DEFAULT (datetime('now')),

FOREIGN KEY (id_camper) REFERENCES campers (id_camper),
FOREIGN KEY (id_ruta) REFERENCES rutas (id_ruta)
);
60 changes: 60 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-76/dml/inserts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
PRAGMA foreign_keys = ON;

-- Ejercicio 76: UPDATE Nivel Aplicado
-- Caso de negocio: cada inscripcion activa debe restar 1 al cupo
-- disponible de su ruta, y cada cancelacion debe devolverlo. La
-- consulta 5 en dql/consultas.sql es la validacion final: confirma
-- que cupo_disponible siempre coincide con
-- cupo_maximo - inscripciones activas.

INSERT INTO campers (nombre, email) VALUES
('Karen Solis', 'karen.solis@campus.com'),
('Mario Ixtabalan', 'mario.ixtabalan@campus.com'),
('Ana Gomez', 'ana.gomez@campus.com'),
('Luis Marroquin', 'luis.marroquin@campus.com'),
('Rosa Chavez', 'rosa.chavez@campus.com'),
('Diego Paz', 'diego.paz@campus.com');

-- Rutas con cupo_disponible = cupo_maximo al inicio (nadie inscrito
-- todavia). Cumbre Extrema tiene cupo reducido a proposito para poder
-- demostrar el caso de ruta llena.
INSERT INTO rutas (nombre_ruta, cupo_maximo, cupo_disponible) VALUES
('Cumbre Extrema', 3, 3),
('Sendero del Canon', 5, 5),
('Ruta del Volcan', 10, 10);

-- Inscripciones en Cumbre Extrema: 3 campers llenan el cupo. Cada
-- INSERT va seguido de su UPDATE correspondiente, que resta 1 al
-- cupo_disponible de esa ruta con una expresion.
INSERT INTO inscripciones (id_camper, id_ruta) VALUES (1, 1);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 1;

INSERT INTO inscripciones (id_camper, id_ruta) VALUES (2, 1);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 1;

INSERT INTO inscripciones (id_camper, id_ruta) VALUES (3, 1);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 1;

-- Caso comentado que debe fallar (no ser recomendable), dejar
-- comentado: Cumbre Extrema ya esta llena (cupo_disponible = 0);
-- inscribir a un cuarto camper y restar 1 dejaria el cupo en -1, lo
-- que viola el CHECK de cupo_disponible >= 0.
-- INSERT INTO inscripciones (id_camper, id_ruta) VALUES (4, 1);
-- UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 1;

-- Inscripciones en Sendero del Canon.
INSERT INTO inscripciones (id_camper, id_ruta) VALUES (4, 2);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 2;

INSERT INTO inscripciones (id_camper, id_ruta) VALUES (5, 2);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 2;

-- Inscripcion en Ruta del Volcan.
INSERT INTO inscripciones (id_camper, id_ruta) VALUES (6, 3);
UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 3;

-- Mario Ixtabalan cancela su inscripcion en Cumbre Extrema: se libera
-- un cupo. Dos UPDATE, uno por tabla: primero el estado de la
-- inscripcion, despues el cupo de la ruta.
UPDATE inscripciones SET estado = 'cancelada' WHERE id_inscripcion = 2;
UPDATE rutas SET cupo_disponible = cupo_disponible + 1 WHERE id_ruta = 1;
47 changes: 47 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-76/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.headers on
.mode column

-- Ejercicio 76: UPDATE Nivel Aplicado
-- Consultas de validacion.

-- 1. Mostrar todos los datos principales (inscripciones con camper y
-- ruta).
SELECT i.id_inscripcion,
c.nombre AS camper,
r.nombre_ruta,
i.estado,
i.fecha_inscripcion
FROM inscripciones i
JOIN campers c ON c.id_camper = i.id_camper
JOIN rutas r ON r.id_ruta = i.id_ruta;

-- 2. Consulta con WHERE: solo las inscripciones activas.
SELECT id_inscripcion, id_camper, id_ruta
FROM inscripciones
WHERE estado = 'activa';

-- 3. Consulta con ORDER BY: inscripciones ordenadas por fecha.
SELECT id_inscripcion, fecha_inscripcion, estado
FROM inscripciones
ORDER BY fecha_inscripcion;

-- 4. Conteo o resumen: total de inscripciones por estado.
SELECT estado, COUNT(*) AS total
FROM inscripciones
GROUP BY estado;

-- 5. Caso de negocio con reporte final (nivel aplicado): se compara
-- el cupo_disponible que quedo despues de todos los UPDATE contra el
-- cupo que deberia haber, calculado desde cero solo con
-- cupo_maximo y el conteo de inscripciones activas. Si coinciden,
-- los UPDATE de cupo cumplieron su proposito.
SELECT r.nombre_ruta,
r.cupo_maximo,
r.cupo_disponible AS cupo_guardado,
r.cupo_maximo - (
SELECT COUNT(*)
FROM inscripciones i
WHERE i.id_ruta = r.id_ruta AND i.estado = 'activa'
) AS cupo_calculado
FROM rutas r
ORDER BY r.nombre_ruta;
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Evidencias - Ejercicio 76

## 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-76.db < ddl/schema.sql
sqlite3 ejercicio-76.db < dml/inserts.sql
sqlite3 ejercicio-76.db < dql/consultas.sql
```

## Resultados

Estado final de `rutas` tras `dml/inserts.sql` (6 inscripciones, 1
cancelada, con sus `UPDATE` de cupo correspondientes):

```text
id_ruta | nombre_ruta | cupo_maximo | cupo_disponible
1 | Cumbre Extrema | 3 | 1
2 | Sendero del Canon | 5 | 3
3 | Ruta del Volcan | 10 | 9
```

**Caso comentado verificado** (probado en el punto exacto de la
secuencia donde aparece comentado, justo despues de llenar Cumbre
Extrema con 3 inscripciones, cuando `cupo_disponible` ya esta en 0):

- `INSERT INTO inscripciones ...; UPDATE rutas SET cupo_disponible = cupo_disponible - 1 WHERE id_ruta = 1;` → `CHECK constraint failed: cupo_disponible >= 0`.

**4. Resumen: inscripciones por estado:**

```text
estado total
activa 5
cancelada 1
```

**5. Reporte final del caso de negocio (nivel aplicado): cupo
guardado en la tabla vs. cupo calculado desde cero con las
inscripciones activas:**

```text
nombre_ruta cupo_maximo | cupo_guardado | cupo_calculado
Cumbre Extrema 3 | 1 | 1
Ruta del Volcan 10 | 9 | 9
Sendero del Canon 5 | 3 | 3
```

Las tres rutas coinciden exactamente entre lo que quedo guardado por
los `UPDATE` y lo que se recalcula desde cero contando inscripciones
`activa`: los `UPDATE` de cupo cumplieron su proposito.

## Aprendizaje

Cada `UPDATE` de este ejercicio corrige un valor derivado
(`cupo_disponible`) a partir de un evento real (una inscripcion nueva
o una cancelacion), usando la propia columna como base
(`cupo_disponible ± 1`) en vez de recalcular todo desde cero cada vez.
El `CHECK (cupo_disponible >= 0)` actua como una red de seguridad: si
algun `UPDATE` intentara dejar mas inscritos que cupo disponible, la
base de datos lo rechaza en el momento, como se confirmo con el caso
comentado. La consulta 5 es la validacion final propia del nivel
aplicado: demuestra, con una subconsulta independiente, que la columna
mantenida a mano con `UPDATE` sigue siendo consistente con la realidad
de las inscripciones activas.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Solicitud SQL - Ejercicio 076: Cafeteria Campus

**Nombre:** Maria Jose Montepeque
**Fecha:** 2026-08-24

## Solicitud del cliente

Una cafeteria cerca del campus quiere controlar productos, ventas
rapidas y pagos de estudiantes. El cliente quiere diferenciar
catalogos, operaciones y resultados para no mezclar informacion
permanente con movimientos. 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

La peticion central es de diseno: separar claramente lo permanente
(productos, clientes) de lo operativo (ventas y su detalle) y de lo
resultante (pagos), en vez de mezclar todo en una sola tabla. 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

- `productos`: catalogo permanente de lo que vende la cafeteria.
- `clientes`: catalogo permanente de estudiantes.
- `ventas`: operacion, el encabezado de cada venta rapida.
- `detalle_ventas`: operacion, cada linea de producto dentro de una
venta. Aqui esta el `UNIQUE (id_venta, id_producto)` que impide
registrar el mismo producto dos veces en la misma venta.
- `pagos`: resultado de una venta. El `UNIQUE (id_venta)` garantiza un
solo pago oficial por venta.

## Como se relacionan

`clientes` 1:N `ventas`; `ventas` 1:N `detalle_ventas`; `productos`
1:N `detalle_ventas`; `ventas` 1:1 `pagos`. El diagrama esta en
[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg).

## Que datos de prueba use

5 productos, 4 clientes, 4 ventas (2 `cerrada` con pago desde el
inicio, 2 `abierta`) y 8 lineas de detalle, incluida una linea
cargada por error en una venta que todavia no tenia pago. Tambien un
`INSERT` comentado que reproduce el problema de duplicar un producto
en la misma venta 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 linea agregada por error (solo posible porque esa
venta seguia `abierta` y sin pago), un `UPDATE` de estado (la venta se
cobra y se cierra) y el registro de su pago oficial.

## Que consultas responden al cliente

En [dql/consultas.sql](dql/consultas.sql): que lineas de venta existen
(JOIN producto-venta), en que estado esta cada venta, que cliente tiene
mas actividad (mas gastado), las lineas ordenadas por subtotal, y un
reporte con `GROUP BY` + `HAVING` de los productos mas vendidos, para
decidir cuales reabastecer primero.

## 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-076.db < ddl/schema.sql
sqlite3 ejercicio-076.db < dml/inserts.sql
sqlite3 ejercicio-076.db < dml/operaciones.sql
sqlite3 ejercicio-076.db < dql/consultas.sql
```

No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`.
Loading
Loading