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
83 changes: 83 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-78/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Ejercicio 78: DELETE Nivel Intermedio

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

## Tema central

DELETE

## Descripcion del problema

Un sistema de registro de campers necesita limpiar inscripciones
duplicadas y canceladas sin arriesgar el resto de los datos, y tambien
poder descontinuar una ruta de entrenamiento sin perder el historial
de quien esta inscrito en ella.

## Tablas y relaciones

- `campers`: catalogo de campers registrados.
- `rutas`: catalogo de rutas, con una bandera `activa` para la baja
logica.
- `inscripciones`: relaciona un camper con una ruta. `campers` 1—N
`inscripciones`; `rutas` 1—N `inscripciones`.

## Uso de DELETE

En `dml/inserts.sql`:

1. `DELETE` de una sola fila: Mario Ixtabalan quedo inscrito dos veces
en Cumbre Extrema por error de digitacion; se elimina solo la copia
duplicada con `WHERE id_inscripcion = 3`.
2. `DELETE` multiple: todas las inscripciones `'cancelada'` de
cualquier ruta se eliminan de una sola vez con
`WHERE estado = 'cancelada'`, sin listar cada id a mano. Este es el
diferenciador de nivel intermedio frente al DELETE de una sola fila
del nivel basico.
3. Baja logica (sin `DELETE`): Cumbre Extrema se descontinua, pero
todavia tiene inscripciones activas. En vez de intentar borrarla,
se marca `activa = 0` con `UPDATE`.

La consulta 5 en `dql/consultas.sql` confirma que ya no queda ninguna
inscripcion cancelada y que el total final es el esperado.

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

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

`DELETE FROM rutas WHERE id_ruta = 1;` falla porque Cumbre Extrema
todavia tiene inscripciones activas que dependen de ella por
`FOREIGN KEY`. Se valido con Python (`sqlite3`): lanza
`FOREIGN KEY constraint failed`. Esto justifica usar baja logica
(`UPDATE activa = 0`) en vez de `DELETE` para rutas con inscripciones
vigentes.

## 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 inscripciones (todas activas), Cumbre Extrema dada
de baja logica.

## Como ejecutar

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

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

-- Ejercicio 78: DELETE Nivel Intermedio
-- Tema central: DELETE
-- Contexto: registro de campers inscritos en rutas de entrenamiento.

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

-- rutas: "activa" es la bandera de baja logica. Una ruta con
-- inscripciones todavia activas no se puede borrar de verdad.
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),
activa INTEGER NOT NULL DEFAULT 1 CHECK (activa IN (0, 1))
);

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)
);
53 changes: 53 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-78/dml/inserts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
PRAGMA foreign_keys = ON;

-- Ejercicio 78: DELETE Nivel Intermedio
-- Datos de prueba y DELETE de validacion.

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');

INSERT INTO rutas (nombre_ruta) VALUES
('Cumbre Extrema'),
('Sendero del Canon'),
('Ruta del Volcan');

INSERT INTO inscripciones (id_camper, id_ruta, estado) VALUES
(1, 1, 'activa'),
(2, 1, 'activa'),
(2, 1, 'activa'),
(3, 2, 'cancelada'),
(4, 2, 'cancelada'),
(5, 3, 'activa'),
(6, 1, 'cancelada');

-- 1. DELETE de una sola fila: Mario Ixtabalan quedo inscrito dos
-- veces en Cumbre Extrema por error de digitacion. Se elimina la
-- copia duplicada (id_inscripcion = 3), con WHERE por id especifico.
DELETE FROM inscripciones
WHERE id_inscripcion = 3;

-- 2. DELETE multiple: se limpian de una sola vez todas las
-- inscripciones canceladas de cualquier ruta, con un solo DELETE y un
-- WHERE por estado (no un id a la vez).
DELETE FROM inscripciones
WHERE estado = 'cancelada';

-- 3. Baja logica (no DELETE): Cumbre Extrema se descontinua, pero
-- Karen Solis y Mario Ixtabalan todavia tienen inscripciones activas
-- ahi. En vez de intentar borrar la ruta, se marca como inactiva.
UPDATE rutas
SET activa = 0
WHERE id_ruta = 1;

-- Caso comentado que debe fallar (no ser recomendable), dejar
-- comentado: intentar el DELETE fisico de una ruta que todavia tiene
-- inscripciones activas asociadas. SQLite, con
-- PRAGMA foreign_keys = ON, no lo permite. Esto es justo lo que
-- justifica usar baja logica (UPDATE activa = 0) en vez de DELETE
-- para rutas.
-- DELETE FROM rutas WHERE id_ruta = 1;
43 changes: 43 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-78/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.headers on
.mode column

-- Ejercicio 78: DELETE Nivel Intermedio
-- 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
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 ruta.
SELECT id_ruta, COUNT(*) AS total_inscripciones
FROM inscripciones
GROUP BY id_ruta;

-- 5. Validacion especifica de DELETE: ya no queda ninguna
-- inscripcion 'cancelada' (se borraron todas de una vez), ni la copia
-- duplicada de Mario Ixtabalan. Solo quedan las 3 inscripciones
-- 'activa' originales.
SELECT COUNT(*) AS canceladas_restantes
FROM inscripciones
WHERE estado = 'cancelada';
-- Debe devolver 0: el DELETE multiple elimino todas las canceladas.

SELECT COUNT(*) AS total_restante
FROM inscripciones;
-- Debe devolver 3: empezaron 7, se borro 1 duplicado y 3 canceladas.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Evidencias - Ejercicio 78

## Tema

DELETE

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

## Resultados

Estado final tras `dml/inserts.sql` (7 inscripciones iniciales, 1
duplicado y 3 canceladas eliminadas):

```text
id_inscripcion | id_camper | id_ruta | estado
1 | 1 | 1 | activa
2 | 2 | 1 | activa
6 | 5 | 3 | activa
```

```text
rutas:
id_ruta | nombre_ruta | cupo_maximo | activa
1 | Cumbre Extrema | 10 | 0
2 | Sendero del Canon | 10 | 1
3 | Ruta del Volcan | 10 | 1
```

**Caso comentado verificado:**

- `DELETE FROM rutas WHERE id_ruta = 1;` → `FOREIGN KEY constraint failed` (Cumbre Extrema todavia tiene 2 inscripciones activas).

**5. Validacion especifica de DELETE:**

```text
5a. canceladas_restantes: 0 -- el DELETE multiple elimino las 3 de un solo golpe.
5b. total_restante: 3 -- empezaron 7, se borro 1 duplicado y 3 canceladas.
```

## Aprendizaje

`DELETE` con `WHERE` por id especifico elimina exactamente una fila
(la copia duplicada de Mario Ixtabalan), mientras que `DELETE` con
`WHERE estado = 'cancelada'` elimina todas las filas que cumplen esa
condicion en una sola sentencia, sin importar cuantas sean ni en que
ruta esten: eso es lo que diferencia el nivel intermedio del basico.
Igual que con los productos del ejercicio anterior, `DELETE` no
siempre es la herramienta correcta para una tabla que todavia tiene
dependientes: cuando una ruta sigue teniendo inscripciones activas,
SQLite rechaza el `DELETE` fisico por `FOREIGN KEY`, y ahi es donde
conviene la baja logica (`UPDATE activa = 0`) para dar de baja la
ruta sin perder el historial de quien esta inscrito en ella.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Solicitud SQL - Ejercicio 078: Torneo Esports

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

## Solicitud del cliente

Una organizacion de esports registra equipos, jugadores, partidas y
puntos. El cliente quiere consultar rankings, totales y casos
pendientes desde la base de datos. 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

"Casos pendientes" se traduce en partidas `programada`: el cliente
necesita poder distinguirlas de las que ya se jugaron o se
cancelaron. Y "consultar rankings... desde la base de datos" sugiere
que el ranking no se calcula siempre al vuelo, sino que vive en su
propia tabla y se corrige con `UPDATE`. 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

- `equipos`: catalogo de equipos del torneo.
- `jugadores`: catalogo de jugadores, cada uno de un equipo.
- `partidas`: tabla transaccional, con `estado` para distinguir casos
pendientes de partidas ya resueltas.
- `estadisticas`: detalle de cada partida. Aqui esta el
`UNIQUE (id_partida, id_jugador)` que impide cargar dos veces al
mismo jugador en la misma partida.
- `ranking`: una fila por equipo (`UNIQUE (id_equipo)`), que se
corrige con `UPDATE` a partir de las estadisticas reales.

## Como se relacionan

`equipos` 1:N `jugadores`; `equipos` 1:N `partidas` (como local y como
visitante); `partidas` 1:N `estadisticas`; `jugadores` 1:N
`estadisticas`; `equipos` 1:1 `ranking`. El diagrama esta en
[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg).

## Que datos de prueba use

3 equipos, 9 jugadores, 4 partidas (3 marcadas `jugada` en algun
momento, 1 `programada` como caso pendiente), 14 filas de
estadisticas (incluye 2 cargadas por error para una partida que
despues se descubrio que habia que cancelar) y el ranking inicial en
0. Tambien un `INSERT` comentado que reproduce el problema de cargar
dos veces al mismo jugador en la misma partida 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 partida que fallo por el servidor pasa a `cancelada`), un
`DELETE` controlado (multiple) que limpia las estadisticas huerfanas
de esa partida, y un `UPDATE` que recalcula el ranking de los 3
equipos a partir de las partidas realmente jugadas.

## Que consultas responden al cliente

En [dql/consultas.sql](dql/consultas.sql): que estadisticas existen
(JOIN jugador-partida), que partidas estan pendientes, jugadas o
canceladas, que jugador tiene mas actividad, el ranking final
ordenado, y un reporte con `GROUP BY` + `HAVING` de que equipos
superan un puntaje minimo, para decidir quienes avanzan a la
siguiente fase.

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

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