diff --git a/resoluciones/maria-montepeque/ejercicio-87/README.md b/resoluciones/maria-montepeque/ejercicio-87/README.md new file mode 100644 index 00000000..d4371d79 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-87/README.md @@ -0,0 +1,76 @@ +# Ejercicio 87: ORDER BY Nivel Intermedio + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +ORDER BY + +## Descripcion del problema + +Una clinica necesita ver sus citas en el orden en que realmente +importan para la operacion diaria: primero las programadas (las que +todavia hay que atender), despues las atendidas y al final las +canceladas, y solo las mas urgentes, no la lista completa. + +## Tablas y relaciones + +- `medicos`: catalogo de medicos. +- `pacientes`: catalogo de pacientes. +- `citas`: tabla principal. `pacientes` 1—N `citas`; `medicos` 1—N + `citas`. + +## Uso de ORDER BY + +En `dql/consultas.sql`: + +1. Orden simple por dos columnas: `ORDER BY fecha_cita, hora_cita` + (nivel basico, punto de partida). +2. Orden por prioridad de negocio con `CASE WHEN` (nivel intermedio): + la consulta 5 ordena las citas por un orden de prioridad definido + a mano (`'programada'` = 1, `'atendida'` = 2, `'cancelada'` = 3), + en vez del orden alfabetico que darian esos mismos textos. +3. `LIMIT` combinado con `ORDER BY`: se queda solo con las 3 citas + mas urgentes segun ese orden personalizado, en vez de traer toda + la tabla. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `citas.id_paciente`, `citas.id_medico`. +- `NOT NULL` en todas las columnas obligatorias. +- `UNIQUE`: `medicos.nombre_medico`, `pacientes.telefono`. +- `CHECK`: `citas.estado IN (...)`. +- `DEFAULT` en `citas.estado`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dql/consultas.sql`) + +`SELECT estado, COUNT(*) FROM citas GROUP BY estado ORDER BY +horacita;` falla porque `horacita` no es el nombre real de la columna +(falta el guion bajo de `hora_cita`). Se valido con Python +(`sqlite3`): lanza `no such column: horacita`. + +Se probo primero un caso distinto: ordenar una consulta agrupada por +una columna que no esta en el `GROUP BY` ni en una funcion de +agregacion. En SQLite esa combinacion **si es valida** (a diferencia +de motores mas estrictos como PostgreSQL), asi que no sirve como caso +que falla; se reemplazo por el error de escritura real. + +## 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). + +## Como ejecutar + +```bash +sqlite3 ejercicio-87.db < ddl/schema.sql +sqlite3 ejercicio-87.db < dml/inserts.sql +sqlite3 ejercicio-87.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-87/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-87/ddl/schema.sql new file mode 100644 index 00000000..8af0e058 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-87/ddl/schema.sql @@ -0,0 +1,30 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 87: ORDER BY Nivel Intermedio +-- Tema central: ORDER BY +-- Contexto: agenda de citas medicas por fecha. + +CREATE TABLE medicos ( + id_medico INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_medico TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +CREATE TABLE pacientes ( + id_paciente INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_paciente TEXT NOT NULL, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE citas ( + id_cita INTEGER PRIMARY KEY AUTOINCREMENT, + id_paciente INTEGER NOT NULL, + id_medico INTEGER NOT NULL, + fecha_cita TEXT NOT NULL, + hora_cita TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'atendida', 'cancelada')), + + FOREIGN KEY (id_paciente) REFERENCES pacientes (id_paciente), + FOREIGN KEY (id_medico) REFERENCES medicos (id_medico) +); diff --git a/resoluciones/maria-montepeque/ejercicio-87/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-87/dml/inserts.sql new file mode 100644 index 00000000..f763fe97 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-87/dml/inserts.sql @@ -0,0 +1,26 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 87: ORDER BY Nivel Intermedio +-- Datos de prueba. + +INSERT INTO medicos (nombre_medico, especialidad) VALUES + ('Dra. Sofia Ramirez', 'Medicina General'), + ('Dr. Carlos Perez', 'Pediatria'), + ('Dra. Marta Lopez', 'Traumatologia'); + +INSERT INTO pacientes (nombre_paciente, telefono) VALUES + ('Manuel Estrada', '5555-4501'), + ('Alejandra Chinchilla', '5555-4502'), + ('Byron Xicay', '5555-4503'), + ('Cristina Barrios', '5555-4504'), + ('Diego Paz', '5555-4505'); + +INSERT INTO citas (id_paciente, id_medico, fecha_cita, hora_cita, estado) VALUES + (1, 1, '2026-08-20', '09:00', 'atendida'), + (2, 2, '2026-08-20', '10:00', 'atendida'), + (3, 3, '2026-08-20', '11:00', 'programada'), + (4, 1, '2026-08-20', '14:00', 'programada'), + (5, 2, '2026-08-21', '08:00', 'cancelada'), + (1, 1, '2026-08-21', '09:00', 'programada'), + (3, 2, '2026-08-21', '11:00', 'programada'), + (2, 3, '2026-08-22', '10:00', 'programada'); diff --git a/resoluciones/maria-montepeque/ejercicio-87/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-87/dql/consultas.sql new file mode 100644 index 00000000..3598a980 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-87/dql/consultas.sql @@ -0,0 +1,49 @@ +.headers on +.mode column + +-- Ejercicio 87: ORDER BY Nivel Intermedio +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales. +SELECT c.id_cita, p.nombre_paciente, m.nombre_medico, c.fecha_cita, c.hora_cita, c.estado +FROM citas c +JOIN pacientes p ON p.id_paciente = c.id_paciente +JOIN medicos m ON m.id_medico = c.id_medico; + +-- 2. Consulta con WHERE: citas del medico 1. +SELECT id_cita, fecha_cita, hora_cita, estado +FROM citas +WHERE id_medico = 1; + +-- 3. Consulta con ORDER BY: citas ordenadas por fecha y hora. +SELECT id_cita, fecha_cita, hora_cita +FROM citas +ORDER BY fecha_cita, hora_cita; + +-- 4. Conteo o resumen: citas por estado. +SELECT estado, COUNT(*) AS total +FROM citas +GROUP BY estado; + +-- 5. Validacion especifica de ORDER BY (nivel intermedio): orden por +-- prioridad de negocio en vez de orden alfabetico, usando CASE WHEN +-- dentro del ORDER BY, combinado con LIMIT para quedarse solo con las +-- 3 proximas citas mas urgentes (las programadas van primero, sin +-- importar que "atendida" y "cancelada" vengan antes alfabeticamente). +SELECT c.id_cita, p.nombre_paciente, c.fecha_cita, c.hora_cita, c.estado +FROM citas c +JOIN pacientes p ON p.id_paciente = c.id_paciente +ORDER BY + CASE c.estado + WHEN 'programada' THEN 1 + WHEN 'atendida' THEN 2 + WHEN 'cancelada' THEN 3 + END, + c.fecha_cita, + c.hora_cita +LIMIT 3; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: escribir mal el nombre de la columna en el ORDER BY +-- (typo: "horacita" en vez de "hora_cita"). +-- SELECT estado, COUNT(*) FROM citas GROUP BY estado ORDER BY horacita; diff --git a/resoluciones/maria-montepeque/ejercicio-87/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-87/evidencias/resultados.md new file mode 100644 index 00000000..6e65f2cd --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-87/evidencias/resultados.md @@ -0,0 +1,59 @@ +# Evidencias - Ejercicio 87 + +## Tema + +ORDER BY + +## 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-87.db < ddl/schema.sql +sqlite3 ejercicio-87.db < dml/inserts.sql +sqlite3 ejercicio-87.db < dql/consultas.sql +``` + +## Resultados + +**Caso comentado verificado:** + +- `SELECT estado, COUNT(*) FROM citas GROUP BY estado ORDER BY horacita;` → `no such column: horacita` (falta el guion bajo de `hora_cita`). + +Nota: se probo primero un caso ordenando por una columna que no +aparece en el `SELECT` ni en el `GROUP BY` (`ORDER BY hora_cita` sobre +una consulta agrupada por `estado`), pero en SQLite eso **si es +valido** (a diferencia de otros motores mas estrictos): SQLite toma un +valor arbitrario de esa columna por cada grupo. Por eso se reemplazo +por el error de escritura real. + +**5. Proximas 3 citas mas urgentes (orden por prioridad de negocio con +`CASE WHEN`, no alfabetico, y `LIMIT`):** + +```text +id_cita | nombre_paciente | fecha_cita | hora_cita | estado +3 | Byron Xicay | 2026-08-20 | 11:00 | programada +4 | Cristina Barrios | 2026-08-20 | 14:00 | programada +6 | Manuel Estrada | 2026-08-21 | 09:00 | programada +``` + +Las citas `'programada'` aparecen primero (prioridad 1 en el `CASE +WHEN`), aunque alfabeticamente "atendida" y "cancelada" vendrian antes +que "programada". Dentro de las programadas, se ordenan por fecha y +hora, y `LIMIT 3` deja solo las 3 mas cercanas. + +## Aprendizaje + +`ORDER BY` no esta limitado a ordenar por el valor literal de una +columna: un `CASE WHEN` dentro del `ORDER BY` permite definir un orden +de prioridad de negocio (programada antes que atendida antes que +cancelada) que no coincide con el orden alfabetico natural del texto. +Combinado con `LIMIT`, se puede quedar solo con los primeros +resultados de ese orden personalizado (las citas mas urgentes), sin +tener que traer toda la tabla y filtrar despues en el codigo de la +aplicacion. El caso comentado confirma que SQLite es mas permisivo que +otros motores al ordenar resultados agrupados por columnas fuera del +`GROUP BY`, pero sigue exigiendo que el nombre de la columna exista de +verdad. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/README.md new file mode 100644 index 00000000..d4be7667 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/README.md @@ -0,0 +1,86 @@ +# Solicitud SQL - Ejercicio 087: Club Futbol Sala + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un club registra jugadores, partidos, goles, tarjetas y posiciones. El +cliente pide que el sistema permita corregir estados sin borrar +informacion importante. 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 + +Los goles y tarjetas de un partido ya jugado son historico oficial +del torneo: no se borran, se corrige el estado del partido con +`UPDATE` cuando algo cambia. Es un nivel 5 (solicitud profesional): +ademas del modelo, se pide interpretar ambiguedad, normalizar datos, +documentar decisiones y crear al menos una vista SQL. 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. `UNIQUE (id_equipo, + numero_camiseta)` impide repetir un numero de camiseta en el mismo + equipo. +- `partidos`: tabla transaccional, cada enfrentamiento entre dos + equipos. +- `goles`, `tarjetas`: historico oficial de cada partido. + +## Vista SQL + +`vista_resumen_partidos` (definida en +[ddl/schema.sql](ddl/schema.sql)) junta partido con los nombres de +ambos equipos, sin repetir el doble `JOIN` cada vez. + +## Como se relacionan + +`equipos` 1:N `jugadores`; `equipos` 1:N `partidos` (como local y como +visitante); `partidos` 1:N `goles` y `tarjetas`; `jugadores` 1:N +`goles` y `tarjetas`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +3 equipos, 6 jugadores, 3 partidos (marcados `finalizado` en algun +momento), 7 goles y 4 tarjetas, incluidos un gol y una tarjeta +cargados por error para un partido que despues se descubrio que habia +que suspender. Tambien un `INSERT` comentado que reproduce el problema +de repetir un numero de camiseta 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 +(el partido con corte de luz pasa a `suspendido`) y dos `DELETE` +controlados que limpian el gol y la tarjeta huerfanos de ese partido, +sin tocar ningun gol ni tarjeta de un partido ya `finalizado`. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): el resumen de partidos +usando la vista, en que estado esta cada partido, que jugador tiene +mas goles, los goles ordenados por partido y minuto, y un reporte con +`GROUP BY` + `HAVING` de jugadores con 2 o mas goles, candidatos a +mejor jugador del torneo. + +## 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-087.db < ddl/schema.sql +sqlite3 ejercicio-087.db < dml/inserts.sql +sqlite3 ejercicio-087.db < dml/operaciones.sql +sqlite3 ejercicio-087.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/analisis/requerimiento.md new file mode 100644 index 00000000..46ad597a --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/analisis/requerimiento.md @@ -0,0 +1,91 @@ +# Analisis del requerimiento - Ejercicio 087 + +## Solicitud entendida + +Un club registra jugadores, partidos, goles, tarjetas y posiciones de +un torneo de futbol sala. El cliente pide que el sistema permita +corregir estados sin borrar informacion importante: los goles y +tarjetas de un partido ya jugado son historico oficial y no se +borran, solo se corrige el estado del partido cuando algo cambia. Es +un nivel 5 (solicitud profesional): ademas del modelo, se pide +interpretar ambiguedad, normalizar datos, documentar decisiones y +crear al menos una vista SQL. + +## Entidades detectadas + +| Entidad | Por que existe | Atributos importantes | +| --- | --- | --- | +| equipos | Catalogo: cada equipo del torneo | nombre_equipo (unico), categoria | +| jugadores | Catalogo: cada jugador, de un equipo | nombre_jugador, numero_camiseta | +| partidos | Tabla transaccional: enfrentamiento entre dos equipos | fecha_partido, estado | +| goles | Historico: cada gol anotado en un partido | minuto | +| tarjetas | Historico: cada tarjeta mostrada en un partido | tipo_tarjeta, minuto | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| equipos -> jugadores | 1:N | Un equipo tiene varios jugadores. | +| equipos -> partidos | 1:N (dos veces) | Un equipo juega muchos partidos, como local o como visitante. | +| partidos -> goles | 1:N | Un partido tiene una fila de goles por cada anotacion. | +| jugadores -> goles | 1:N | Un jugador anota goles en muchos partidos distintos. | +| partidos -> tarjetas | 1:N | Un partido tiene una fila de tarjetas por cada sancion. | +| jugadores -> tarjetas | 1:N | Un jugador puede recibir tarjetas en varios partidos. | + +## Decisiones de modelado y ambiguedad interpretada + +- **"Corregir estados sin borrar informacion importante" (la peticion + central del cliente):** el estado de un partido + (`'programado'`/`'en_curso'`/`'finalizado'`/`'suspendido'`) se + corrige siempre con `UPDATE`. Los goles y tarjetas de un partido ya + `'finalizado'` son historico oficial y no se borran; el `DELETE` + solo se permite sobre goles o tarjetas de un partido que se + suspende, cuando esos registros resultaron ser un error de captura + hecho antes de que se confirmara la suspension. +- **`UNIQUE (id_equipo, numero_camiseta)` en `jugadores`:** ningun + equipo puede repetir el mismo numero de camiseta en dos jugadores + distintos, una regla de negocio real de cualquier liga. +- **Vista SQL:** se crea `vista_resumen_partidos`, que junta partido y + los nombres de ambos equipos en un solo reporte legible. +- **Ambiguedad no resuelta por el cliente:** no se detallo que pasa si + un jugador recibe una segunda tarjeta amarilla en el mismo partido + (deberia implicar expulsion). Se documenta como fuera del alcance de + este nivel: el modelo registra las tarjetas tal como ocurren, sin + calcular automaticamente si eso implica una expulsion. + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): todo jugador debe apuntar a un + equipo real; todo partido debe apuntar a dos equipos reales; todo + gol y toda tarjeta deben apuntar a un partido y a un jugador reales + (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `equipos.nombre_equipo` no se repite + (`UNIQUE`); un jugador no repite numero de camiseta dentro de su + mismo equipo (`UNIQUE` compuesto). +- Regla 3 (valores fuera de rango): `goles.minuto` y + `tarjetas.minuto` siempre entre 1 y 60 (`CHECK`, incluye tiempo + extra de futsal). +- Regla 4: un partido nace `'programado'` y avanza a `'en_curso'`, + `'finalizado'` o `'suspendido'` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: ver decision de modelado arriba (DELETE solo para + registros de un partido suspendido, nunca de uno finalizado). + +## Supuestos + +- No se detallo si un jugador puede cambiar de equipo durante el + torneo; se asume que no, para el alcance de este nivel. +- Se asume que "posiciones" (mencionado en el contexto del cliente) se + calcula como un reporte derivado de goles y resultados, no como una + tabla propia, porque no aporta informacion nueva que no se pueda + calcular desde `partidos` y `goles`. + +## Preguntas que responde la base de datos + +1. Que partidos existen, con sus dos equipos (via la vista + `vista_resumen_partidos`). +2. Que partidos estan programados, en curso, finalizados o + suspendidos. +3. Que jugador tiene mas goles (ranking de actividad). +4. Como se ordenan los goles por partido y por minuto. +5. Que jugadores tienen 2 o mas goles, candidatos a mejor jugador del + torneo. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/ddl/schema.sql new file mode 100644 index 00000000..66ef51aa --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/ddl/schema.sql @@ -0,0 +1,74 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 087: Club Futbol Sala +-- Modelo: equipos -> jugadores (1:N); equipos -> partidos (1:N, como +-- local y como visitante); partidos + jugadores -> goles (1:N cada +-- una); partidos + jugadores -> tarjetas (1:N cada una). + +CREATE TABLE equipos ( + id_equipo INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_equipo TEXT NOT NULL UNIQUE, + categoria TEXT NOT NULL +); + +-- jugadores: el UNIQUE compuesto impide que un equipo repita el mismo +-- numero de camiseta en dos jugadores. +CREATE TABLE jugadores ( + id_jugador INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_jugador TEXT NOT NULL, + id_equipo INTEGER NOT NULL, + numero_camiseta INTEGER NOT NULL CHECK (numero_camiseta >= 1), + + FOREIGN KEY (id_equipo) REFERENCES equipos (id_equipo), + UNIQUE (id_equipo, numero_camiseta) +); + +CREATE TABLE partidos ( + id_partido INTEGER PRIMARY KEY AUTOINCREMENT, + id_equipo_local INTEGER NOT NULL, + id_equipo_visitante INTEGER NOT NULL, + fecha_partido TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'programado' + CHECK (estado IN ('programado', 'en_curso', 'finalizado', 'suspendido')), + + FOREIGN KEY (id_equipo_local) REFERENCES equipos (id_equipo), + FOREIGN KEY (id_equipo_visitante) REFERENCES equipos (id_equipo) +); + +-- goles y tarjetas: historico oficial de cada partido. Solo se borran +-- si el partido se suspende y resultaron ser un error de captura (ver +-- decision documentada en analisis/requerimiento.md). +CREATE TABLE goles ( + id_gol INTEGER PRIMARY KEY AUTOINCREMENT, + id_partido INTEGER NOT NULL, + id_jugador INTEGER NOT NULL, + minuto INTEGER NOT NULL CHECK (minuto BETWEEN 1 AND 60), + + FOREIGN KEY (id_partido) REFERENCES partidos (id_partido), + FOREIGN KEY (id_jugador) REFERENCES jugadores (id_jugador) +); + +CREATE TABLE tarjetas ( + id_tarjeta INTEGER PRIMARY KEY AUTOINCREMENT, + id_partido INTEGER NOT NULL, + id_jugador INTEGER NOT NULL, + tipo_tarjeta TEXT NOT NULL CHECK (tipo_tarjeta IN ('amarilla', 'roja')), + minuto INTEGER NOT NULL CHECK (minuto BETWEEN 1 AND 60), + + FOREIGN KEY (id_partido) REFERENCES partidos (id_partido), + FOREIGN KEY (id_jugador) REFERENCES jugadores (id_jugador) +); + +-- Vista SQL (requerida en nivel 5): resumen legible de cada partido +-- con los nombres de ambos equipos, sin repetir el doble JOIN cada +-- vez. +CREATE VIEW vista_resumen_partidos AS + SELECT + p.id_partido, + eloc.nombre_equipo AS equipo_local, + evis.nombre_equipo AS equipo_visitante, + p.fecha_partido, + p.estado + FROM partidos p + JOIN equipos eloc ON eloc.id_equipo = p.id_equipo_local + JOIN equipos evis ON evis.id_equipo = p.id_equipo_visitante; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/diagramas/diagrama-er.svg new file mode 100644 index 00000000..63b96ab9 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/diagramas/diagrama-er.svg @@ -0,0 +1,62 @@ + + + + + equipos + id_equipo PK + nombre_equipo UNIQUE + + + jugadores + id_jugador PK + id_equipo FK + UNIQUE(equipo, camiseta) + + + partidos + id_partido PK + id_equipo_local/visitante FK + estado CHECK + + + goles + id_gol PK + id_partido FK, id_jugador FK + + + tarjetas + id_tarjeta PK + id_partido FK, id_jugador FK + + + vista_resumen_partidos + doble JOIN a equipos + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:N + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/inserts.sql new file mode 100644 index 00000000..f946f87d --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/inserts.sql @@ -0,0 +1,70 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 087: Club Futbol Sala +-- Datos base: 3 equipos, 6 jugadores, 3 partidos (2 finalizados, 1 +-- marcado finalizado por error que se corrige despues) y sus goles y +-- tarjetas (incluye 1 gol y 1 tarjeta cargados por error en el +-- partido que se suspende). + +INSERT INTO equipos (nombre_equipo, categoria) VALUES + ('Halcones FS', 'Primera Division'), + ('Titanes FS', 'Primera Division'), + ('Rayos FS', 'Primera Division'); + +INSERT INTO jugadores (nombre_jugador, id_equipo, numero_camiseta) VALUES + ('Kevin Us', 1, 7), + ('Oscar Tzul', 1, 10), + ('Melissa Ordonez', 2, 9), + ('Sergio Batz', 2, 11), + ('Diana Perez', 3, 5), + ('Mario Ixtabalan', 3, 4); + +-- Partido 1: Halcones (local) vs Titanes (visitante), finalizado. +INSERT INTO partidos (id_equipo_local, id_equipo_visitante, fecha_partido, estado) VALUES + (1, 2, '2026-08-01', 'finalizado'); + +-- Partido 2: Titanes (local) vs Rayos (visitante), finalizado. +INSERT INTO partidos (id_equipo_local, id_equipo_visitante, fecha_partido, estado) VALUES + (2, 3, '2026-08-03', 'finalizado'); + +-- Partido 3: Halcones vs Rayos. Se marco 'finalizado' y se cargaron +-- gol y tarjeta, pero se corto la luz de la cancha y el partido se +-- suspendio despues. Se corrige en dml/operaciones.sql. +INSERT INTO partidos (id_equipo_local, id_equipo_visitante, fecha_partido, estado) VALUES + (1, 3, '2026-08-05', 'finalizado'); + +-- Goles del partido 1: Kevin Us anota 2, Melissa Ordonez anota 1. +INSERT INTO goles (id_partido, id_jugador, minuto) VALUES + (1, 1, 10), + (1, 1, 25), + (1, 3, 30); + +-- Tarjeta del partido 1. +INSERT INTO tarjetas (id_partido, id_jugador, tipo_tarjeta, minuto) VALUES + (1, 2, 'amarilla', 15); + +-- Goles del partido 2: Melissa Ordonez anota 1, Diana Perez anota 2. +INSERT INTO goles (id_partido, id_jugador, minuto) VALUES + (2, 3, 5), + (2, 5, 20), + (2, 5, 35); + +-- Tarjetas del partido 2. +INSERT INTO tarjetas (id_partido, id_jugador, tipo_tarjeta, minuto) VALUES + (2, 4, 'amarilla', 20), + (2, 6, 'roja', 40); + +-- Gol cargado por error para el partido 3, antes de saber que se +-- habia cortado la luz. Quedara huerfano cuando el partido se marque +-- 'suspendido' en dml/operaciones.sql, y se elimina ahi mismo. +INSERT INTO goles (id_partido, id_jugador, minuto) VALUES + (3, 1, 8); + +-- Tarjeta cargada por error para el mismo partido 3. +INSERT INTO tarjetas (id_partido, id_jugador, tipo_tarjeta, minuto) VALUES + (3, 5, 'amarilla', 12); + +-- Caso comentado que debe fallar (queda comentado): repetir el numero +-- de camiseta 7 en otro jugador de Halcones FS, exactamente el +-- problema que este UNIQUE esta disenado para evitar. +-- INSERT INTO jugadores (nombre_jugador, id_equipo, numero_camiseta) VALUES ('Jugador Nuevo', 1, 7); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/operaciones.sql new file mode 100644 index 00000000..ce372f56 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dml/operaciones.sql @@ -0,0 +1,30 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 087: Club Futbol Sala +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que se corto la luz de la cancha +-- durante el partido 3 y se suspende. +UPDATE partidos +SET estado = 'suspendido' +WHERE id_partido = 3 AND estado = 'finalizado'; + +-- 2 DELETE controlados: el gol y la tarjeta del partido 3 quedaron +-- huerfanos apenas se marco 'suspendido'. Solo se borran registros de +-- partidos 'suspendido'; un partido 'finalizado' nunca pierde sus +-- goles ni tarjetas por estos DELETE. +DELETE FROM goles +WHERE id_partido IN ( + SELECT id_partido FROM partidos WHERE estado = 'suspendido' +); + +DELETE FROM tarjetas +WHERE id_partido IN ( + SELECT id_partido FROM partidos WHERE estado = 'suspendido' +); + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar un gol del partido 1, que ya esta 'finalizado' (historico +-- oficial). Los DELETE de arriba solo alcanzan partidos 'suspendido' +-- por diseno. +-- DELETE FROM goles WHERE id_partido = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dql/consultas.sql new file mode 100644 index 00000000..2f77585e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/dql/consultas.sql @@ -0,0 +1,39 @@ +.headers on +.mode column + +-- Ejercicio 087: Club Futbol Sala +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: se usa la vista +-- vista_resumen_partidos (creada en ddl/schema.sql). +SELECT * +FROM vista_resumen_partidos; + +-- 2. Que partidos estan programados, en curso, finalizados o +-- suspendidos. +SELECT id_partido, fecha_partido, estado +FROM partidos +ORDER BY estado; + +-- 3. Que jugador tiene mas goles (ranking de actividad). +SELECT j.nombre_jugador, COUNT(*) AS total_goles +FROM jugadores j +JOIN goles g ON g.id_jugador = j.id_jugador +GROUP BY j.id_jugador, j.nombre_jugador +ORDER BY total_goles DESC, j.nombre_jugador; + +-- 4. Goles ordenados por partido y, dentro de cada partido, por +-- minuto. +SELECT id_partido, id_jugador, minuto +FROM goles +ORDER BY id_partido, minuto; + +-- 5. Reporte para decision de negocio: jugadores con 2 o mas goles, +-- candidatos a mejor jugador del torneo (GROUP BY + HAVING). +SELECT j.nombre_jugador, + COUNT(*) AS total_goles +FROM jugadores j +JOIN goles g ON g.id_jugador = j.id_jugador +GROUP BY j.id_jugador, j.nombre_jugador +HAVING COUNT(*) >= 2 +ORDER BY total_goles DESC, j.nombre_jugador; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/evidencias/resultados.md new file mode 100644 index 00000000..8dc9bb1d --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-087/evidencias/resultados.md @@ -0,0 +1,65 @@ +# Evidencias - Solicitudes SQL - Ejercicio 087 (Club Futbol Sala) + +## 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-087.db < ddl/schema.sql +sqlite3 ejercicio-087.db < dml/inserts.sql +sqlite3 ejercicio-087.db < dml/operaciones.sql +sqlite3 ejercicio-087.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 3 equipos, 6 jugadores, 3 partidos +(3 marcados `finalizado` en algun momento), 7 goles y 4 tarjetas +(incluye el gol y la tarjeta cargados por error en el partido que se +debia suspender). + +**Caso comentado verificado:** + +- `INSERT INTO jugadores (nombre_jugador, id_equipo, numero_camiseta) VALUES ('Jugador Nuevo', 1, 7);` (repetir la camiseta 7 en Halcones FS) → `UNIQUE constraint failed: jugadores.id_equipo, jugadores.numero_camiseta`. + +**1. Resumen de partidos via `vista_resumen_partidos` (el partido 3 ya +aparece `suspendido`):** + +```text +id_partido | equipo_local | equipo_visitante | fecha_partido | estado +1 | Halcones FS | Titanes FS | 2026-08-01 | finalizado +2 | Titanes FS | Rayos FS | 2026-08-03 | finalizado +3 | Halcones FS | Rayos FS | 2026-08-05 | suspendido +``` + +**3. Jugador con mas goles (empate a 2 entre tres jugadores):** + +```text +nombre_jugador total_goles +Diana Perez 2 +Kevin Us 2 +Melissa Ordonez 2 +``` + +**5. Jugadores con 2 o mas goles (candidatos a mejor jugador del +torneo):** los mismos tres jugadores de la consulta 3. + +## Operaciones de mantenimiento verificadas + +- `UPDATE partidos SET estado = 'suspendido' WHERE id_partido = 3 ...;` → el partido del 2026-08-05 se suspendio despues de confirmarse el corte de luz. +- **DELETE controlado (goles y tarjetas)**: se eliminaron el gol y la tarjeta que habian quedado huerfanos en el partido 3, apenas se marco `suspendido`. Total de goles: 7 -> 6; total de tarjetas: 4 -> 3. Ningun gol ni tarjeta de un partido `finalizado` se toco. + +## Aprendizaje + +El `UNIQUE (id_equipo, numero_camiseta)` en `jugadores` evita que un +equipo repita el mismo numero de camiseta en dos jugadores. Los goles +y tarjetas de un partido `finalizado` son historico oficial y nunca se +borran, tal como pidio el cliente ("corregir estados sin borrar +informacion importante"): el `DELETE` controlado solo alcanza +registros de un partido `suspendido`, y unicamente cuando resultaron +ser un error de captura confirmado. La vista `vista_resumen_partidos` +demuestra la habilidad de nivel 5 de "crear vistas": centraliza el +doble `JOIN` a `equipos` (local y visitante) en un solo objeto +reutilizable.