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

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

## Tema central

GROUP BY

## Descripcion del problema

Un torneo de videojuegos necesita administrar las partidas y los
puntajes de cada equipo, para saber cuantas partidas juega cada uno y
que equipos tienen mejor rendimiento promedio.

## Tablas y relaciones

- `equipos`: catalogo de equipos.
- `jugadores`: catalogo de jugadores, cada uno pertenece a un equipo.
- `partidas`: tabla principal, con `puntaje` por partida jugada.
`equipos` 1—N `jugadores`; `equipos` 1—N `partidas`.

## Uso de GROUP BY

En `dql/consultas.sql`:

1. Conteo simple: `GROUP BY id_equipo` con `COUNT(*)`, para saber
cuantas partidas jugo cada equipo.
2. Suma y promedio con `HAVING`: la consulta 5 agrupa las partidas por
equipo y calcula `SUM(puntaje)` y `AVG(puntaje)` por grupo, y usa
`HAVING AVG(...) > 70` para quedarse solo con los equipos cuyo
promedio supera los 70 puntos.

## Otras restricciones aplicadas

- `PRIMARY KEY` autoincremental en las 3 tablas.
- `FOREIGN KEY`: `jugadores.id_equipo`, `partidas.id_equipo`.
- `NOT NULL` en todas las columnas obligatorias.
- `UNIQUE`: `equipos.nombre_equipo`, `jugadores.gamer_tag`.
- `CHECK`: `partidas.puntaje >= 0`, `partidas.resultado IN (...)`.
- `DEFAULT` en `partidas.resultado`.
- `PRAGMA foreign_keys = ON;` activado al inicio del script.

## Caso que falla / no recomendable (comentado en `dql/consultas.sql`)

Agrupar por equipo (`GROUP BY e.id_equipo`) pero mostrar
`j.nombre_jugador` sin agregarlo ni incluirlo en el `GROUP BY`.
SQLite lo permite (a diferencia de MySQL en modo estricto
`ONLY_FULL_GROUP_BY`, donde fallaria), pero el valor de
`nombre_jugador` que devuelve es arbitrario: se verifico con Python
(`sqlite3`) que para "Dragones Digitales" (2 jugadores, 3 partidas)
el resultado muestra "Alejandra Chinchilla" y un `COUNT(*)` de 6 en
vez de 3, porque el `JOIN` con `jugadores` duplica cada partida por
cada jugador del equipo antes de agrupar. La version correcta (sin el
`JOIN` a `jugadores`) es la de la consulta 4.

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

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

-- Ejercicio 90: GROUP BY Nivel Intermedio
-- Tema central: GROUP BY
-- Contexto: torneo de videojuegos, equipos y sus partidas.

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,
id_equipo INTEGER NOT NULL,
nombre_jugador TEXT NOT NULL,
gamer_tag TEXT NOT NULL UNIQUE,

FOREIGN KEY (id_equipo) REFERENCES equipos (id_equipo)
);

CREATE TABLE partidas (
id_partida INTEGER PRIMARY KEY AUTOINCREMENT,
id_equipo INTEGER NOT NULL,
fecha_partida TEXT NOT NULL,
puntaje INTEGER NOT NULL CHECK (puntaje >= 0),
resultado TEXT NOT NULL DEFAULT 'pendiente'
CHECK (resultado IN ('victoria', 'derrota', 'empate', 'pendiente')),

FOREIGN KEY (id_equipo) REFERENCES equipos (id_equipo)
);
28 changes: 28 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-90/dml/inserts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
PRAGMA foreign_keys = ON;

-- Ejercicio 90: GROUP BY Nivel Intermedio
-- Datos de prueba: 3 equipos, 4 jugadores, 7 partidas.

INSERT INTO equipos (nombre_equipo, region) VALUES
('Dragones Digitales', 'GT-Central'),
('Halcones Nocturnos', 'GT-Occidente'),
('Fenix Cibernetico', 'GT-Oriente');

INSERT INTO jugadores (id_equipo, nombre_jugador, gamer_tag) VALUES
(1, 'Manuel Estrada', 'M-Blaze'),
(1, 'Alejandra Chinchilla', 'AleShadow'),
(2, 'Byron Xicay', 'ByroWolf'),
(3, 'Cristina Barrios', 'CrisFrost');

INSERT INTO partidas (id_equipo, fecha_partida, puntaje, resultado) VALUES
(1, '2026-08-10', 85, 'victoria'),
(1, '2026-08-12', 60, 'derrota'),
(1, '2026-08-14', 90, 'victoria'),
(2, '2026-08-10', 70, 'victoria'),
(2, '2026-08-13', 55, 'derrota'),
(3, '2026-08-11', 40, 'derrota'),
(3, '2026-08-15', 65, 'empate');

-- Caso comentado que no se debe hacer, dejar comentado: registrar una
-- partida con puntaje negativo. El CHECK (puntaje >= 0) lo rechaza.
-- INSERT INTO partidas (id_equipo, fecha_partida, puntaje, resultado) VALUES (1, '2026-08-16', -10, 'derrota');
51 changes: 51 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-90/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.headers on
.mode column

-- Ejercicio 90: GROUP BY Nivel Intermedio
-- Consultas de validacion.

-- 1. Mostrar todos los datos principales.
SELECT p.id_partida, e.nombre_equipo, p.fecha_partida, p.puntaje, p.resultado
FROM partidas p
JOIN equipos e ON e.id_equipo = p.id_equipo;

-- 2. Consulta con WHERE: solo las partidas ganadas.
SELECT id_partida, id_equipo, fecha_partida, puntaje
FROM partidas
WHERE resultado = 'victoria';

-- 3. Consulta con ORDER BY: partidas ordenadas por fecha.
SELECT id_partida, fecha_partida, resultado
FROM partidas
ORDER BY fecha_partida;

-- 4. Conteo o resumen: total de partidas jugadas por equipo (GROUP BY simple).
SELECT id_equipo, COUNT(*) AS total_partidas
FROM partidas
GROUP BY id_equipo;

-- 5. Validacion especifica de GROUP BY: por cada equipo, suma y
-- promedio del puntaje de sus partidas, filtrando con HAVING solo a
-- los equipos cuyo promedio supera los 70 puntos. HAVING filtra
-- despues de agrupar (a diferencia de WHERE, que filtraria filas
-- individuales antes de que existan los grupos).
SELECT e.nombre_equipo,
COUNT(*) AS total_partidas,
SUM(p.puntaje) AS puntaje_total,
ROUND(AVG(p.puntaje), 2) AS promedio_puntaje
FROM partidas p
JOIN equipos e ON e.id_equipo = p.id_equipo
GROUP BY e.id_equipo, e.nombre_equipo
HAVING AVG(p.puntaje) > 70;

-- Caso comentado que no es recomendable, dejar comentado: agrupar por
-- equipo pero mostrar nombre_jugador sin agregarlo ni incluirlo en el
-- GROUP BY. SQLite lo permite (a diferencia de MySQL en modo
-- estricto ONLY_FULL_GROUP_BY, donde fallaria), pero el valor de
-- nombre_jugador que devuelve es arbitrario: no representa a todo el
-- grupo, porque un equipo puede tener varios jugadores.
-- SELECT e.nombre_equipo, j.nombre_jugador, COUNT(*)
-- FROM partidas p
-- JOIN equipos e ON e.id_equipo = p.id_equipo
-- JOIN jugadores j ON j.id_equipo = e.id_equipo
-- GROUP BY e.id_equipo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Evidencias - Ejercicio 90

## Tema

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

## Resultados

**4. Total de partidas por equipo:**

```text
id_equipo total_partidas
1 3
2 2
3 2
```

**5. Equipos con promedio de puntaje mayor a 70:**

```text
nombre_equipo total_partidas puntaje_total promedio_puntaje
Dragones Digitales 3 235 78.33
```

Verificacion manual: Halcones Nocturnos (equipo 2) tiene 2 partidas
de 70+55=125 puntos, promedio 62.5; Fenix Cibernetico (equipo 3) tiene
40+65=105 puntos, promedio 52.5. Ambos por debajo del umbral de 70 y
por eso no aparecen en el resultado.

**Caso comentado verificado (CHECK):**

- `INSERT INTO partidas (..., puntaje, ...) VALUES (1, '2026-08-16', -10, 'derrota');` → `CHECK constraint failed: puntaje >= 0`.

**Caso comentado verificado (GROUP BY no recomendable):**

```text
nombre_equipo nombre_jugador COUNT(*)
Dragones Digitales Alejandra Chinchilla 6
Halcones Nocturnos Byron Xicay 2
Fenix Cibernetico Cristina Barrios 2
```

Para "Dragones Digitales" el `COUNT(*)` deberia ser 3 (su numero real
de partidas), pero da 6 porque el `JOIN` con `jugadores` duplica cada
partida por cada uno de sus 2 jugadores antes de que `GROUP BY` los
agrupe; y el nombre de jugador mostrado ("Alejandra Chinchilla") es
arbitrario, no representa a todo el equipo.

## Aprendizaje

`GROUP BY` agrupa las filas que comparten el mismo valor en la
columna indicada, y las funciones de agregacion (`COUNT`, `SUM`,
`AVG`) calculan un resultado por cada grupo, no por cada fila
individual. `HAVING` filtra esos grupos ya formados (por ejemplo,
solo los equipos con promedio mayor a 70 puntos). Ademas, cualquier
columna que aparezca en el `SELECT` sin estar dentro de una funcion de
agregacion debe estar tambien en el `GROUP BY`; si no lo esta (como
`j.nombre_jugador` en el caso comentado), SQLite no lanza error pero
el valor mostrado es arbitrario y, si ademas el `JOIN` introduce
filas de mas (un equipo con varios jugadores), tambien distorsiona
los conteos y sumas del grupo.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Solicitud SQL - Ejercicio 090: Laboratorio Quimico

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

## Solicitud del cliente

Un laboratorio quimico registra formulas, muestras, reactivos y
resultados. El cliente quiere detectar errores (registros repetidos,
relaciones invalidas o valores fuera de rango) y 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

Ninguna muestra se borra una vez registrada: el historico solo se
corrige con `UPDATE` de estado. 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

- `tecnicos`, `formulas`, `reactivos`: catalogos.
- `muestras`: historico, cada muestra recibida para analisis.
- `resultados`: historico, con `UNIQUE (id_muestra)` para que una
muestra nunca tenga dos resultados oficiales contradictorios.
- `detalle_reactivos`: tabla puente entre muestras y reactivos.

## Vista SQL

`vista_historial_muestra` (definida en
[ddl/schema.sql](ddl/schema.sql)) junta muestra, formula, tecnico y
resultado, respondiendo directamente "que paso y cuando paso" con
cada muestra.

## Como se relacionan

`tecnicos` 1:N `muestras`; `formulas` 1:N `muestras`; `muestras` 1:1
`resultados`; `muestras` 1:N `detalle_reactivos`; `reactivos` 1:N
`detalle_reactivos`. El diagrama esta en
[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg).

## Que datos de prueba use

3 tecnicos, 4 formulas, 5 reactivos, 6 muestras (2 `finalizada`, 1
`rechazada`, 2 `en_analisis`, 1 `recibida`), 3 resultados y 7 lineas
de detalle, incluida una cargada por error en una muestra todavia
`en_analisis`. Tambien un `INSERT` comentado que reproduce el
problema de dos resultados oficiales para la misma muestra y debe
fallar. Detalle en [dml/inserts.sql](dml/inserts.sql).

## Que operaciones de mantenimiento incluyo

En [dml/operaciones.sql](dml/operaciones.sql): un `INSERT` adicional
(nueva muestra recibida), un `UPDATE` de estado (una muestra pasa de
`recibida` a `en_analisis`) y un `DELETE` controlado que corrige la
linea de reactivo agregada por error (solo mientras la muestra sigue
`recibida` o `en_analisis`).

## Que consultas responden al cliente

En [dql/consultas.sql](dql/consultas.sql): el historial completo
usando la vista, que muestras estan en analisis en este momento, que
reactivos se usaron en cada muestra (via `JOIN`), las muestras
ordenadas por fecha de recepcion, un `GROUP BY` del promedio de valor
medido por formula, y un reporte final con `HAVING` de que formulas
tienen muestras rechazadas, para decidir cuales revisar con el
proveedor de reactivos.

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

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