From 88f4f2ed7d7b01b6efb8627d6d792e29daffb275 Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 08:18:18 -0600 Subject: [PATCH 1/2] feat(sql): resolver ejercicio 85 --- .../maria-montepeque/ejercicio-85/README.md | 75 +++++++++++++++++++ .../ejercicio-85/ddl/schema.sql | 30 ++++++++ .../ejercicio-85/dml/inserts.sql | 28 +++++++ .../ejercicio-85/dql/consultas.sql | 50 +++++++++++++ .../ejercicio-85/evidencias/resultados.md | 57 ++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 resoluciones/maria-montepeque/ejercicio-85/README.md create mode 100644 resoluciones/maria-montepeque/ejercicio-85/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-85/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-85/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/ejercicio-85/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/ejercicio-85/README.md b/resoluciones/maria-montepeque/ejercicio-85/README.md new file mode 100644 index 00000000..547a4579 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-85/README.md @@ -0,0 +1,75 @@ +# Ejercicio 85: WHERE Nivel Aplicado + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +WHERE + +## Descripcion del problema + +Una clinica agenda citas por fecha y necesita dos cosas del dia a dia: +la agenda pendiente de un dia especifico, ordenada por hora, y una +alerta de que pacientes cancelan seguido, para contactarlos antes de +agendarles una cita nueva: un caso de negocio con reporte final, +propio del nivel aplicado. + +## Tablas y relaciones + +- `medicos`: catalogo de medicos. +- `pacientes`: catalogo de pacientes. +- `citas`: tabla principal, relaciona un paciente con un medico en una + fecha y hora. `pacientes` 1—N `citas`; `medicos` 1—N `citas`. + +## Uso de WHERE + +En `dql/consultas.sql`: + +1. Consulta 2: agenda pendiente de un dia especifico + (`WHERE fecha_cita = '2026-08-20' AND estado = 'programada'`, + ordenada por hora), el caso de negocio central del ejercicio. +2. Consulta 5 (reporte final): pacientes con 2 o mas citas + canceladas, usando `WHERE id_paciente IN (subconsulta)`, donde la + subconsulta tiene su propio `GROUP BY` y `HAVING`. Esto combina + filtrado simple con deteccion de un patron agregado, en una sola + consulta legible. + +## 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 nombre_paciente FROM pacientes p WHERE x.nombre_paciente = +'Manuel Estrada';` falla porque la tabla se declaro con el alias `p` +en el `FROM`, pero el `WHERE` intenta usar un alias `x` que nunca se +declaro. Se valido con Python (`sqlite3`): lanza +`no such column: x.nombre_paciente`. + +## Evidencias de ejecucion + +Scripts validados en orden (`ddl` -> `inserts` -> `consultas`) con +Python (modulo `sqlite3`), ya que no se tenia el binario `sqlite3` +disponible en el entorno. Detalle completo en +[`evidencias/resultados.md`](evidencias/resultados.md). + +- Datos finales: 3 medicos, 5 pacientes, 10 citas. Agenda pendiente + del 2026-08-20: 2 citas. Diego Paz es el unico paciente con 2 o mas + cancelaciones. + +## Como ejecutar + +```bash +sqlite3 ejercicio-85.db < ddl/schema.sql +sqlite3 ejercicio-85.db < dml/inserts.sql +sqlite3 ejercicio-85.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-85/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-85/ddl/schema.sql new file mode 100644 index 00000000..b7ddc4c5 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-85/ddl/schema.sql @@ -0,0 +1,30 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 85: WHERE Nivel Aplicado +-- Tema central: WHERE +-- 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-85/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-85/dml/inserts.sql new file mode 100644 index 00000000..b1232a05 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-85/dml/inserts.sql @@ -0,0 +1,28 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 85: WHERE Nivel Aplicado +-- 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-4401'), + ('Alejandra Chinchilla', '5555-4402'), + ('Byron Xicay', '5555-4403'), + ('Cristina Barrios', '5555-4404'), + ('Diego Paz', '5555-4405'); + +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-18', '09:00', 'cancelada'), + (5, 3, '2026-08-19', '10:00', 'cancelada'), + (1, 1, '2026-08-21', '09:00', 'programada'), + (3, 2, '2026-08-21', '11:00', 'programada'), + (2, 3, '2026-08-17', '15:00', 'atendida'), + (5, 1, '2026-08-22', '10:00', 'programada'); diff --git a/resoluciones/maria-montepeque/ejercicio-85/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-85/dql/consultas.sql new file mode 100644 index 00000000..f443a058 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-85/dql/consultas.sql @@ -0,0 +1,50 @@ +.headers on +.mode column + +-- Ejercicio 85: WHERE Nivel Aplicado +-- 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: agenda pendiente del 2026-08-20 (solo citas +-- programadas de ese dia especifico), ordenada por hora. Es el caso +-- de negocio: lo que la recepcion necesita ver esa manana. +SELECT c.id_cita, p.nombre_paciente, m.nombre_medico, c.hora_cita +FROM citas c +JOIN pacientes p ON p.id_paciente = c.id_paciente +JOIN medicos m ON m.id_medico = c.id_medico +WHERE c.fecha_cita = '2026-08-20' AND c.estado = 'programada' +ORDER BY c.hora_cita; + +-- 3. Consulta con ORDER BY: todas las citas ordenadas por fecha y +-- hora. +SELECT id_cita, fecha_cita, hora_cita, estado +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. Caso de negocio con reporte final (nivel aplicado): pacientes +-- con 2 o mas citas canceladas, para que la clinica los contacte +-- antes de agendarles una cita nueva. Combina WHERE con una +-- subconsulta que a su vez usa GROUP BY y HAVING. +SELECT nombre_paciente, telefono +FROM pacientes +WHERE id_paciente IN ( + SELECT id_paciente + FROM citas + WHERE estado = 'cancelada' + GROUP BY id_paciente + HAVING COUNT(*) >= 2 +); + +-- Caso comentado que debe fallar (no ser recomendable), dejar +-- comentado: usar un alias de tabla que no se declaro en el FROM. +-- SELECT nombre_paciente FROM pacientes p WHERE x.nombre_paciente = 'Manuel Estrada'; diff --git a/resoluciones/maria-montepeque/ejercicio-85/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-85/evidencias/resultados.md new file mode 100644 index 00000000..1165c6f6 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-85/evidencias/resultados.md @@ -0,0 +1,57 @@ +# Evidencias - Ejercicio 85 + +## Tema + +WHERE + +## 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-85.db < ddl/schema.sql +sqlite3 ejercicio-85.db < dml/inserts.sql +sqlite3 ejercicio-85.db < dql/consultas.sql +``` + +## Resultados + +**2. Agenda pendiente del 2026-08-20 (caso de negocio: lo que +recepcion necesita ver esa manana), ordenada por hora:** + +```text +id_cita | nombre_paciente | nombre_medico | hora_cita +3 | Byron Xicay | Dra. Marta Lopez | 11:00 +4 | Cristina Barrios | Dra. Sofia Ramirez | 14:00 +``` + +**Caso comentado verificado:** + +- `SELECT nombre_paciente FROM pacientes p WHERE x.nombre_paciente = 'Manuel Estrada';` → `no such column: x.nombre_paciente` (el alias `x` nunca se declaro en el `FROM`; la tabla se declaro como `p`). + +**5. Reporte final del caso de negocio (nivel aplicado): pacientes con +2 o mas citas canceladas, para contactarlos antes de agendarles algo +nuevo:** + +```text +nombre_paciente telefono +Diego Paz 5555-4405 +``` + +Diego Paz tiene exactamente 2 citas canceladas (2026-08-18 y +2026-08-19); es el unico paciente que cumple el umbral. + +## Aprendizaje + +Este ejercicio combino todo lo visto en los niveles basico e +intermedio de `WHERE` en un solo caso de negocio real: filtrar por una +fecha exacta, por estado, ordenar el resultado, y usar una subconsulta +que a su vez tiene su propio `GROUP BY` y `HAVING` para detectar un +patron (pacientes que cancelan seguido). El `WHERE ... IN (subconsulta)` +permite responder una pregunta de dos pasos ("que pacientes cumplen +esta condicion agregada") sin tener que calcularla a mano fuera de +SQL. El caso comentado recuerda que un alias de tabla que no se +declaro en el `FROM` no existe para el resto de la consulta, aunque el +nombre se parezca al de la tabla real. From 56ff39ef0732d6e5941fcd8ba472999ddb764ccb Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 25 Aug 2026 08:18:19 -0600 Subject: [PATCH 2/2] feat(sql): resolver solicitud SQL ejercicio 085 (biblioteca sci-fi) --- .../solicitudes-sql/ejercicio-085/README.md | 88 ++++++++++++++++++ .../ejercicio-085/analisis/requerimiento.md | 92 +++++++++++++++++++ .../ejercicio-085/ddl/schema.sql | 75 +++++++++++++++ .../ejercicio-085/diagramas/.gitkeep | 0 .../ejercicio-085/diagramas/diagrama-er.svg | 60 ++++++++++++ .../ejercicio-085/dml/inserts.sql | 52 +++++++++++ .../ejercicio-085/dml/operaciones.sql | 22 +++++ .../ejercicio-085/dql/consultas.sql | 37 ++++++++ .../ejercicio-085/evidencias/resultados.md | 64 +++++++++++++ 9 files changed, 490 insertions(+) create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/README.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/analisis/requerimiento.md create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/ddl/schema.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/.gitkeep create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/diagrama-er.svg create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/inserts.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/operaciones.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dql/consultas.sql create mode 100644 resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/evidencias/resultados.md diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/README.md new file mode 100644 index 00000000..078ba8fd --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/README.md @@ -0,0 +1,88 @@ +# Solicitud SQL - Ejercicio 085: Biblioteca Sci-Fi + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Una biblioteca especializada presta libros de ciencia ficcion y +controla devoluciones. El cliente no sabe hablar en terminos de +tablas: solo describe su operacion diaria y espera que se traduzca a +SQL. 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 + +A diferencia de versiones mas simples de este mismo caso (donde la +devolucion era solo un cambio de estado dentro de `prestamos`), aqui +se decidio separar `devoluciones` en su propia tabla, porque el +cliente quiere "registrar movimientos" y una devolucion es un +movimiento con su propio detalle (el estado fisico del libro). 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 + +- `autores`: catalogo de autores. +- `libros`: catalogo de libros, cada uno de un autor. +- `lectores`: catalogo de lectores registrados. +- `prestamos`: tabla transaccional, cada prestamo de un libro. +- `devoluciones`: resultado de un prestamo. `UNIQUE (id_prestamo)` + garantiza una sola devolucion oficial por prestamo. + +## Vista SQL + +`vista_resumen_prestamos` (definida en +[ddl/schema.sql](ddl/schema.sql)) junta prestamo, lector, libro, +autor y devolucion (si existe) con `LEFT JOIN`, mostrando en un solo +reporte tanto los prestamos activos como los ya devueltos. + +## Como se relacionan + +`autores` 1:N `libros`; `libros` 1:N `prestamos`; `lectores` 1:N +`prestamos`; `prestamos` 1:1 `devoluciones`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +4 autores, 5 libros, 5 lectores, 6 prestamos y 4 devoluciones +(incluida una cargada por error en el prestamo equivocado). Tambien +un `INSERT` comentado que reproduce el problema de registrar dos +devoluciones para el mismo prestamo y debe fallar. Detalle en +[dml/inserts.sql](dml/inserts.sql). + +## Que operaciones de mantenimiento incluyo + +En [dml/operaciones.sql](dml/operaciones.sql): un `DELETE` controlado +que corrige la devolucion registrada por error (solo cuando es un +error de captura confirmado) y un `UPDATE` de estado (un prestamo que +paso su fecha esperada se marca `atrasado`). + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): el resumen completo de +prestamos usando la vista, en que estado esta cada prestamo, que +lector tiene mas prestamos, los prestamos ordenados por fecha, y un +reporte con `GROUP BY` + `HAVING` (tambien sobre la vista) de que +autor tiene mas prestamos en total, para decidir de cual comprar mas +ejemplares. + +## 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-085.db < ddl/schema.sql +sqlite3 ejercicio-085.db < dml/inserts.sql +sqlite3 ejercicio-085.db < dml/operaciones.sql +sqlite3 ejercicio-085.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/analisis/requerimiento.md new file mode 100644 index 00000000..c4e9781e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/analisis/requerimiento.md @@ -0,0 +1,92 @@ +# Analisis del requerimiento - Ejercicio 085 + +## Solicitud entendida + +Una biblioteca especializada presta libros de ciencia ficcion y +controla devoluciones. El cliente no sabe hablar en terminos de +tablas: solo describe su operacion diaria y espera que se traduzca a +SQL. 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 | +| --- | --- | --- | +| autores | Catalogo: cada autor de ciencia ficcion | nombre_autor (unico), nacionalidad | +| libros | Catalogo: cada libro, de un autor | titulo, genero | +| lectores | Catalogo: cada lector registrado | nombre_lector (unico), email (unico) | +| prestamos | Tabla transaccional: cada prestamo de un libro | fecha_prestamo, fecha_devolucion_esperada, estado | +| devoluciones | Resultado: el evento real de devolucion de un prestamo, uno por prestamo | fecha_devolucion_real, estado_libro | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| autores -> libros | 1:N | Un autor puede tener varios libros en el catalogo. | +| libros -> prestamos | 1:N | Un libro puede tener muchos prestamos a lo largo del tiempo. | +| lectores -> prestamos | 1:N | Un lector puede tener muchos prestamos. | +| prestamos -> devoluciones | 1:1 | Cada prestamo tiene, como mucho, una devolucion registrada. | + +## Decisiones de modelado y ambiguedad interpretada + +- **Separar `devoluciones` de `prestamos` (normalizacion):** en + versiones mas simples de este mismo caso, la devolucion se + registraba solo como un cambio de `estado` dentro de `prestamos`. + Aqui, al ser nivel 5, se decidio separar el evento de devolucion en + su propia tabla, con `UNIQUE (id_prestamo)`, porque el cliente + quiere "registrar movimientos": la devolucion es un movimiento con + su propio detalle (en que estado fisico volvio el libro), no solo un + cambio de estado. Esto tambien evita registrar la misma devolucion + dos veces por error. +- **"El cliente no sabe hablar en terminos de tablas":** su + descripcion informal ("presta libros y controla devoluciones") se + tradujo en el par `prestamos`/`devoluciones` como dos eventos de + negocio distintos, en vez de una sola tabla con un campo de fecha de + devolucion. +- **Vista SQL:** se crea `vista_resumen_prestamos`, que junta + prestamo, libro, autor, lector y devolucion (si existe) con + `LEFT JOIN`, dando una vista completa de cada prestamo sin importar + si ya se devolvio o no. +- **Ambiguedad no resuelta por el cliente:** no se detallo si se cobra + una multa por atraso. Se documenta como fuera del alcance de este + nivel: el modelo solo marca el prestamo como `'atrasado'`, sin + calcular un monto. + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): todo libro debe apuntar a un autor + real; todo prestamo debe apuntar a un libro y a un lector reales; + toda devolucion debe apuntar a un prestamo real (`FOREIGN KEY` en + cadena). +- Regla 2 (registros repetidos): `autores.nombre_autor`, + `lectores.nombre_lector` y `lectores.email` no se repiten + (`UNIQUE`); un mismo autor no repite el mismo titulo dos veces + (`UNIQUE` compuesto); un prestamo no puede tener dos devoluciones + (`UNIQUE (id_prestamo)`). +- Regla 3 (valores fuera de rango): + `fecha_devolucion_esperada > fecha_prestamo` (`CHECK`). +- Regla 4: un prestamo nace `'prestado'` y avanza a `'devuelto'`, + `'atrasado'` o `'perdido'` (`CHECK`); se corrige con `UPDATE` cuando + pasa la fecha esperada sin devolucion. +- Regla 5: una devolucion se puede eliminar con `DELETE` solo cuando + fue un error de captura confirmado (se registro sobre el prestamo + equivocado). El historico real de devoluciones no se borra. + +## Supuestos + +- Se asume que el estado fisico del libro al devolverse + (`estado_libro`) es informacion util para decidir si el libro sigue + disponible para prestamo o necesita reemplazo. +- No se detallo un limite de prestamos simultaneos por lector; se + asume que no hay limite en el alcance de este nivel. + +## Preguntas que responde la base de datos + +1. Que prestamos existen, con su libro, autor, lector y devolucion (si + existe), via la vista `vista_resumen_prestamos`. +2. Que prestamos estan prestados, atrasados, devueltos o perdidos. +3. Que lector tiene mas prestamos (ranking de actividad). +4. Como se ordenan los prestamos por fecha. +5. Que autor tiene mas prestamos en total, para decidir de cual + comprar mas ejemplares. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/ddl/schema.sql new file mode 100644 index 00000000..07f65888 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/ddl/schema.sql @@ -0,0 +1,75 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 085: Biblioteca Sci-Fi +-- Modelo: autores -> libros (1:N); libros + lectores -> prestamos +-- (1:N cada una); prestamos -> devoluciones (1:1). A diferencia de +-- versiones mas simples de este mismo caso, aqui la devolucion es su +-- propia tabla (ver decision documentada en analisis/requerimiento.md). + +CREATE TABLE autores ( + id_autor INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_autor TEXT NOT NULL UNIQUE, + nacionalidad TEXT NOT NULL +); + +CREATE TABLE libros ( + id_libro INTEGER PRIMARY KEY AUTOINCREMENT, + titulo TEXT NOT NULL, + id_autor INTEGER NOT NULL, + genero TEXT NOT NULL CHECK (genero IN ('space_opera', 'cyberpunk', 'distopia', 'hard_sci_fi')), + + FOREIGN KEY (id_autor) REFERENCES autores (id_autor), + UNIQUE (titulo, id_autor) +); + +CREATE TABLE lectores ( + id_lector INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_lector TEXT NOT NULL UNIQUE, + email TEXT NOT NULL UNIQUE +); + +CREATE TABLE prestamos ( + id_prestamo INTEGER PRIMARY KEY AUTOINCREMENT, + id_libro INTEGER NOT NULL, + id_lector INTEGER NOT NULL, + fecha_prestamo TEXT NOT NULL, + fecha_devolucion_esperada TEXT NOT NULL, + estado TEXT NOT NULL DEFAULT 'prestado' + CHECK (estado IN ('prestado', 'devuelto', 'atrasado', 'perdido')), + + FOREIGN KEY (id_libro) REFERENCES libros (id_libro), + FOREIGN KEY (id_lector) REFERENCES lectores (id_lector), + CHECK (fecha_devolucion_esperada > fecha_prestamo) +); + +-- devoluciones: evento de negocio separado de prestamos (ver +-- analisis/requerimiento.md). UNIQUE (id_prestamo) impide registrar +-- la misma devolucion dos veces. +CREATE TABLE devoluciones ( + id_devolucion INTEGER PRIMARY KEY AUTOINCREMENT, + id_prestamo INTEGER NOT NULL UNIQUE, + fecha_devolucion_real TEXT NOT NULL, + estado_libro TEXT NOT NULL CHECK (estado_libro IN ('bueno', 'danado', 'perdido')), + + FOREIGN KEY (id_prestamo) REFERENCES prestamos (id_prestamo) +); + +-- Vista SQL (requerida en nivel 5): resumen completo de cada +-- prestamo, con LEFT JOIN a devoluciones para que un prestamo +-- todavia activo siga siendo visible en el reporte. +CREATE VIEW vista_resumen_prestamos AS + SELECT + pr.id_prestamo, + lec.nombre_lector, + lib.titulo, + aut.nombre_autor, + pr.fecha_prestamo, + pr.fecha_devolucion_esperada, + pr.estado, + dev.fecha_devolucion_real, + dev.estado_libro + FROM prestamos pr + JOIN lectores lec ON lec.id_lector = pr.id_lector + JOIN libros lib ON lib.id_libro = pr.id_libro + JOIN autores aut ON aut.id_autor = lib.id_autor + LEFT JOIN devoluciones dev ON dev.id_prestamo = pr.id_prestamo; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/diagrama-er.svg new file mode 100644 index 00000000..30b5e687 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/diagramas/diagrama-er.svg @@ -0,0 +1,60 @@ + + + + + autores + id_autor PK + nombre_autor UNIQUE + nacionalidad + + + lectores + id_lector PK + nombre_lector UNIQUE + email UNIQUE + + + libros + id_libro PK + id_autor FK + UNIQUE(titulo, autor) + + + prestamos + id_prestamo PK + id_libro FK, id_lector FK + estado CHECK + + + devoluciones + id_devolucion PK + id_prestamo FK UNIQUE (1:1) + + + vista_resumen_prestamos + LEFT JOIN con devoluciones + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:1 + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/inserts.sql new file mode 100644 index 00000000..b5aedd4c --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/inserts.sql @@ -0,0 +1,52 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 085: Biblioteca Sci-Fi +-- Datos base: 4 autores, 5 libros, 5 lectores, 6 prestamos (3 con +-- devolucion registrada, 2 activos, 1 que se corrige a atrasado) y +-- una devolucion cargada por error en el prestamo equivocado. + +INSERT INTO autores (nombre_autor, nacionalidad) VALUES + ('Frank Herbert', 'Estadounidense'), + ('Isaac Asimov', 'Estadounidense'), + ('William Gibson', 'Estadounidense'), + ('George Orwell', 'Britanico'); + +INSERT INTO libros (titulo, id_autor, genero) VALUES + ('Dune', 1, 'space_opera'), + ('Fundacion', 2, 'space_opera'), + ('Neuromante', 3, 'cyberpunk'), + ('1984', 4, 'distopia'), + ('Yo, Robot', 2, 'hard_sci_fi'); + +INSERT INTO lectores (nombre_lector, email) VALUES + ('Karla Rivas', 'karla.rivas@correo.com'), + ('Bryan Solis', 'bryan.solis@correo.com'), + ('Fernanda Lopez', 'fernanda.lopez@correo.com'), + ('Jorge Cifuentes', 'jorge.cifuentes@correo.com'), + ('Priscila Ajanel', 'priscila.ajanel@correo.com'); + +INSERT INTO prestamos (id_libro, id_lector, fecha_prestamo, fecha_devolucion_esperada, estado) VALUES + (1, 1, '2026-08-01', '2026-08-15', 'devuelto'), + (2, 2, '2026-08-02', '2026-08-16', 'prestado'), + (1, 3, '2026-08-03', '2026-08-17', 'devuelto'), + (4, 4, '2026-08-04', '2026-08-18', 'prestado'), + (3, 1, '2026-08-05', '2026-08-19', 'prestado'), + (5, 5, '2026-08-06', '2026-08-20', 'devuelto'); + +-- Devoluciones reales de los prestamos 1, 3 y 6. +INSERT INTO devoluciones (id_prestamo, fecha_devolucion_real, estado_libro) VALUES + (1, '2026-08-14', 'bueno'), + (3, '2026-08-16', 'bueno'), + (6, '2026-08-19', 'danado'); + +-- Devolucion cargada por error: el bibliotecario registro por +-- accidente la devolucion del prestamo 2 (Bryan Solis, Fundacion), +-- que en realidad sigue prestado. Se corrige con DELETE en +-- dml/operaciones.sql. +INSERT INTO devoluciones (id_prestamo, fecha_devolucion_real, estado_libro) VALUES + (2, '2026-08-10', 'bueno'); + +-- Caso comentado que debe fallar (queda comentado): registrar una +-- segunda devolucion para el prestamo 1, exactamente el problema que +-- este UNIQUE esta disenado para evitar. +-- INSERT INTO devoluciones (id_prestamo, fecha_devolucion_real, estado_libro) VALUES (1, '2026-08-15', 'bueno'); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/operaciones.sql new file mode 100644 index 00000000..84ba0bcd --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dml/operaciones.sql @@ -0,0 +1,22 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 085: Biblioteca Sci-Fi +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 DELETE controlado: la devolucion del prestamo 2 se registro por +-- error (Bryan Solis todavia no ha devuelto Fundacion). Se elimina +-- porque fue un error de captura confirmado. +DELETE FROM devoluciones +WHERE id_prestamo = 2; + +-- 1 UPDATE de estado: el prestamo 4 (Jorge Cifuentes, 1984) paso su +-- fecha de devolucion esperada sin que el libro volviera, se marca +-- como atrasado. +UPDATE prestamos +SET estado = 'atrasado' +WHERE id_prestamo = 4 AND estado = 'prestado'; + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar una devolucion real ya confirmada, por ejemplo la del +-- prestamo 1. El historico real de devoluciones no se borra. +-- DELETE FROM devoluciones WHERE id_prestamo = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dql/consultas.sql new file mode 100644 index 00000000..27173601 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/dql/consultas.sql @@ -0,0 +1,37 @@ +.headers on +.mode column + +-- Ejercicio 085: Biblioteca Sci-Fi +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: se usa la vista +-- vista_resumen_prestamos (creada en ddl/schema.sql). +SELECT * +FROM vista_resumen_prestamos; + +-- 2. Que prestamos estan prestados, atrasados, devueltos o perdidos. +SELECT id_prestamo, id_libro, id_lector, estado +FROM prestamos +ORDER BY estado; + +-- 3. Que lector tiene mas prestamos (ranking de actividad). +SELECT lec.nombre_lector, COUNT(*) AS total_prestamos +FROM lectores lec +JOIN prestamos pr ON pr.id_lector = lec.id_lector +GROUP BY lec.id_lector, lec.nombre_lector +ORDER BY total_prestamos DESC, lec.nombre_lector; + +-- 4. Prestamos ordenados por fecha. +SELECT id_prestamo, fecha_prestamo, estado +FROM prestamos +ORDER BY fecha_prestamo; + +-- 5. Reporte para decision de negocio: autor con mas prestamos en +-- total, para decidir de cual comprar mas ejemplares (GROUP BY + +-- HAVING, usando la vista para no repetir el JOIN). +SELECT nombre_autor, + COUNT(*) AS total_prestamos +FROM vista_resumen_prestamos +GROUP BY nombre_autor +HAVING COUNT(*) >= 1 +ORDER BY total_prestamos DESC, nombre_autor; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/evidencias/resultados.md new file mode 100644 index 00000000..eec7cbf7 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-085/evidencias/resultados.md @@ -0,0 +1,64 @@ +# Evidencias - Solicitudes SQL - Ejercicio 085 (Biblioteca Sci-Fi) + +## 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-085.db < ddl/schema.sql +sqlite3 ejercicio-085.db < dml/inserts.sql +sqlite3 ejercicio-085.db < dml/operaciones.sql +sqlite3 ejercicio-085.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 4 autores, 5 libros, 5 lectores, 6 +prestamos y 4 devoluciones (incluye la cargada por error en el +prestamo equivocado). + +**Caso comentado verificado:** + +- `INSERT INTO devoluciones (id_prestamo, ...) VALUES (1, ...);` (segunda devolucion para el prestamo 1) → `UNIQUE constraint failed: devoluciones.id_prestamo`. + +**1. Resumen completo via `vista_resumen_prestamos` (ya con la +devolucion erronea eliminada y el prestamo 4 marcado atrasado):** + +```text +id_prestamo | nombre_lector | titulo | nombre_autor | fecha_prestamo | fecha_devolucion_esperada | estado | fecha_devolucion_real | estado_libro +1 | Karla Rivas | Dune | Frank Herbert | 2026-08-01 | 2026-08-15 | devuelto | 2026-08-14 | bueno +2 | Bryan Solis | Fundacion | Isaac Asimov | 2026-08-02 | 2026-08-16 | prestado | (NULL) | (NULL) +3 | Fernanda Lopez | Dune | Frank Herbert | 2026-08-03 | 2026-08-17 | devuelto | 2026-08-16 | bueno +4 | Jorge Cifuentes | 1984 | George Orwell | 2026-08-04 | 2026-08-18 | atrasado | (NULL) | (NULL) +5 | Karla Rivas | Neuromante | William Gibson | 2026-08-05 | 2026-08-19 | prestado | (NULL) | (NULL) +6 | Priscila Ajanel | Yo, Robot | Isaac Asimov | 2026-08-06 | 2026-08-20 | devuelto | 2026-08-19 | danado +``` + +**5. Autor con mas prestamos en total (para decidir de cual comprar +mas ejemplares):** + +```text +nombre_autor total_prestamos +Frank Herbert 2 +Isaac Asimov 2 +George Orwell 1 +William Gibson 1 +``` + +## Operaciones de mantenimiento verificadas + +- **DELETE controlado**: se elimino la devolucion cargada por error en el prestamo 2. Total de devoluciones: 4 -> 3. El prestamo 2 sigue correctamente `prestado`. +- `UPDATE prestamos SET estado = 'atrasado' WHERE id_prestamo = 4 ...;` → el prestamo de Jorge Cifuentes paso su fecha esperada sin devolucion. + +## Aprendizaje + +Separar `devoluciones` de `prestamos` (decision documentada en el +analisis) permitio registrar el estado fisico real del libro al +devolverse, ademas de proteger con `UNIQUE (id_prestamo)` contra el +error de registrar la misma devolucion dos veces (como paso con el +prestamo 2, corregido con `DELETE`). La vista `vista_resumen_prestamos`, +con `LEFT JOIN` a devoluciones, muestra tanto los prestamos activos +como los ya devueltos en un solo reporte, sin necesitar dos consultas +separadas.