diff --git a/resoluciones/maria-montepeque/ejercicio-74/README.md b/resoluciones/maria-montepeque/ejercicio-74/README.md new file mode 100644 index 00000000..d7db8297 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-74/README.md @@ -0,0 +1,86 @@ +# Ejercicio 74: UPDATE Nivel Basico + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +UPDATE + +## Descripcion del problema + +Un torneo de videojuegos registra sus partidas todas como +`'programada'` con puntaje 0-0 apenas se arma el calendario. Los +resultados reales, las cancelaciones y las correcciones se aplican +despues, una por una, con `UPDATE`. + +## Tablas y relaciones + +- `equipos`: catalogo de equipos participantes. +- `jugadores`: catalogo de jugadores, cada uno ligado a un equipo. +- `partidas`: tabla principal de este ejercicio. Relaciona un equipo + local con un equipo visitante en una fecha, con puntaje y estado. + `equipos` 1—N `jugadores`; `equipos` 1—N `partidas` (dos veces: + como local y como visitante). + +## Uso de UPDATE + +En `dml/inserts.sql`, despues de insertar las 4 partidas (todas +`'programada'`, 0-0): + +1. `UPDATE` de una sola fila (dos veces): llegan los resultados reales + de las partidas 1 y 2, cada uno con su propio `WHERE id_partida = ...`. +2. `UPDATE` multiple: las partidas 3 y 4 se cancelan juntas por un + problema con el estadio, con un solo `UPDATE` y + `WHERE id_partida IN (3, 4)`. +3. `UPDATE` con expresion: la revision en video anula un gol que ya se + habia contado de mas en la partida 1. En vez de escribir el + resultado final a mano, se usa + `SET puntaje_local = puntaje_local - 1`, que calcula el nuevo valor + a partir del que ya tenia la columna. + +La consulta 5 en `dql/consultas.sql` confirma el estado final de las +partidas 1, 3 y 4 despues de todos estos `UPDATE`. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `jugadores.id_equipo`, `partidas.id_equipo_local`, + `partidas.id_equipo_visitante`. +- `NOT NULL` en todas las columnas obligatorias. +- `UNIQUE`: `equipos.nombre_equipo`. +- `CHECK`: `partidas.estado IN (...)`, `puntaje_local >= 0`, + `puntaje_visitante >= 0`. +- `DEFAULT` en `partidas.puntaje_local`, `puntaje_visitante` y + `estado`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dml/inserts.sql`) + +`UPDATE partidas SET estado = 'suspendida' WHERE id_partida = 1;` +falla porque `'suspendida'` no esta en la lista de valores permitidos +por el `CHECK` de `estado`. Se valido con Python (`sqlite3`): lanza +`CHECK constraint failed`. Ademas se dejo una nota (sin ejecutar) +sobre por que cada `UPDATE` de este archivo usa un `WHERE` especifico: +un `UPDATE` sin `WHERE` habria modificado las 4 partidas a la vez. + +## 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: 4 equipos, 4 jugadores, 4 partidas (2 `jugada`, 2 + `cancelada`). Partida 1 terminada 2-1 despues de la correccion por + video. + +## Como ejecutar + +```bash +sqlite3 ejercicio-74.db < ddl/schema.sql +sqlite3 ejercicio-74.db < dml/inserts.sql +sqlite3 ejercicio-74.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-74/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-74/ddl/schema.sql new file mode 100644 index 00000000..dd071584 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-74/ddl/schema.sql @@ -0,0 +1,36 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 74: UPDATE Nivel Basico +-- Tema central: UPDATE +-- Contexto: torneo de videojuegos, partidas y puntajes por equipo. + +CREATE TABLE equipos ( + id_equipo INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_equipo TEXT NOT NULL UNIQUE, + region TEXT NOT NULL +); + +CREATE TABLE jugadores ( + id_jugador INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + id_equipo INTEGER NOT NULL, + + FOREIGN KEY (id_equipo) REFERENCES equipos (id_equipo) +); + +-- partidas: tabla principal de este ejercicio. Todas las partidas +-- nacen 'programada' con puntaje en 0; los UPDATE de +-- dml/inserts.sql son los que las mueven a su estado y puntaje real. +CREATE TABLE partidas ( + id_partida INTEGER PRIMARY KEY AUTOINCREMENT, + id_equipo_local INTEGER NOT NULL, + id_equipo_visitante INTEGER NOT NULL, + fecha_partida TEXT NOT NULL, + puntaje_local INTEGER NOT NULL DEFAULT 0 CHECK (puntaje_local >= 0), + puntaje_visitante INTEGER NOT NULL DEFAULT 0 CHECK (puntaje_visitante >= 0), + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'jugada', 'cancelada')), + + FOREIGN KEY (id_equipo_local) REFERENCES equipos (id_equipo), + FOREIGN KEY (id_equipo_visitante) REFERENCES equipos (id_equipo) +); diff --git a/resoluciones/maria-montepeque/ejercicio-74/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-74/dml/inserts.sql new file mode 100644 index 00000000..634b6e91 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-74/dml/inserts.sql @@ -0,0 +1,60 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 74: UPDATE Nivel Basico +-- Datos de prueba y UPDATE de validacion. + +INSERT INTO equipos (nombre_equipo, region) VALUES + ('Dragones del Norte', 'Norte'), + ('Lobos del Sur', 'Sur'), + ('Halcones del Centro', 'Centro'), + ('Tigres del Oeste', 'Oeste'); + +INSERT INTO jugadores (nombre, id_equipo) VALUES + ('Kevin Us', 1), + ('Oscar Tzul', 2), + ('Melissa Ordonez', 3), + ('Sergio Batz', 4); + +-- Las 4 partidas nacen todas 'programada' con puntaje 0-0 (el DEFAULT +-- de la tabla). Los UPDATE de abajo son los que las mueven a su +-- estado y resultado real, uno por uno. +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida) VALUES + (1, 2, '2026-08-01'), + (3, 4, '2026-08-01'), + (2, 1, '2026-08-08'), + (4, 3, '2026-08-08'); + +-- 1. UPDATE de una sola fila: llega el resultado de la partida 1 con +-- WHERE por id_partida. +UPDATE partidas +SET puntaje_local = 3, puntaje_visitante = 1, estado = 'jugada' +WHERE id_partida = 1; + +-- 2. UPDATE de una sola fila: llega el resultado de la partida 2. +UPDATE partidas +SET puntaje_local = 2, puntaje_visitante = 2, estado = 'jugada' +WHERE id_partida = 2; + +-- 3. UPDATE multiple: las partidas 3 y 4 se cancelan juntas por un +-- problema con el estadio, con un solo UPDATE y un WHERE con IN. +UPDATE partidas +SET estado = 'cancelada' +WHERE id_partida IN (3, 4); + +-- 4. UPDATE con expresion (no un valor fijo): la revision en video +-- anula un gol que ya se habia contado de mas para el equipo local de +-- la partida 1. En vez de escribir el numero final a mano, se resta 1 +-- al valor que ya tenia la columna. +UPDATE partidas +SET puntaje_local = puntaje_local - 1 +WHERE id_partida = 1 AND puntaje_local > 0; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: un estado que no esta en la lista permitida viola el +-- CHECK. +-- UPDATE partidas SET estado = 'suspendida' WHERE id_partida = 1; + +-- Nota sobre buenas practicas (no se ejecuta): un UPDATE sin WHERE +-- modificaria las 4 partidas a la vez, incluidas las que no debian +-- cambiar. Por eso cada UPDATE de este archivo usa una condicion +-- especifica (WHERE id_partida = ... o WHERE id_partida IN (...)). diff --git a/resoluciones/maria-montepeque/ejercicio-74/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-74/dql/consultas.sql new file mode 100644 index 00000000..efbe2793 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-74/dql/consultas.sql @@ -0,0 +1,41 @@ +.headers on +.mode column + +-- Ejercicio 74: UPDATE Nivel Basico +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales (partidas con nombre de +-- equipo local y visitante). +SELECT p.id_partida, + eloc.nombre_equipo AS equipo_local, + evis.nombre_equipo AS equipo_visitante, + p.fecha_partida, + p.puntaje_local, + p.puntaje_visitante, + p.estado +FROM partidas p +JOIN equipos eloc ON eloc.id_equipo = p.id_equipo_local +JOIN equipos evis ON evis.id_equipo = p.id_equipo_visitante; + +-- 2. Consulta con WHERE: partidas ya jugadas. +SELECT id_partida, fecha_partida, puntaje_local, puntaje_visitante +FROM partidas +WHERE estado = 'jugada'; + +-- 3. Consulta con ORDER BY: partidas ordenadas por fecha. +SELECT id_partida, fecha_partida, estado +FROM partidas +ORDER BY fecha_partida; + +-- 4. Conteo o resumen: total de partidas por estado. +SELECT estado, COUNT(*) AS total +FROM partidas +GROUP BY estado; + +-- 5. Validacion especifica de UPDATE: la partida 1 quedo con +-- puntaje_local = 2 (broto 3, la revision en video resto 1 con un +-- UPDATE por expresion) y estado = 'jugada'; las partidas 3 y 4 +-- quedaron 'cancelada' por el UPDATE multiple con WHERE IN. +SELECT id_partida, puntaje_local, puntaje_visitante, estado +FROM partidas +WHERE id_partida IN (1, 3, 4); diff --git a/resoluciones/maria-montepeque/ejercicio-74/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-74/evidencias/resultados.md new file mode 100644 index 00000000..c4eaad78 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-74/evidencias/resultados.md @@ -0,0 +1,69 @@ +# Evidencias - Ejercicio 74 + +## 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-74.db < ddl/schema.sql +sqlite3 ejercicio-74.db < dml/inserts.sql +sqlite3 ejercicio-74.db < dql/consultas.sql +``` + +## Resultados + +Estado final tras `dml/inserts.sql` (que incluye los 4 `UPDATE` de +validacion sobre las 4 partidas insertadas todas en `'programada'` +con 0-0): + +```text +id_partida | equipo_local | equipo_visitante | fecha_partida | puntaje_local | puntaje_visitante | estado +1 | 1 | 2 | 2026-08-01 | 2 | 1 | jugada +2 | 3 | 4 | 2026-08-01 | 2 | 2 | jugada +3 | 2 | 1 | 2026-08-08 | 0 | 0 | cancelada +4 | 4 | 3 | 2026-08-08 | 0 | 0 | cancelada +``` + +**Caso comentado verificado:** + +- `UPDATE partidas SET estado = 'suspendida' WHERE id_partida = 1;` → `CHECK constraint failed: estado IN ('programada', 'jugada', 'cancelada')`. + +**4. Resumen: partidas por estado:** + +```text +estado total +cancelada 2 +jugada 2 +``` + +**5. Validacion especifica de UPDATE:** + +```text +id_partida | puntaje_local | puntaje_visitante | estado +1 | 2 | 1 | jugada +3 | 0 | 0 | cancelada +4 | 0 | 0 | cancelada +``` + +La partida 1 llego a 3-1 con el primer `UPDATE`, y quedo en 2-1 +despues del `UPDATE` por expresion (`puntaje_local = puntaje_local - 1`) +que anulo un gol tras revision en video. Las partidas 3 y 4 quedaron +`'cancelada'` con un solo `UPDATE` multiple (`WHERE id_partida IN (3, 4)`). + +## Aprendizaje + +`UPDATE` con `WHERE` modifica exactamente las filas que cumplen la +condicion, ni una mas: el `UPDATE` de la partida 1 nunca toco las +partidas 2, 3 ni 4. Un mismo `UPDATE` puede afectar varias filas a la +vez si el `WHERE` las agrupa (`IN (3, 4)`), sin tener que repetir la +sentencia. Ademas, `SET columna = columna - 1` demuestra que `UPDATE` +puede calcular el nuevo valor a partir del valor actual, en vez de +tener que escribir el resultado final a mano; eso es justo lo que +permitio corregir el marcador de la partida 1 sin necesitar saber de +antemano cual iba a quedar el numero exacto. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/README.md new file mode 100644 index 00000000..87461517 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/README.md @@ -0,0 +1,79 @@ +# Solicitud SQL - Ejercicio 074: Liga Videojuego Futbol + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Una liga de videojuegos de futbol registra usuarios, clubes, jornadas +y goles. El cliente necesita un reporte rapido para tomar decisiones +al final de cada semana. 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 + +"Al final de cada semana" se traduce en "al final de cada jornada": +el modelo necesita poder responder, para una jornada especifica, que +club domino en goles. 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 + +- `usuarios`: catalogo de jugadores humanos de la liga. +- `clubes`: catalogo de clubes de futbol del videojuego. +- `jornadas`: catalogo de semanas de competencia. +- `partidos`: tabla transaccional, cada uno dentro de una jornada, + entre dos usuarios que juegan con dos clubes. +- `goles`: detalle de cada partido. El marcador no se guarda como + numero fijo, se calcula sumando estas filas, lo que hace que el + reporte semanal que pidio el cliente siempre sea confiable. + +## Como se relacionan + +`jornadas` 1:N `partidos`; `usuarios` 1:N `partidos` (como local y +como visitante); `clubes` 1:N `partidos` (como local y como +visitante); `partidos` 1:N `goles`; `clubes` 1:N `goles`. El diagrama +esta en [diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +4 usuarios, 4 clubes, 2 jornadas, 4 partidos (3 marcados `jugado` en +algun momento, 1 `programado`) y 9 goles, incluido uno cargado por +error para un partido que despues se descubrio que habia que cancelar. +Tambien tres `INSERT` comentados que deben fallar (uno por cada +restriccion). Detalle en [dml/inserts.sql](dml/inserts.sql). + +## Que operaciones de mantenimiento incluyo + +En [dml/operaciones.sql](dml/operaciones.sql): un `UPDATE` de estado +(el partido que se desconecto a la mitad pasa a `cancelado`) y un +`DELETE` controlado que limpia el gol huerfano de ese partido, sin +tocar ningun gol de un partido ya `jugado`. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): que goles existen (JOIN +club-partido-jornada), en que estado esta cada partido, que club +anoto mas goles, los goles ordenados por minuto, y el reporte rapido +semanal con `GROUP BY` + `HAVING` de goles por club en una jornada +especifica. + +## Evidencias + +Resultados de ejecutar todo en orden, incluyendo la verificacion de +los tres casos de error y de las operaciones de mantenimiento, en +[evidencias/resultados.md](evidencias/resultados.md). + +## Como ejecutar + +```bash +sqlite3 ejercicio-074.db < ddl/schema.sql +sqlite3 ejercicio-074.db < dml/inserts.sql +sqlite3 ejercicio-074.db < dml/operaciones.sql +sqlite3 ejercicio-074.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/analisis/requerimiento.md new file mode 100644 index 00000000..b11c88df --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/analisis/requerimiento.md @@ -0,0 +1,73 @@ +# Analisis del requerimiento - Ejercicio 074 + +## Solicitud entendida + +Una liga de videojuegos de futbol registra usuarios, clubes, jornadas +y goles. El cliente necesita un reporte rapido para tomar decisiones +al final de cada semana (cada jornada). Se necesita una base de datos +que permita consultar datos, corregir estados, registrar movimientos +y sacar ese reporte semanal de forma confiable. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| usuarios | Catalogo: cada jugador humano de la liga | nombre_usuario (unico), email (unico) | +| clubes | Catalogo: cada club de futbol disponible en el videojuego | nombre_club (unico), liga | +| jornadas | Catalogo: cada semana de competencia | numero_jornada (unico), fecha_inicio, fecha_fin | +| partidos | Tabla transaccional: un usuario juega con un club contra otro usuario con otro club, dentro de una jornada | fecha_partido, estado | +| goles | Detalle de cada partido: cada gol anotado, de que club y en que minuto | minuto | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| jornadas -> partidos | 1:N | Una jornada agrupa varios partidos. | +| usuarios -> partidos | 1:N (dos veces) | Un usuario juega muchos partidos, como local o como visitante. | +| clubes -> partidos | 1:N (dos veces) | Un club se usa en muchos partidos, como local o como visitante (el mismo usuario puede elegir clubes distintos en cada partido). | +| partidos -> goles | 1:N | Un partido tiene una fila de goles por cada anotacion. | +| clubes -> goles | 1:N | Un club anota goles en muchos partidos distintos. | + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): todo partido debe apuntar a una + jornada, dos usuarios y dos clubes reales; todo gol debe apuntar a + un partido y a un club reales (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `usuarios.nombre_usuario`, + `usuarios.email`, `clubes.nombre_club` y `jornadas.numero_jornada` + no se repiten (`UNIQUE`). +- Regla 3 (valores fuera de rango): `goles.minuto` debe estar entre 1 + y 120 (`CHECK`, incluye tiempo extra); + `jornadas.fecha_fin >= jornadas.fecha_inicio` (`CHECK`). +- Regla 4: un partido nace `'programado'` y solo puede avanzar a + `'jugado'` o `'cancelado'` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: el marcador de un partido no se guarda como numero fijo, se + calcula sumando los goles de cada club en ese partido (ver el + reporte semanal en `dql/consultas.sql`). Los goles solo tienen + sentido para un partido que ya se jugo: si un partido se cancela y + ya tenia goles cargados por error, esas filas se eliminan; nunca se + borra un gol de un partido `'jugado'`, porque ya es un resultado + oficial de la liga. + +## Supuestos + +- El cliente no detallo si `goles.id_club` debe validarse contra los + dos clubes de ese partido especifico; SQLite no permite un `CHECK` + que compare columnas de dos tablas distintas, asi que esa regla + queda documentada aqui como responsabilidad del proceso de carga + (siempre se registra el club que realmente jugo ese partido), no + como una restriccion de base de datos. +- Se asume que un mismo usuario puede jugar con clubes distintos en + partidos distintos (no hay una asignacion fija usuario-club), porque + asi funcionan la mayoria de videojuegos de futbol. +- No se detallo un limite de goles por partido; se asume que el + minuto (1 a 120) es la unica validacion de rango necesaria. + +## Preguntas que responde la base de datos + +1. Que goles existen, con que club, que partido y que jornada. +2. Que partidos estan programados, jugados o cancelados. +3. Que club anoto mas goles en total (ranking de actividad). +4. Como se ordenan los goles por minuto. +5. Que club domino la jornada actual en goles anotados, para el + reporte rapido semanal que pidio el cliente. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/ddl/schema.sql new file mode 100644 index 00000000..f7694e97 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/ddl/schema.sql @@ -0,0 +1,58 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 074: Liga Videojuego Futbol +-- Modelo: jornadas -> partidos (1:N); usuarios + clubes -> partidos +-- (1:N cada uno, como local y como visitante); partidos + clubes -> +-- goles (1:N cada una). + +CREATE TABLE usuarios ( + id_usuario INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_usuario TEXT NOT NULL UNIQUE, + email TEXT NOT NULL UNIQUE +); + +CREATE TABLE clubes ( + id_club INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_club TEXT NOT NULL UNIQUE, + liga TEXT NOT NULL +); + +CREATE TABLE jornadas ( + id_jornada INTEGER PRIMARY KEY AUTOINCREMENT, + numero_jornada INTEGER NOT NULL UNIQUE CHECK (numero_jornada >= 1), + fecha_inicio TEXT NOT NULL, + fecha_fin TEXT NOT NULL, + + CHECK (fecha_fin >= fecha_inicio) +); + +CREATE TABLE partidos ( + id_partido INTEGER PRIMARY KEY AUTOINCREMENT, + id_jornada INTEGER NOT NULL, + id_usuario_local INTEGER NOT NULL, + id_club_local INTEGER NOT NULL, + id_usuario_visitante INTEGER NOT NULL, + id_club_visitante INTEGER NOT NULL, + fecha_partido TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'programado' + CHECK (estado IN ('programado', 'jugado', 'cancelado')), + + FOREIGN KEY (id_jornada) REFERENCES jornadas (id_jornada), + FOREIGN KEY (id_usuario_local) REFERENCES usuarios (id_usuario), + FOREIGN KEY (id_club_local) REFERENCES clubes (id_club), + FOREIGN KEY (id_usuario_visitante) REFERENCES usuarios (id_usuario), + FOREIGN KEY (id_club_visitante) REFERENCES clubes (id_club) +); + +-- goles: el marcador de un partido no se guarda como numero fijo, se +-- calcula sumando estas filas (ver el reporte semanal en +-- dql/consultas.sql). +CREATE TABLE goles ( + id_gol INTEGER PRIMARY KEY AUTOINCREMENT, + id_partido INTEGER NOT NULL, + id_club INTEGER NOT NULL, + minuto INTEGER NOT NULL CHECK (minuto BETWEEN 1 AND 120), + + FOREIGN KEY (id_partido) REFERENCES partidos (id_partido), + FOREIGN KEY (id_club) REFERENCES clubes (id_club) +); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/diagramas/diagrama-er.svg new file mode 100644 index 00000000..086d2fcf --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/diagramas/diagrama-er.svg @@ -0,0 +1,60 @@ + + + + + usuarios + id_usuario PK + nombre_usuario UNIQUE + email UNIQUE + + + clubes + id_club PK + nombre_club UNIQUE, liga + + + jornadas + id_jornada PK + numero_jornada UNIQUE + fecha_inicio, fecha_fin + + + partidos + id_partido PK + id_jornada FK + id_usuario_local/visitante FK + id_club_local/visitante FK + estado CHECK + + + goles + id_gol PK + id_partido FK, id_club FK + minuto CHECK 1-120 + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/inserts.sql new file mode 100644 index 00000000..cc87dd7e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/inserts.sql @@ -0,0 +1,77 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 074: Liga Videojuego Futbol +-- Datos base: 4 usuarios, 4 clubes, 2 jornadas, 4 partidos (2 jugados +-- de la jornada 1, 1 jugado-por-error de la jornada 1 que se corrige +-- despues, y 1 programado de la jornada 2), y los goles de los +-- partidos jugados. + +INSERT INTO usuarios (nombre_usuario, email) VALUES + ('jugador_karla', 'karla@correo.com'), + ('jugador_bryan', 'bryan@correo.com'), + ('jugador_fernanda', 'fernanda@correo.com'), + ('jugador_jorge', 'jorge@correo.com'); + +INSERT INTO clubes (nombre_club, liga) VALUES + ('Real Pixel', 'Liga Digital'), + ('Atletico Bit', 'Liga Digital'), + ('Union Byte', 'Liga Digital'), + ('Deportivo Codigo', 'Liga Digital'); + +INSERT INTO jornadas (numero_jornada, fecha_inicio, fecha_fin) VALUES + (1, '2026-08-01', '2026-08-07'), + (2, '2026-08-08', '2026-08-14'); + +-- Partido 1: Karla (Real Pixel, local) vs Bryan (Atletico Bit, +-- visitante), jornada 1, jugado. +INSERT INTO partidos (id_jornada, id_usuario_local, id_club_local, id_usuario_visitante, id_club_visitante, fecha_partido, estado) VALUES + (1, 1, 1, 2, 2, '2026-08-02', 'jugado'); + +-- Partido 2: Fernanda (Union Byte, local) vs Jorge (Deportivo +-- Codigo, visitante), jornada 1, jugado. +INSERT INTO partidos (id_jornada, id_usuario_local, id_club_local, id_usuario_visitante, id_club_visitante, fecha_partido, estado) VALUES + (1, 3, 3, 4, 4, '2026-08-03', 'jugado'); + +-- Partido 3: Karla (Real Pixel, local) vs Fernanda (Union Byte, +-- visitante), jornada 1. Se cargo como 'jugado' con un gol, pero el +-- servidor del videojuego se desconecto a la mitad y el resultado se +-- anulo despues. Se corrige el estado con UPDATE en +-- dml/operaciones.sql. +INSERT INTO partidos (id_jornada, id_usuario_local, id_club_local, id_usuario_visitante, id_club_visitante, fecha_partido, estado) VALUES + (1, 1, 1, 3, 3, '2026-08-04', 'jugado'); + +-- Partido 4: jornada 2, todavia no se juega. +INSERT INTO partidos (id_jornada, id_usuario_local, id_club_local, id_usuario_visitante, id_club_visitante, fecha_partido, estado) VALUES + (2, 2, 2, 4, 4, '2026-08-09', 'programado'); + +-- Goles del partido 1: Real Pixel 3, Atletico Bit 1. +INSERT INTO goles (id_partido, id_club, minuto) VALUES + (1, 1, 12), + (1, 1, 45), + (1, 1, 78), + (1, 2, 60); + +-- Goles del partido 2: Union Byte 2, Deportivo Codigo 2. +INSERT INTO goles (id_partido, id_club, minuto) VALUES + (2, 3, 10), + (2, 3, 30), + (2, 4, 20), + (2, 4, 55); + +-- Gol cargado por error para el partido 3, antes de saber que el +-- servidor se habia caido. Quedara huerfano cuando el partido se +-- marque 'cancelado' en dml/operaciones.sql, y se elimina ahi mismo. +INSERT INTO goles (id_partido, id_club, minuto) VALUES + (3, 1, 5); + +-- Casos comentados que deben fallar (no ser recomendables), dejar +-- comentados: + +-- 1) Registro repetido: numero_jornada ya existe, viola el UNIQUE. +-- INSERT INTO jornadas (numero_jornada, fecha_inicio, fecha_fin) VALUES (1, '2026-08-15', '2026-08-21'); + +-- 2) Relacion invalida: id_club = 99 no existe, viola el FOREIGN KEY. +-- INSERT INTO goles (id_partido, id_club, minuto) VALUES (1, 99, 30); + +-- 3) Valor fuera de rango: minuto mayor a 120, viola el CHECK. +-- INSERT INTO goles (id_partido, id_club, minuto) VALUES (2, 3, 130); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/operaciones.sql new file mode 100644 index 00000000..211e6936 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dml/operaciones.sql @@ -0,0 +1,25 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 074: Liga Videojuego Futbol +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que el partido 3 se desconecto a la +-- mitad y su resultado se anula. +UPDATE partidos +SET estado = 'cancelado' +WHERE id_partido = 3 AND estado = 'jugado'; + +-- 1 DELETE controlado: el gol del partido 3 quedo huerfano apenas se +-- marco 'cancelado' (ya no representa un resultado oficial). Solo se +-- borran goles de partidos 'cancelado'; un partido 'jugado' nunca +-- pierde sus goles por este DELETE. +DELETE FROM goles +WHERE id_partido IN ( + SELECT id_partido FROM partidos WHERE estado = 'cancelado' +); + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar goles de un partido que ya quedo 'jugado' (resultado oficial +-- de la liga). El DELETE de arriba solo alcanza partidos 'cancelado' +-- por diseno. +-- DELETE FROM goles WHERE id_partido = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dql/consultas.sql new file mode 100644 index 00000000..e0a4700e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/dql/consultas.sql @@ -0,0 +1,48 @@ +.headers on +.mode column + +-- Ejercicio 074: Liga Videojuego Futbol +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: todos los goles con su club, +-- su partido y su jornada. +SELECT g.id_gol, + c.nombre_club, + j.numero_jornada, + p.fecha_partido, + g.minuto +FROM goles g +JOIN clubes c ON c.id_club = g.id_club +JOIN partidos p ON p.id_partido = g.id_partido +JOIN jornadas j ON j.id_jornada = p.id_jornada; + +-- 2. Que partidos estan programados, jugados o cancelados. +SELECT id_partido, fecha_partido, estado +FROM partidos +ORDER BY estado; + +-- 3. Que club anoto mas goles en total (ranking de actividad). +SELECT c.nombre_club, COUNT(*) AS goles_totales +FROM clubes c +JOIN goles g ON g.id_club = c.id_club +GROUP BY c.id_club, c.nombre_club +ORDER BY goles_totales DESC, c.nombre_club; + +-- 4. Goles ordenados por minuto. +SELECT g.id_gol, c.nombre_club, g.minuto +FROM goles g +JOIN clubes c ON c.id_club = g.id_club +ORDER BY g.minuto; + +-- 5. Reporte rapido semanal (lo que pidio el cliente): goles por club +-- dentro de la jornada 1, para decidir quien domino la semana +-- (GROUP BY + HAVING). +SELECT c.nombre_club, + SUM(1) AS goles_jornada +FROM goles g +JOIN partidos p ON p.id_partido = g.id_partido +JOIN clubes c ON c.id_club = g.id_club +WHERE p.id_jornada = 1 +GROUP BY c.id_club, c.nombre_club +HAVING SUM(1) >= 2 +ORDER BY goles_jornada DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/evidencias/resultados.md new file mode 100644 index 00000000..cb28bb21 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-074/evidencias/resultados.md @@ -0,0 +1,77 @@ +# Evidencias - Solicitudes SQL - Ejercicio 074 (Liga Videojuego Futbol) + +## 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-074.db < ddl/schema.sql +sqlite3 ejercicio-074.db < dml/inserts.sql +sqlite3 ejercicio-074.db < dml/operaciones.sql +sqlite3 ejercicio-074.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 4 usuarios, 4 clubes, 2 jornadas, +4 partidos (3 `jugado`, 1 `programado`) y 9 goles (incluye el cargado +por error para el partido que se debia cancelar). + +**Casos comentados verificados:** + +- `INSERT INTO jornadas (numero_jornada, ...) VALUES (1, ...);` → `UNIQUE constraint failed: jornadas.numero_jornada`. +- `INSERT INTO goles (id_partido, id_club, ...) VALUES (1, 99, ...);` → `FOREIGN KEY constraint failed`. +- `INSERT INTO goles (..., minuto) VALUES (..., 130);` → `CHECK constraint failed: minuto BETWEEN 1 AND 120`. + +**2. Partidos por estado (el partido 3 ya aparece `cancelado`, se +corrigio con el `UPDATE` de `dml/operaciones.sql`):** + +```text +id_partido | fecha_partido | estado +3 | 2026-08-04 | cancelado +1 | 2026-08-02 | jugado +2 | 2026-08-03 | jugado +4 | 2026-08-09 | programado +``` + +**3. Club con mas goles anotados en total:** + +```text +nombre_club goles_totales +Real Pixel 3 +Deportivo Codigo 2 +Union Byte 2 +Atletico Bit 1 +``` + +**5. Reporte rapido semanal: goles por club en la jornada 1 (minimo +2 goles):** + +```text +nombre_club goles_jornada +Real Pixel 3 +Union Byte 2 +Deportivo Codigo 2 +``` + +(Atletico Bit anoto solo 1 gol en la jornada 1 y no llega al minimo +del reporte; el gol huerfano del partido 3 ya no cuenta porque se +elimino en las operaciones de mantenimiento.) + +## Operaciones de mantenimiento verificadas + +- `UPDATE partidos SET estado = 'cancelado' WHERE id_partido = 3 ...;` → el partido del 2026-08-04 se anulo despues de confirmarse la desconexion del servidor. +- **DELETE controlado**: se elimino el unico gol que habia quedado huerfano (el del partido 3), apenas se marco `cancelado`. Total de goles: 9 -> 8. Ningun gol de un partido `jugado` se toco. + +## Aprendizaje + +El marcador de un partido nunca se guarda como un numero fijo: se +calcula siempre sumando los goles registrados en `goles`, por eso el +reporte rapido semanal que pidio el cliente (`GROUP BY` + `HAVING`) +se puede construir directamente desde el historial de goles, sin +depender de que alguien haya actualizado a mano un marcador. El +`DELETE` controlado solo alcanza goles de partidos `cancelado`, nunca +de uno `jugado` cuyo resultado ya es oficial, lo que garantiza que el +reporte semanal siempre refleje datos confiables.