From ddb36a3b3df0e7db55907c14ba59294ea4083e11 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 11:24:38 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 88 --- .../maria-montepeque/ejercicio-88/README.md | 77 +++++++++++++++++++ .../ejercicio-88/ddl/schema.sql | 33 ++++++++ .../ejercicio-88/dml/inserts.sql | 28 +++++++ .../ejercicio-88/dql/consultas.sql | 77 +++++++++++++++++++ .../ejercicio-88/evidencias/resultados.md | 58 ++++++++++++++ 5 files changed, 273 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-88/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-88/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-88/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-88/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-88/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-88/README.md b/resoluciones/maria-montepeque/ejercicio-88/README.md new file mode 100644 index 00000000..e1793301 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-88/README.md @@ -0,0 +1,77 @@ +# Ejercicio 88: ORDER BY Nivel Aplicado + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +ORDER BY + +## Descripcion del problema + +Un torneo de videojuegos necesita su tabla de posiciones oficial: +cada equipo ordenado por puntos, con la diferencia de goles como +desempate y el nombre del equipo como ultimo criterio, calculado +directamente desde el historial de partidas jugadas. Es el caso de +negocio con reporte final propio del nivel aplicado. + +## Tablas y relaciones + +- `equipos`: catalogo de equipos participantes. +- `jugadores`: catalogo de jugadores, cada uno de un equipo. +- `partidas`: tabla principal, cada enfrentamiento entre dos equipos. + `equipos` 1—N `jugadores`; `equipos` 1—N `partidas` (dos veces: + como local y como visitante). + +## Uso de ORDER BY + +La consulta 5 en `dql/consultas.sql` es la tabla de posiciones: + +1. Tres subconsultas correlacionadas calculan, por cada equipo, + `puntos` (3 por victoria, 1 por empate, 0 por derrota), + `goles_favor` y `goles_contra`, contando solo partidas `'jugada'`. +2. El `ORDER BY` final usa multiples criterios de desempate, igual + que una tabla de posiciones real: `puntos DESC`, despues + `(goles_favor - goles_contra) DESC` (una expresion que combina dos + alias definidos en el propio `SELECT`), y por ultimo + `nombre_equipo ASC`. + +## 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 `dql/consultas.sql`) + +Repetir la consulta 1 pero con `ORDER BY 9` falla porque esa consulta +solo tiene 6 columnas, no 9. Se valido con Python (`sqlite3`): lanza +`1st ORDER BY term out of range - should be between 1 and 6`. + +## 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). + +- Tabla de posiciones final: Dragones del Norte (7 pts), Halcones del + Centro (4 pts), Tigres del Oeste (2 pts), Lobos del Sur (0 pts), + verificada a mano contra los resultados de las 5 partidas jugadas. + +## Como ejecutar + +```bash +sqlite3 ejercicio-88.db < ddl/schema.sql +sqlite3 ejercicio-88.db < dml/inserts.sql +sqlite3 ejercicio-88.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-88/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-88/ddl/schema.sql new file mode 100644 index 00000000..9f8bbd24 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-88/ddl/schema.sql @@ -0,0 +1,33 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 88: ORDER BY Nivel Aplicado +-- Tema central: ORDER BY +-- 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) +); + +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-88/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-88/dml/inserts.sql new file mode 100644 index 00000000..a93848e1 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-88/dml/inserts.sql @@ -0,0 +1,28 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 88: ORDER BY Nivel Aplicado +-- Datos de prueba. + +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); + +-- Partidas jugadas. +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida, puntaje_local, puntaje_visitante, estado) VALUES + (1, 2, '2026-08-01', 3, 1, 'jugada'), + (3, 4, '2026-08-02', 2, 2, 'jugada'), + (2, 3, '2026-08-03', 0, 1, 'jugada'), + (4, 1, '2026-08-04', 1, 1, 'jugada'), + (1, 3, '2026-08-05', 2, 0, 'jugada'); + +-- Partida todavia no jugada. +INSERT INTO partidas (id_equipo_local, id_equipo_visitante, fecha_partida) VALUES + (2, 4, '2026-08-08'); diff --git a/resoluciones/maria-montepeque/ejercicio-88/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-88/dql/consultas.sql new file mode 100644 index 00000000..21982e8f --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-88/dql/consultas.sql @@ -0,0 +1,77 @@ +.headers on +.mode column + +-- Ejercicio 88: ORDER BY Nivel Aplicado +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales. +SELECT p.id_partida, + eloc.nombre_equipo AS equipo_local, + evis.nombre_equipo AS equipo_visitante, + 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. Caso de negocio con reporte final (nivel aplicado): tabla de +-- posiciones del torneo. Cada columna (puntos, goles a favor, goles +-- en contra) se calcula con una subconsulta correlacionada sobre las +-- partidas 'jugada' de cada equipo, y el ORDER BY final usa varios +-- criterios de desempate, igual que una tabla de posiciones real: +-- primero puntos (de mas a menos), despues diferencia de goles (de +-- mas a menos), y por ultimo nombre del equipo (alfabetico) para los +-- casos que sigan empatados. +SELECT + e.nombre_equipo, + ( + SELECT COALESCE(SUM( + CASE + WHEN (p.id_equipo_local = e.id_equipo AND p.puntaje_local > p.puntaje_visitante) + OR (p.id_equipo_visitante = e.id_equipo AND p.puntaje_visitante > p.puntaje_local) + THEN 3 + WHEN p.puntaje_local = p.puntaje_visitante THEN 1 + ELSE 0 + END + ), 0) + FROM partidas p + WHERE p.estado = 'jugada' + AND (p.id_equipo_local = e.id_equipo OR p.id_equipo_visitante = e.id_equipo) + ) AS puntos, + ( + SELECT COALESCE(SUM(CASE WHEN p.id_equipo_local = e.id_equipo THEN p.puntaje_local ELSE p.puntaje_visitante END), 0) + FROM partidas p + WHERE p.estado = 'jugada' + AND (p.id_equipo_local = e.id_equipo OR p.id_equipo_visitante = e.id_equipo) + ) AS goles_favor, + ( + SELECT COALESCE(SUM(CASE WHEN p.id_equipo_local = e.id_equipo THEN p.puntaje_visitante ELSE p.puntaje_local END), 0) + FROM partidas p + WHERE p.estado = 'jugada' + AND (p.id_equipo_local = e.id_equipo OR p.id_equipo_visitante = e.id_equipo) + ) AS goles_contra +FROM equipos e +ORDER BY puntos DESC, (goles_favor - goles_contra) DESC, e.nombre_equipo ASC; + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: ordenar por la posicion de una columna que no existe en +-- el resultado (la consulta 1 solo tiene 6 columnas, no 9). +-- SELECT p.id_partida, eloc.nombre_equipo, evis.nombre_equipo, 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 +-- ORDER BY 9; diff --git a/resoluciones/maria-montepeque/ejercicio-88/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-88/evidencias/resultados.md new file mode 100644 index 00000000..78cc63a6 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-88/evidencias/resultados.md @@ -0,0 +1,58 @@ +# Evidencias - Ejercicio 88 + +## 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-88.db < ddl/schema.sql +sqlite3 ejercicio-88.db < dml/inserts.sql +sqlite3 ejercicio-88.db < dql/consultas.sql +``` + +## Resultados + +**Caso comentado verificado:** + +- La consulta 1 (`SELECT ... ORDER BY 9`) → `1st ORDER BY term out of range - should be between 1 and 6` (esa consulta solo tiene 6 columnas). + +**5. Tabla de posiciones del torneo (reporte final, nivel aplicado):** + +```text +nombre_equipo puntos goles_favor goles_contra +Dragones del Norte 7 6 2 +Halcones del Centro 4 3 4 +Tigres del Oeste 2 3 3 +Lobos del Sur 0 1 4 +``` + +Verificacion manual: + +- Dragones del Norte: gano 2 (3-1 vs Lobos, 2-0 vs Halcones), empato 1 (1-1 vs Tigres) = 3+3+1 = 7 puntos; goles a favor 3+2+1=6, en contra 1+0+1=2. +- Halcones del Centro: empato 1 (2-2 vs Tigres), gano 1 (1-0 vs Lobos), perdio 1 (0-2 vs Dragones) = 1+3+0 = 4 puntos; GF 2+1+0=3, GC 2+0+2=4. +- Tigres del Oeste: empato 2 (2-2 vs Halcones, 1-1 vs Dragones) = 1+1 = 2 puntos; GF 2+1=3, GC 2+1=3. +- Lobos del Sur: perdio 2 (1-3 vs Dragones, 0-1 vs Halcones) = 0 puntos; GF 1+0=1, GC 3+1=4. + +El orden final coincide exactamente: Dragones domina por puntos, y +como ningun equipo empata en puntos con otro, el segundo criterio +(diferencia de goles) no llego a usarse esta vez, pero esta listo +para desempatar si hiciera falta. + +## Aprendizaje + +El `ORDER BY` de este reporte usa tres criterios en cascada, tal como +una tabla de posiciones real: `puntos DESC` primero, despues +`(goles_favor - goles_contra) DESC`, y por ultimo `nombre_equipo ASC` +como desempate final. SQLite permite referenciar los alias definidos +en el `SELECT` (`puntos`, `goles_favor`, `goles_contra`) dentro del +`ORDER BY`, incluso combinandolos en una operacion aritmetica nueva +(`goles_favor - goles_contra`), sin tener que repetir las +subconsultas completas. El caso comentado confirma que ordenar por +posicion numerica sigue siendo fragil: la consulta 1 solo devuelve 6 +columnas, y pedir `ORDER BY 9` la hace fallar de inmediato. From 84b1aeac5092298a6a6b0953035f47a241642c9a Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 11:24:39 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 088 (clinica de tatuajes) --- .../solicitudes-sql/ejercicio-088/README.md | 83 ++++++++++++++++++ .../ejercicio-088/analisis/requerimiento.md | 86 +++++++++++++++++++ .../ejercicio-088/ddl/schema.sql | 69 +++++++++++++++ .../ejercicio-088/diagramas/.gitkeep | 0 .../ejercicio-088/diagramas/diagrama-er.svg | 57 ++++++++++++ .../ejercicio-088/dml/inserts.sql | 59 +++++++++++++ .../ejercicio-088/dml/operaciones.sql | 25 ++++++ .../ejercicio-088/dql/consultas.sql | 42 +++++++++ .../ejercicio-088/evidencias/resultados.md | 71 +++++++++++++++ 9 files changed, 492 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/README.md new file mode 100644 index 00000000..05b00ee2 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/README.md @@ -0,0 +1,83 @@ +# Solicitud SQL - Ejercicio 088: Clinica de Tatuajes + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un estudio de tatuajes agenda sesiones, artistas, estilos y pagos. 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 + +"Rankings, totales y casos pendientes" se tradujo en tres tipos de +consulta concretos: ranking de artistas por actividad, totales de +ingresos por artista, y sesiones `'programada'` como casos +pendientes. 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 + +- `clientes`, `artistas`, `estilos`: catalogos. +- `sesiones`: tabla transaccional, cada cita agendada. +- `pagos`: resultado de una sesion. `UNIQUE (id_sesion)` garantiza un + solo pago oficial por sesion. + +## Vista SQL + +`vista_resumen_sesiones` (definida en +[ddl/schema.sql](ddl/schema.sql)) junta sesion, cliente, artista, +estilo y pago con `LEFT JOIN`, y sirve de base comun para los tres +tipos de reporte que pidio el cliente. + +## Como se relacionan + +`clientes` 1:N `sesiones`; `artistas` 1:N `sesiones`; `estilos` 1:N +`sesiones`; `sesiones` 1:1 `pagos`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +4 clientes, 3 artistas, 3 estilos, 5 sesiones (3 `finalizada` con +pago, 1 `programada` como caso pendiente, 1 marcada `finalizada` por +error con un pago que se corrige despues) y 4 pagos. Tambien un +`INSERT` comentado que reproduce el problema de duplicar un pago para +la misma sesion 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 sesion aplazada pasa a `cancelada`) y un `DELETE` controlado que +elimina el pago invalido de esa sesion, sin tocar ningun pago de una +sesion ya `finalizada`. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): el resumen completo de +sesiones usando la vista, que sesiones estan pendientes, un ranking +de artistas con `ORDER BY` + `LIMIT`, las sesiones ordenadas por +fecha y duracion, y un reporte de totales con `GROUP BY` + `HAVING` +de ingresos por artista, para decidir a quien asignar mas horarios. + +## 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-088.db < ddl/schema.sql +sqlite3 ejercicio-088.db < dml/inserts.sql +sqlite3 ejercicio-088.db < dml/operaciones.sql +sqlite3 ejercicio-088.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/analisis/requerimiento.md new file mode 100644 index 00000000..fa52cb47 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/analisis/requerimiento.md @@ -0,0 +1,86 @@ +# Analisis del requerimiento - Ejercicio 088 + +## Solicitud entendida + +Un estudio de tatuajes agenda sesiones con distintos artistas y +estilos. El cliente quiere consultar rankings, totales y casos +pendientes desde la base de datos: eso significa que el modelo debe +poder mostrar directamente quien es el artista mas activo (ranking), +cuanto se ha cobrado en total (totales), y que sesiones todavia no se +han realizado (casos pendientes). 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 | +| --- | --- | --- | +| clientes | Catalogo: cada cliente que agenda una sesion | nombre_cliente, telefono (unico) | +| artistas | Catalogo: cada tatuador del estudio | nombre_artista (unico), especialidad | +| estilos | Catalogo: cada estilo de tatuaje disponible | nombre_estilo (unico), dificultad | +| sesiones | Tabla transaccional: cada cita agendada | fecha_sesion, duracion_horas, estado | +| pagos | Resultado: el pago de una sesion, uno por sesion | monto, metodo_pago | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| clientes -> sesiones | 1:N | Un cliente puede tener varias sesiones. | +| artistas -> sesiones | 1:N | Un artista atiende muchas sesiones. | +| estilos -> sesiones | 1:N | Un estilo se usa en muchas sesiones distintas. | +| sesiones -> pagos | 1:1 | Cada sesion tiene, como mucho, un pago oficial. | + +## Decisiones de modelado y ambiguedad interpretada + +- **"Rankings, totales y casos pendientes" (la peticion central del + cliente):** se traduce directamente en tres tipos de consulta: + ranking de artistas por actividad (`ORDER BY` + `LIMIT`), totales de + ingresos por artista (`GROUP BY` + `HAVING`), y casos pendientes + como las sesiones en estado `'programada'`. +- **Vista SQL:** se crea `vista_resumen_sesiones`, que junta sesion, + cliente, artista, estilo y pago (si existe) con `LEFT JOIN`, + sirviendo de base para todos esos reportes sin repetir el `JOIN` + cada vez. +- **Ambiguedad no resuelta por el cliente:** no se detallo si el + precio depende de la dificultad del estilo o de la duracion de la + sesion. Se documenta como supuesto: el monto se negocia por sesion y + se guarda en `pagos.monto`, sin una formula automatica basada en + `estilos.dificultad` o `sesiones.duracion_horas`. + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): toda sesion debe apuntar a un + cliente, un artista y un estilo reales; todo pago debe apuntar a una + sesion real (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `clientes.telefono`, + `artistas.nombre_artista` y `estilos.nombre_estilo` no se repiten + (`UNIQUE`); una sesion no puede tener mas de un pago + (`UNIQUE (id_sesion)` en `pagos`). +- Regla 3 (valores fuera de rango): `sesiones.duracion_horas` siempre + mayor que 0; `pagos.monto` nunca negativo (`CHECK`). +- Regla 4: una sesion nace `'programada'` (caso pendiente) y avanza a + `'en_curso'`, `'finalizada'` o `'cancelada'` (`CHECK`); se corrige + con `UPDATE`. +- Regla 5: un pago se elimina con `DELETE` solo cuando la sesion a la + que pertenece se cancela y ese pago resulto ser un error (se + proceso el cobro de una sesion que en realidad se pospuso). Un pago + de una sesion `'finalizada'` nunca se borra. + +## Supuestos + +- Se asume que un cliente puede tener varias sesiones con artistas + distintos, sin restriccion. +- No se detallo un limite de sesiones por dia por artista; se asume + que ese control queda fuera del alcance de este nivel. + +## Preguntas que responde la base de datos + +1. Que sesiones existen, con su cliente, artista, estilo y pago (via + la vista `vista_resumen_sesiones`). +2. Que sesiones estan programadas (casos pendientes), en curso, + finalizadas o canceladas. +3. Que artista tiene mas sesiones (ranking de actividad). +4. Como se ordenan las sesiones por fecha y duracion. +5. Que artista genero mas ingresos en total, para decidir a quien + asignar mas horarios. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/ddl/schema.sql new file mode 100644 index 00000000..7f21db51 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/ddl/schema.sql @@ -0,0 +1,69 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 088: Clinica de Tatuajes +-- Modelo: clientes + artistas + estilos -> sesiones (1:N cada una); +-- sesiones -> pagos (1:1). + +CREATE TABLE clientes ( + id_cliente INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_cliente TEXT NOT NULL, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE artistas ( + id_artista INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_artista TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +CREATE TABLE estilos ( + id_estilo INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_estilo TEXT NOT NULL UNIQUE, + dificultad TEXT NOT NULL CHECK (dificultad IN ('baja', 'media', 'alta')) +); + +CREATE TABLE sesiones ( + id_sesion INTEGER PRIMARY KEY AUTOINCREMENT, + id_cliente INTEGER NOT NULL, + id_artista INTEGER NOT NULL, + id_estilo INTEGER NOT NULL, + fecha_sesion TEXT NOT NULL, + duracion_horas REAL NOT NULL CHECK (duracion_horas > 0), + estado TEXT NOT NULL DEFAULT 'programada' + CHECK (estado IN ('programada', 'en_curso', 'finalizada', 'cancelada')), + + FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente), + FOREIGN KEY (id_artista) REFERENCES artistas (id_artista), + FOREIGN KEY (id_estilo) REFERENCES estilos (id_estilo) +); + +-- pagos: el UNIQUE sobre id_sesion garantiza como maximo un pago +-- oficial por sesion. +CREATE TABLE pagos ( + id_pago INTEGER PRIMARY KEY AUTOINCREMENT, + id_sesion INTEGER NOT NULL UNIQUE, + monto REAL NOT NULL CHECK (monto >= 0), + metodo_pago TEXT NOT NULL CHECK (metodo_pago IN ('efectivo', 'tarjeta')), + fecha_pago TEXT NOT NULL DEFAULT (datetime('now')), + + FOREIGN KEY (id_sesion) REFERENCES sesiones (id_sesion) +); + +-- Vista SQL (requerida en nivel 5): base comun para los rankings, +-- totales y casos pendientes que pidio el cliente, sin repetir el +-- JOIN de 4 tablas cada vez. +CREATE VIEW vista_resumen_sesiones AS + SELECT + s.id_sesion, + cl.nombre_cliente, + ar.nombre_artista, + es.nombre_estilo, + s.fecha_sesion, + s.duracion_horas, + s.estado, + pa.monto AS monto_pagado + FROM sesiones s + JOIN clientes cl ON cl.id_cliente = s.id_cliente + JOIN artistas ar ON ar.id_artista = s.id_artista + JOIN estilos es ON es.id_estilo = s.id_estilo + LEFT JOIN pagos pa ON pa.id_sesion = s.id_sesion; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/diagrama-er.svg new file mode 100644 index 00000000..e9899408 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/diagramas/diagrama-er.svg @@ -0,0 +1,57 @@ + + + + + clientes + id_cliente PK + telefono UNIQUE + + + artistas + id_artista PK + nombre_artista UNIQUE + + + estilos + id_estilo PK + nombre_estilo UNIQUE + + + sesiones + id_sesion PK + id_cliente/artista/estilo FK + estado CHECK + + + pagos + id_pago PK + id_sesion FK UNIQUE (1:1) + + + vista_resumen_sesiones + base para rankings/totales/pendientes + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:1 + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/inserts.sql new file mode 100644 index 00000000..e42f2647 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/inserts.sql @@ -0,0 +1,59 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 088: Clinica de Tatuajes +-- Datos base: 4 clientes, 3 artistas, 3 estilos, 5 sesiones (3 +-- finalizadas con pago, 1 programada = caso pendiente, 1 marcada +-- finalizada por error con un pago que se corrige despues). + +INSERT INTO clientes (nombre_cliente, telefono) VALUES + ('Manuel Estrada', '5555-9101'), + ('Alejandra Chinchilla', '5555-9102'), + ('Byron Xicay', '5555-9103'), + ('Cristina Barrios', '5555-9104'); + +INSERT INTO artistas (nombre_artista, especialidad) VALUES + ('Karla Rivas', 'realismo'), + ('Bryan Solis', 'tradicional americano'), + ('Fernanda Lopez', 'blackwork'); + +INSERT INTO estilos (nombre_estilo, dificultad) VALUES + ('Realismo', 'alta'), + ('Tradicional Americano', 'media'), + ('Blackwork', 'alta'); + +-- Sesion 1: Manuel con Karla Rivas, Realismo, finalizada. +INSERT INTO sesiones (id_cliente, id_artista, id_estilo, fecha_sesion, duracion_horas, estado) VALUES + (1, 1, 1, '2026-08-01', 3, 'finalizada'); +INSERT INTO pagos (id_sesion, monto, metodo_pago) VALUES + (1, 900.00, 'tarjeta'); + +-- Sesion 2: Alejandra con Bryan Solis, Tradicional Americano, +-- finalizada. +INSERT INTO sesiones (id_cliente, id_artista, id_estilo, fecha_sesion, duracion_horas, estado) VALUES + (2, 2, 2, '2026-08-02', 2, 'finalizada'); +INSERT INTO pagos (id_sesion, monto, metodo_pago) VALUES + (2, 500.00, 'efectivo'); + +-- Sesion 3: Byron con Fernanda Lopez, Blackwork, finalizada. +INSERT INTO sesiones (id_cliente, id_artista, id_estilo, fecha_sesion, duracion_horas, estado) VALUES + (3, 3, 3, '2026-08-03', 4, 'finalizada'); +INSERT INTO pagos (id_sesion, monto, metodo_pago) VALUES + (3, 1200.00, 'tarjeta'); + +-- Sesion 4: Cristina con Karla Rivas, Realismo, todavia sin agendar +-- del todo (caso pendiente). +INSERT INTO sesiones (id_cliente, id_artista, id_estilo, fecha_sesion, duracion_horas, estado) VALUES + (4, 1, 1, '2026-08-08', 3, 'programada'); + +-- Sesion 5: Manuel con Bryan Solis, Tradicional Americano. Se marco +-- 'finalizada' y se proceso el pago, pero despues Manuel pidio +-- posponerla. Se corrige en dml/operaciones.sql. +INSERT INTO sesiones (id_cliente, id_artista, id_estilo, fecha_sesion, duracion_horas, estado) VALUES + (1, 2, 2, '2026-08-05', 2, 'finalizada'); +INSERT INTO pagos (id_sesion, monto, metodo_pago) VALUES + (5, 500.00, 'tarjeta'); + +-- Caso comentado que debe fallar (queda comentado): registrar un +-- segundo pago para la sesion 1, exactamente el tipo de dato +-- duplicado que este UNIQUE esta disenado para evitar. +-- INSERT INTO pagos (id_sesion, monto, metodo_pago) VALUES (1, 900.00, 'tarjeta'); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/operaciones.sql new file mode 100644 index 00000000..b563ccf8 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dml/operaciones.sql @@ -0,0 +1,25 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 088: Clinica de Tatuajes +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que Manuel Estrada pidio posponer +-- la sesion 5. +UPDATE sesiones +SET estado = 'cancelada' +WHERE id_sesion = 5 AND estado = 'finalizada'; + +-- 1 DELETE controlado: el pago de la sesion 5 quedo invalido apenas +-- se corrigio el estado (la sesion nunca se realizo de verdad). Solo +-- se borran pagos de sesiones 'cancelada'; una sesion 'finalizada' +-- nunca pierde su pago por este DELETE. +DELETE FROM pagos +WHERE id_sesion IN ( + SELECT id_sesion FROM sesiones WHERE estado = 'cancelada' +); + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar el pago de la sesion 1, que ya esta 'finalizada' (resultado +-- oficial). El DELETE de arriba solo alcanza sesiones 'cancelada' por +-- diseno. +-- DELETE FROM pagos WHERE id_sesion = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dql/consultas.sql new file mode 100644 index 00000000..c0c8225d --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/dql/consultas.sql @@ -0,0 +1,42 @@ +.headers on +.mode column + +-- Ejercicio 088: Clinica de Tatuajes +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: se usa la vista +-- vista_resumen_sesiones (creada en ddl/schema.sql). +SELECT * +FROM vista_resumen_sesiones; + +-- 2. Que sesiones estan programadas (casos pendientes), en curso, +-- finalizadas o canceladas. +SELECT id_sesion, id_cliente, estado +FROM sesiones +ORDER BY estado; + +-- 3. Ranking: que artista tiene mas sesiones (ORDER BY + LIMIT, tal +-- como pidio el cliente). +SELECT ar.nombre_artista, COUNT(*) AS total_sesiones +FROM artistas ar +JOIN sesiones s ON s.id_artista = ar.id_artista +GROUP BY ar.id_artista, ar.nombre_artista +ORDER BY total_sesiones DESC, ar.nombre_artista +LIMIT 3; + +-- 4. Sesiones ordenadas por fecha y, como segundo criterio, por +-- duracion (de mas a menos horas). +SELECT id_sesion, fecha_sesion, duracion_horas +FROM sesiones +ORDER BY fecha_sesion, duracion_horas DESC; + +-- 5. Totales: ingresos totales por artista, para decidir a quien +-- asignar mas horarios (GROUP BY + HAVING, usando la vista para no +-- repetir el JOIN). +SELECT nombre_artista, + SUM(monto_pagado) AS ingresos_totales +FROM vista_resumen_sesiones +WHERE monto_pagado IS NOT NULL +GROUP BY nombre_artista +HAVING SUM(monto_pagado) > 0 +ORDER BY ingresos_totales DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/evidencias/resultados.md new file mode 100644 index 00000000..ea740ff9 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-088/evidencias/resultados.md @@ -0,0 +1,71 @@ +# Evidencias - Solicitudes SQL - Ejercicio 088 (Clinica de Tatuajes) + +## 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-088.db < ddl/schema.sql +sqlite3 ejercicio-088.db < dml/inserts.sql +sqlite3 ejercicio-088.db < dml/operaciones.sql +sqlite3 ejercicio-088.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 4 clientes, 3 artistas, 3 estilos, +5 sesiones (3 `finalizada` con pago, 1 `programada`, 1 marcada +`finalizada` por error con pago) y 4 pagos. + +**Caso comentado verificado:** + +- `INSERT INTO pagos (id_sesion, ...) VALUES (1, ...);` (segundo pago para la sesion 1) → `UNIQUE constraint failed: pagos.id_sesion`. + +**1. Resumen completo via `vista_resumen_sesiones` (ya con la sesion 5 +cancelada y sin pago):** + +```text +id_sesion | nombre_cliente | nombre_artista | nombre_estilo | fecha_sesion | duracion_horas | estado | monto_pagado +1 | Manuel Estrada | Karla Rivas | Realismo | 2026-08-01 | 3.0 | finalizada | 900.0 +2 | Alejandra Chinchilla | Bryan Solis | Tradicional Americano | 2026-08-02 | 2.0 | finalizada | 500.0 +3 | Byron Xicay | Fernanda Lopez | Blackwork | 2026-08-03 | 4.0 | finalizada | 1200.0 +4 | Cristina Barrios | Karla Rivas | Realismo | 2026-08-08 | 3.0 | programada | (NULL) +5 | Manuel Estrada | Bryan Solis | Tradicional Americano | 2026-08-05 | 2.0 | cancelada | (NULL) +``` + +**3. Ranking: artistas con mas sesiones:** + +```text +nombre_artista total_sesiones +Bryan Solis 2 +Karla Rivas 2 +Fernanda Lopez 1 +``` + +**5. Totales: ingresos por artista (para decidir a quien asignar mas +horarios):** + +```text +nombre_artista ingresos_totales +Fernanda Lopez 1200.0 +Karla Rivas 900.0 +Bryan Solis 500.0 +``` + +## Operaciones de mantenimiento verificadas + +- `UPDATE sesiones SET estado = 'cancelada' WHERE id_sesion = 5 ...;` → la sesion de Manuel Estrada se corrigio despues de confirmarse el aplazamiento. +- **DELETE controlado**: se elimino el pago que habia quedado invalido en la sesion 5. Total de pagos: 4 -> 3. Ningun pago de una sesion `finalizada` se toco. + +## Aprendizaje + +La vista `vista_resumen_sesiones` es la base comun para los tres tipos +de reporte que pidio el cliente: el ranking de artistas usa `ORDER BY` ++ `LIMIT` sobre un `GROUP BY`, los totales usan `GROUP BY` + `HAVING` +sobre la misma vista, y los casos pendientes son simplemente un +`WHERE estado = 'programada'`. El `UNIQUE (id_sesion)` en `pagos` +evita registrar dos pagos para la misma sesion, y el `DELETE` +controlado solo corrige pagos de sesiones canceladas, nunca de una ya +`finalizada`.