diff --git a/resoluciones/maria-montepeque/ejercicio-86/README.md b/resoluciones/maria-montepeque/ejercicio-86/README.md new file mode 100644 index 00000000..318a7b47 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-86/README.md @@ -0,0 +1,80 @@ +# Ejercicio 86: ORDER BY Nivel Basico + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Tema central + +ORDER BY + +## Descripcion del problema + +Una biblioteca tecnica necesita mostrar sus libros y prestamos en +distintos ordenes: por cantidad de ejemplares, y agrupados por +categoria con un segundo criterio de desempate, sin tener que +ordenarlos manualmente fuera de la base de datos. + +## Tablas y relaciones + +- `autores`: catalogo de autores. +- `libros`: catalogo de libros, cada uno de un autor. +- `prestamos`: tabla principal, cada prestamo de un libro. `autores` + 1—N `libros`; `libros` 1—N `prestamos`. + +## Uso de ORDER BY + +En `dql/consultas.sql`: + +1. Orden ascendente (por defecto): `ORDER BY ejemplares_totales` + ordena los libros de menos a mas ejemplares sin necesitar `ASC` + explicito. +2. Orden descendente y por varias columnas a la vez: la consulta 5 + usa `ORDER BY categoria ASC, ejemplares_totales DESC`. Primero + agrupa los libros por categoria (alfabeticamente), y dentro de + cada categoria los ordena de mas a menos ejemplares. El segundo + criterio solo desempata dentro de los grupos que forma el primero. + +## Otras restricciones aplicadas + +- `PRIMARY KEY` autoincremental en las 3 tablas. +- `FOREIGN KEY`: `libros.id_autor`, `prestamos.id_libro`. +- `NOT NULL` en todas las columnas obligatorias (excepto + `fecha_devolucion`, `NULL` a proposito mientras el prestamo sigue + activo). +- `UNIQUE`: `autores.nombre_autor`. +- `CHECK`: `libros.ejemplares_totales > 0`. +- `DEFAULT` en `prestamos.fecha_prestamo`. +- `PRAGMA foreign_keys = ON;` activado al inicio del script. + +## Caso que falla / no recomendable (comentado en `dql/consultas.sql`) + +`SELECT titulo, categoria FROM libros ORDER BY 5;` falla porque la +consulta solo devuelve 2 columnas, no 5. Se valido con Python +(`sqlite3`): lanza +`1st ORDER BY term out of range - should be between 1 and 2`. Ordenar +por posicion numerica es fragil: si el orden de las columnas del +`SELECT` cambia, el `ORDER BY` termina apuntando a otra columna sin +avisar; por eso es mejor ordenar siempre por nombre. + +(Se probo primero ordenar `SELECT DISTINCT categoria ... ORDER BY +titulo`, una columna fuera del resultado, pero en SQLite esa +combinacion es valida a diferencia de otros motores, asi que no sirve +como caso que falla; se reemplazo por el de la posicion fuera de +rango.) + +## 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-86.db < ddl/schema.sql +sqlite3 ejercicio-86.db < dml/inserts.sql +sqlite3 ejercicio-86.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite` ni `.sqlite3`. diff --git a/resoluciones/maria-montepeque/ejercicio-86/ddl/schema.sql b/resoluciones/maria-montepeque/ejercicio-86/ddl/schema.sql new file mode 100644 index 00000000..522ed8f2 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-86/ddl/schema.sql @@ -0,0 +1,31 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 86: ORDER BY Nivel Basico +-- Tema central: ORDER BY +-- Contexto: biblioteca tecnica, prestamos de libros. + +CREATE TABLE autores ( + id_autor INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_autor TEXT NOT NULL UNIQUE, + especialidad TEXT NOT NULL +); + +CREATE TABLE libros ( + id_libro INTEGER PRIMARY KEY AUTOINCREMENT, + titulo TEXT NOT NULL, + id_autor INTEGER NOT NULL, + categoria TEXT NOT NULL, + ejemplares_totales INTEGER NOT NULL CHECK (ejemplares_totales > 0), + + FOREIGN KEY (id_autor) REFERENCES autores (id_autor) +); + +CREATE TABLE prestamos ( + id_prestamo INTEGER PRIMARY KEY AUTOINCREMENT, + id_libro INTEGER NOT NULL, + nombre_prestatario TEXT NOT NULL, + fecha_prestamo TEXT NOT NULL DEFAULT (date('now')), + fecha_devolucion TEXT, + + FOREIGN KEY (id_libro) REFERENCES libros (id_libro) +); diff --git a/resoluciones/maria-montepeque/ejercicio-86/dml/inserts.sql b/resoluciones/maria-montepeque/ejercicio-86/dml/inserts.sql new file mode 100644 index 00000000..cb133ea7 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-86/dml/inserts.sql @@ -0,0 +1,27 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 86: ORDER BY Nivel Basico +-- Datos de prueba. + +INSERT INTO autores (nombre_autor, especialidad) VALUES + ('Robert C. Martin', 'Ingenieria de Software'), + ('Donald Knuth', 'Algoritmos'), + ('Martin Fowler', 'Arquitectura de Software'); + +INSERT INTO libros (titulo, id_autor, categoria, ejemplares_totales) VALUES + ('Clean Code', 1, 'Ingenieria', 2), + ('Clean Architecture', 1, 'Arquitectura', 1), + ('The Art of Computer Programming Vol. 1', 2, 'Algoritmos', 1), + ('Refactoring', 3, 'Arquitectura', 3), + ('Patterns of Enterprise Application Architecture', 3, 'Arquitectura', 2); + +INSERT INTO prestamos (id_libro, nombre_prestatario, fecha_prestamo, fecha_devolucion) VALUES + (1, 'Karla Rivas', '2026-08-01', '2026-08-10'), + (4, 'Karla Rivas', '2026-08-04', '2026-08-12'); + +INSERT INTO prestamos (id_libro, nombre_prestatario, fecha_prestamo) VALUES + (1, 'Bryan Solis', '2026-08-05'), + (2, 'Fernanda Lopez', '2026-08-02'), + (3, 'Jorge Cifuentes', '2026-08-03'), + (4, 'Priscila Ajanel', '2026-08-06'), + (5, 'Bryan Solis', '2026-08-07'); diff --git a/resoluciones/maria-montepeque/ejercicio-86/dql/consultas.sql b/resoluciones/maria-montepeque/ejercicio-86/dql/consultas.sql new file mode 100644 index 00000000..1ab8b5f4 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-86/dql/consultas.sql @@ -0,0 +1,46 @@ +.headers on +.mode column + +-- Ejercicio 86: ORDER BY Nivel Basico +-- Consultas de validacion. + +-- 1. Mostrar todos los datos principales. +SELECT p.id_prestamo, l.titulo, a.nombre_autor, p.nombre_prestatario, p.fecha_prestamo +FROM prestamos p +JOIN libros l ON l.id_libro = p.id_libro +JOIN autores a ON a.id_autor = l.id_autor; + +-- 2. Consulta con WHERE: solo los prestamos activos. +SELECT id_prestamo, id_libro, nombre_prestatario +FROM prestamos +WHERE fecha_devolucion IS NULL; + +-- 3. Consulta con ORDER BY ascendente (por defecto): libros +-- ordenados por cantidad de ejemplares, de menor a mayor. +SELECT titulo, ejemplares_totales +FROM libros +ORDER BY ejemplares_totales; + +-- 4. Conteo o resumen: total de libros por categoria. +SELECT categoria, COUNT(*) AS total_libros +FROM libros +GROUP BY categoria; + +-- 5. Validacion especifica de ORDER BY: orden descendente +-- (`DESC`) y orden por varias columnas a la vez. Primero se agrupan +-- los libros por categoria (ascendente, orden alfabetico por +-- defecto) y, dentro de cada categoria, se ordenan por cantidad de +-- ejemplares de mayor a menor (DESC). Esto demuestra que ORDER BY +-- puede combinar mas de una columna, cada una con su propia +-- direccion. +SELECT titulo, categoria, ejemplares_totales +FROM libros +ORDER BY categoria ASC, ejemplares_totales DESC; + +-- 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 solo tiene 2 columnas, no 5). Ordenar por +-- posicion numerica en vez de por nombre es fragil: si el SELECT +-- cambia de orden, el ORDER BY termina apuntando a otra columna sin +-- avisar. +-- SELECT titulo, categoria FROM libros ORDER BY 5; diff --git a/resoluciones/maria-montepeque/ejercicio-86/evidencias/resultados.md b/resoluciones/maria-montepeque/ejercicio-86/evidencias/resultados.md new file mode 100644 index 00000000..8b24a1c3 --- /dev/null +++ b/resoluciones/maria-montepeque/ejercicio-86/evidencias/resultados.md @@ -0,0 +1,67 @@ +# Evidencias - Ejercicio 86 + +## 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-86.db < ddl/schema.sql +sqlite3 ejercicio-86.db < dml/inserts.sql +sqlite3 ejercicio-86.db < dql/consultas.sql +``` + +## Resultados + +**3. Libros ordenados por ejemplares, ascendente (por defecto):** + +```text +titulo ejemplares_totales +Clean Architecture 1 +The Art of Computer Programming Vol. 1 1 +Clean Code 2 +Patterns of Enterprise Application Architecture 2 +Refactoring 3 +``` + +**5. Orden por dos columnas (categoria ascendente, ejemplares +descendente):** + +```text +titulo categoria ejemplares_totales +The Art of Computer Programming Vol. 1 Algoritmos 1 +Refactoring Arquitectura 3 +Patterns of Enterprise Application Architecture Arquitectura 2 +Clean Architecture Arquitectura 1 +Clean Code Ingenieria 2 +``` + +Dentro del grupo "Arquitectura" (que quedo ordenado alfabeticamente +antes que "Ingenieria"), los libros bajan de 3 a 1 ejemplares: el +segundo criterio de orden solo actua dentro de cada grupo del primero. + +**Caso comentado verificado:** + +- `SELECT titulo, categoria FROM libros ORDER BY 5;` → `1st ORDER BY term out of range - should be between 1 and 2` (la consulta solo tiene 2 columnas). + +Nota: se probo primero un caso con `SELECT DISTINCT ... ORDER BY` de +una columna fuera del resultado, pero en SQLite esa combinacion **si +es valida** (a diferencia de otros motores como PostgreSQL), asi que +se reemplazo por el caso de la posicion fuera de rango, que si falla. + +## Aprendizaje + +`ORDER BY` ordena ascendente por defecto y descendente con `DESC`. +Cuando se listan varias columnas separadas por coma, cada una puede +tener su propia direccion, y la segunda columna solo desempata dentro +de los grupos que ya formo la primera (no reordena todo el resultado +de nuevo). Ordenar por la posicion numerica de una columna (`ORDER BY +5`) es fragil: si esa posicion no existe en el resultado, la consulta +falla, y aunque existiera, cualquier cambio en el orden de las +columnas del `SELECT` cambiaria el significado del `ORDER BY` sin +avisar. Por eso es mejor ordenar siempre por nombre de columna. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/README.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/README.md new file mode 100644 index 00000000..d085803e --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/README.md @@ -0,0 +1,85 @@ +# Solicitud SQL - Ejercicio 086: Delivery de Comida + +**Nombre:** Maria Jose Montepeque +**Fecha:** 2026-08-24 + +## Solicitud del cliente + +Un negocio de comida recibe pedidos, repartidores, menus y +calificaciones. El cliente quiere diferenciar catalogos, operaciones y +resultados para no mezclar informacion permanente con movimientos. +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 + +La peticion central es de diseno: separar lo permanente (clientes, +menus, repartidores) de lo operativo (pedidos) y de lo resultante +(pagos), sin mezclar las tres capas en una sola tabla. 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`, `menus`, `repartidores`: catalogos permanentes. +- `pedidos`: operacion, cada pedido nuevo. +- `pagos`: resultado de un pedido. `UNIQUE (id_pedido)` garantiza un + solo pago oficial por pedido. + +## Vista SQL + +`vista_pedidos_completos` (definida en +[ddl/schema.sql](ddl/schema.sql)) junta pedido, cliente, menu, +repartidor y pago con `LEFT JOIN`, manteniendo separadas las tres +capas en un solo reporte legible. + +## Como se relacionan + +`clientes` 1:N `pedidos`; `menus` 1:N `pedidos`; `repartidores` 1:N +`pedidos`; `pedidos` 1:1 `pagos`. El diagrama esta en +[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg). + +## Que datos de prueba use + +4 clientes, 5 platillos, 2 repartidores, 5 pedidos (2 `entregado` con +pago, 1 `en_camino` sin pago, 1 marcado `entregado` por error con un +pago que se corrige despues, 1 `recibido`) y 3 pagos. Tambien un +`INSERT` comentado que reproduce el problema de duplicar un pago para +el mismo pedido 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 pedido que se marco entregado por error pasa a `cancelado`) y un +`DELETE` controlado que elimina el pago invalido de ese pedido, sin +tocar ningun pago de un pedido ya `entregado`. + +## Que consultas responden al cliente + +En [dql/consultas.sql](dql/consultas.sql): el resumen completo de +pedidos usando la vista, en que estado esta cada pedido, que cliente +tiene mas pedidos, los pedidos ordenados por fecha y monto, y un +reporte con `GROUP BY` + `HAVING` (tambien sobre la vista) de ingresos +totales por categoria de menu, para decidir en cual enfocar +promociones. + +## 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-086.db < ddl/schema.sql +sqlite3 ejercicio-086.db < dml/inserts.sql +sqlite3 ejercicio-086.db < dml/operaciones.sql +sqlite3 ejercicio-086.db < dql/consultas.sql +``` + +No suba archivos `.db`, `.sqlite`, `.sqlite3` ni `.dump`. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/analisis/requerimiento.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/analisis/requerimiento.md new file mode 100644 index 00000000..aa000872 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/analisis/requerimiento.md @@ -0,0 +1,88 @@ +# Analisis del requerimiento - Ejercicio 086 + +## Solicitud entendida + +Un negocio de comida recibe pedidos, repartidores, menus y +calificaciones. El cliente quiere diferenciar catalogos, operaciones y +resultados para no mezclar informacion permanente con movimientos. 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 | Capa | Atributos importantes | +| --- | --- | --- | --- | +| clientes | Quien hace el pedido | Catalogo | nombre_cliente, telefono (unico) | +| menus | Cada platillo disponible | Catalogo | nombre_platillo (unico), precio, categoria | +| repartidores | Quien entrega el pedido | Catalogo | nombre_repartidor (unico), vehiculo | +| pedidos | Cada pedido de un cliente | Operacion | cantidad, fecha_pedido, estado | +| pagos | El pago de un pedido | Resultado | monto, metodo_pago | + +## Relaciones detectadas + +| Relacion | Tipo | Explicacion | +| --- | --- | --- | +| clientes -> pedidos | 1:N | Un cliente puede hacer varios pedidos. | +| menus -> pedidos | 1:N | Un platillo aparece en muchos pedidos distintos. | +| repartidores -> pedidos | 1:N | Un repartidor entrega muchos pedidos. | +| pedidos -> pagos | 1:1 | Cada pedido tiene, como mucho, un pago oficial. | + +## Decisiones de modelado y ambiguedad interpretada + +- **Diferenciar catalogos, operaciones y resultados (la peticion + central del cliente):** `clientes`, `menus` y `repartidores` son + catalogos (informacion permanente, cambia poco); `pedidos` es la + operacion (un movimiento nuevo cada vez que alguien pide comida); + `pagos` es el resultado (lo que se cobro realmente). Ninguna tabla + mezcla las tres capas. +- **Un pedido, un platillo:** para mantener el modelo dentro de las 5 + entidades sugeridas por el cliente (sin agregar una sexta tabla de + detalle), se asume que cada fila de `pedidos` representa un platillo + con su cantidad; un cliente que quiere varios platillos distintos + genera varios pedidos. Se documenta como supuesto porque el cliente + no lo aclaro. +- **Vista SQL:** se crea `vista_pedidos_completos`, que junta pedido, + cliente, menu, repartidor y pago (si existe) con `LEFT JOIN`. Separa + visualmente las tres capas en un solo reporte legible, sin mezclar + el dato permanente con el movimiento. +- **Ambiguedad no resuelta por el cliente:** no se detallo si un + repartidor puede rechazar un pedido asignado. Se documenta como + fuera del alcance: el modelo asume que todo pedido con repartidor + asignado sera entregado, salvo que se cancele explicitamente. + +## Reglas de negocio + +- Regla 1 (relaciones invalidas): todo pedido debe apuntar a un + cliente, un menu y un repartidor reales; todo pago debe apuntar a un + pedido real (`FOREIGN KEY` en cadena). +- Regla 2 (registros repetidos): `clientes.telefono`, + `menus.nombre_platillo` y `repartidores.nombre_repartidor` no se + repiten (`UNIQUE`); un pedido no puede tener mas de un pago + (`UNIQUE (id_pedido)` en `pagos`). +- Regla 3 (valores fuera de rango): `menus.precio` y `pagos.monto` + nunca negativos; `pedidos.cantidad` siempre mayor que 0 (`CHECK`). +- Regla 4: un pedido nace `'recibido'` y avanza a `'en_camino'`, + `'entregado'` o `'cancelado'` (`CHECK`); se corrige con `UPDATE`. +- Regla 5: un pago se elimina con `DELETE` solo cuando el pedido al + que pertenece se cancela y ese pago resulto ser un error (se cobro + un pedido que en realidad no se entrego). Un pago de un pedido + `'entregado'` nunca se borra. + +## Supuestos + +- Se asume que el precio cobrado puede diferir del precio de catalogo + (por ejemplo, promociones), por eso el monto real se guarda en + `pagos.monto`, separado de `menus.precio`. +- No se detallo un tiempo maximo de entrega; se asume que ese control + queda fuera del alcance de este nivel. + +## Preguntas que responde la base de datos + +1. Que pedidos existen, con su cliente, menu, repartidor y pago (via + la vista `vista_pedidos_completos`). +2. Que pedidos estan recibidos, en camino, entregados o cancelados. +3. Que cliente tiene mas pedidos (ranking de actividad). +4. Como se ordenan los pedidos por fecha y por monto. +5. Que categoria de menu genero mas ingresos, para decidir en cual + enfocar promociones. diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/ddl/schema.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/ddl/schema.sql new file mode 100644 index 00000000..20746487 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/ddl/schema.sql @@ -0,0 +1,73 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 086: Delivery de Comida +-- Modelo separado en catalogos (clientes, menus, repartidores), +-- operacion (pedidos) y resultado (pagos), tal como pidio el +-- cliente. + +CREATE TABLE clientes ( + id_cliente INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_cliente TEXT NOT NULL, + telefono TEXT NOT NULL UNIQUE +); + +CREATE TABLE menus ( + id_menu INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_platillo TEXT NOT NULL UNIQUE, + precio REAL NOT NULL CHECK (precio >= 0), + categoria TEXT NOT NULL CHECK (categoria IN ('comida', 'bebida', 'postre')) +); + +CREATE TABLE repartidores ( + id_repartidor INTEGER PRIMARY KEY AUTOINCREMENT, + nombre_repartidor TEXT NOT NULL UNIQUE, + vehiculo TEXT NOT NULL +); + +CREATE TABLE pedidos ( + id_pedido INTEGER PRIMARY KEY AUTOINCREMENT, + id_cliente INTEGER NOT NULL, + id_menu INTEGER NOT NULL, + id_repartidor INTEGER NOT NULL, + cantidad INTEGER NOT NULL CHECK (cantidad > 0), + fecha_pedido TEXT NOT NULL DEFAULT (datetime('now')), + estado TEXT NOT NULL DEFAULT 'recibido' + CHECK (estado IN ('recibido', 'en_camino', 'entregado', 'cancelado')), + + FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente), + FOREIGN KEY (id_menu) REFERENCES menus (id_menu), + FOREIGN KEY (id_repartidor) REFERENCES repartidores (id_repartidor) +); + +-- pagos: el UNIQUE sobre id_pedido garantiza como maximo un pago +-- oficial por pedido. +CREATE TABLE pagos ( + id_pago INTEGER PRIMARY KEY AUTOINCREMENT, + id_pedido 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_pedido) REFERENCES pedidos (id_pedido) +); + +-- Vista SQL (requerida en nivel 5): separa visualmente catalogos +-- (cliente, menu, repartidor), operacion (pedido) y resultado (pago) +-- en un solo reporte, con LEFT JOIN para que un pedido sin pago +-- todavia siga siendo visible. +CREATE VIEW vista_pedidos_completos AS + SELECT + pe.id_pedido, + cl.nombre_cliente, + me.nombre_platillo, + me.categoria, + re.nombre_repartidor, + pe.cantidad, + pe.fecha_pedido, + pe.estado, + pa.monto AS monto_pagado + FROM pedidos pe + JOIN clientes cl ON cl.id_cliente = pe.id_cliente + JOIN menus me ON me.id_menu = pe.id_menu + JOIN repartidores re ON re.id_repartidor = pe.id_repartidor + LEFT JOIN pagos pa ON pa.id_pedido = pe.id_pedido; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/diagramas/.gitkeep b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/diagramas/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/diagramas/diagrama-er.svg b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/diagramas/diagrama-er.svg new file mode 100644 index 00000000..de285756 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/diagramas/diagrama-er.svg @@ -0,0 +1,57 @@ + + + + + clientes + id_cliente PK + telefono UNIQUE + + + menus + id_menu PK + nombre_platillo UNIQUE + + + repartidores + id_repartidor PK + nombre_repartidor UNIQUE + + + pedidos + id_pedido PK + id_cliente/menu/repartidor FK + estado CHECK + + + pagos + id_pago PK + id_pedido FK UNIQUE (1:1) + + + vista_pedidos_completos + JOIN de las 5 tablas + + + + 1:N + + + + 1:N + + + + 1:N + + + + 1:1 + diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/inserts.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/inserts.sql new file mode 100644 index 00000000..ec001e83 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/inserts.sql @@ -0,0 +1,57 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 086: Delivery de Comida +-- Datos base: 4 clientes, 5 platillos, 2 repartidores, 5 pedidos (2 +-- entregados con pago, 1 en camino sin pago, 1 marcado entregado por +-- error con un pago que se corrige, 1 recibido) y sus pagos. + +INSERT INTO clientes (nombre_cliente, telefono) VALUES + ('Manuel Estrada', '5555-8801'), + ('Alejandra Chinchilla', '5555-8802'), + ('Byron Xicay', '5555-8803'), + ('Cristina Barrios', '5555-8804'); + +INSERT INTO menus (nombre_platillo, precio, categoria) VALUES + ('Hamburguesa Clasica', 45.00, 'comida'), + ('Pizza Pepperoni', 60.00, 'comida'), + ('Gaseosa', 12.00, 'bebida'), + ('Flan de Caramelo', 20.00, 'postre'), + ('Tacos al Pastor', 35.00, 'comida'); + +INSERT INTO repartidores (nombre_repartidor, vehiculo) VALUES + ('Hugo Marroquin', 'motocicleta'), + ('Esteban Cifuentes', 'bicicleta'); + +-- Pedido 1: Manuel, Hamburguesa Clasica x2, entregado. +INSERT INTO pedidos (id_cliente, id_menu, id_repartidor, cantidad, estado) VALUES + (1, 1, 1, 2, 'entregado'); +INSERT INTO pagos (id_pedido, monto, metodo_pago) VALUES + (1, 90.00, 'tarjeta'); + +-- Pedido 2: Alejandra, Pizza Pepperoni x1, entregado. +INSERT INTO pedidos (id_cliente, id_menu, id_repartidor, cantidad, estado) VALUES + (2, 2, 2, 1, 'entregado'); +INSERT INTO pagos (id_pedido, monto, metodo_pago) VALUES + (2, 60.00, 'efectivo'); + +-- Pedido 3: Byron, Gaseosa x3, en camino (todavia sin pago). +INSERT INTO pedidos (id_cliente, id_menu, id_repartidor, cantidad, estado) VALUES + (3, 3, 1, 3, 'en_camino'); + +-- Pedido 4: Cristina, Tacos al Pastor x2. Se marco 'entregado' y se +-- proceso el pago, pero despues se confirmo que la clienta cancelo el +-- pedido antes de que saliera el repartidor. Se corrige en +-- dml/operaciones.sql. +INSERT INTO pedidos (id_cliente, id_menu, id_repartidor, cantidad, estado) VALUES + (4, 5, 2, 2, 'entregado'); +INSERT INTO pagos (id_pedido, monto, metodo_pago) VALUES + (4, 70.00, 'tarjeta'); + +-- Pedido 5: Manuel, Flan de Caramelo x1, recien recibido. +INSERT INTO pedidos (id_cliente, id_menu, id_repartidor, cantidad, estado) VALUES + (1, 4, 1, 1, 'recibido'); + +-- Caso comentado que debe fallar (queda comentado): registrar un +-- segundo pago para el pedido 1, exactamente el tipo de dato +-- duplicado que este UNIQUE esta disenado para evitar. +-- INSERT INTO pagos (id_pedido, monto, metodo_pago) VALUES (1, 90.00, 'tarjeta'); diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/operaciones.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/operaciones.sql new file mode 100644 index 00000000..2e6f9eeb --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dml/operaciones.sql @@ -0,0 +1,25 @@ +PRAGMA foreign_keys = ON; + +-- Ejercicio 086: Delivery de Comida +-- Operaciones de mantenimiento sobre los datos base. + +-- 1 UPDATE de estado: se confirma que Cristina Barrios cancelo el +-- pedido 4 antes de que saliera el repartidor. +UPDATE pedidos +SET estado = 'cancelado' +WHERE id_pedido = 4 AND estado = 'entregado'; + +-- 1 DELETE controlado: el pago del pedido 4 quedo invalido apenas se +-- corrigio el estado (el pedido nunca se entrego de verdad). Solo se +-- borran pagos de pedidos 'cancelado'; un pedido 'entregado' nunca +-- pierde su pago por este DELETE. +DELETE FROM pagos +WHERE id_pedido IN ( + SELECT id_pedido FROM pedidos WHERE estado = 'cancelado' +); + +-- Caso que debe fallar / no ser recomendable (queda comentado): +-- borrar el pago del pedido 1, que ya esta 'entregado' (resultado +-- oficial). El DELETE de arriba solo alcanza pedidos 'cancelado' por +-- diseno. +-- DELETE FROM pagos WHERE id_pedido = 1; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dql/consultas.sql b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dql/consultas.sql new file mode 100644 index 00000000..c7cc49ec --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/dql/consultas.sql @@ -0,0 +1,41 @@ +.headers on +.mode column + +-- Ejercicio 086: Delivery de Comida +-- Consultas que responden preguntas reales del cliente. + +-- 1. Que registros principales existen: se usa la vista +-- vista_pedidos_completos (creada en ddl/schema.sql). +SELECT * +FROM vista_pedidos_completos; + +-- 2. Que pedidos estan recibidos, en camino, entregados o +-- cancelados. +SELECT id_pedido, id_cliente, estado +FROM pedidos +ORDER BY estado; + +-- 3. Que cliente tiene mas pedidos (ranking de actividad). +SELECT c.nombre_cliente, COUNT(*) AS total_pedidos +FROM clientes c +JOIN pedidos p ON p.id_cliente = c.id_cliente +GROUP BY c.id_cliente, c.nombre_cliente +ORDER BY total_pedidos DESC, c.nombre_cliente; + +-- 4. Pedidos ordenados por fecha y, como segundo criterio, por monto +-- pagado (de mayor a menor), usando la vista para tener ambas +-- columnas listas. +SELECT id_pedido, fecha_pedido, monto_pagado +FROM vista_pedidos_completos +ORDER BY fecha_pedido, monto_pagado DESC; + +-- 5. Reporte para decision de negocio: ingresos totales por +-- categoria de menu, para decidir en cual enfocar promociones +-- (GROUP BY + HAVING, usando la vista para no repetir el JOIN). +SELECT categoria, + SUM(monto_pagado) AS ingresos_totales +FROM vista_pedidos_completos +WHERE monto_pagado IS NOT NULL +GROUP BY categoria +HAVING SUM(monto_pagado) > 0 +ORDER BY ingresos_totales DESC; diff --git a/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/evidencias/resultados.md b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/evidencias/resultados.md new file mode 100644 index 00000000..e50d5ba6 --- /dev/null +++ b/resoluciones/maria-montepeque/solicitudes-sql/ejercicio-086/evidencias/resultados.md @@ -0,0 +1,63 @@ +# Evidencias - Solicitudes SQL - Ejercicio 086 (Delivery de Comida) + +## 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-086.db < ddl/schema.sql +sqlite3 ejercicio-086.db < dml/inserts.sql +sqlite3 ejercicio-086.db < dml/operaciones.sql +sqlite3 ejercicio-086.db < dql/consultas.sql +``` + +## Resultados + +Datos base tras `dml/inserts.sql`: 4 clientes, 5 platillos, 2 +repartidores, 5 pedidos (2 `entregado` con pago, 1 `en_camino`, 1 +marcado `entregado` por error con pago, 1 `recibido`) y 3 pagos. + +**Caso comentado verificado:** + +- `INSERT INTO pagos (id_pedido, ...) VALUES (1, ...);` (segundo pago para el pedido 1) → `UNIQUE constraint failed: pagos.id_pedido`. + +**1. Resumen completo via `vista_pedidos_completos` (ya con el pedido +4 cancelado y sin pago):** + +```text +id_pedido | nombre_cliente | nombre_platillo | categoria | nombre_repartidor | cantidad | estado | monto_pagado +1 | Manuel Estrada | Hamburguesa Clasica | comida | Hugo Marroquin | 2 | entregado | 90.0 +2 | Alejandra Chinchilla | Pizza Pepperoni | comida | Esteban Cifuentes | 1 | entregado | 60.0 +3 | Byron Xicay | Gaseosa | bebida | Hugo Marroquin | 3 | en_camino | (NULL) +4 | Cristina Barrios | Tacos al Pastor | comida | Esteban Cifuentes | 2 | cancelado | (NULL) +5 | Manuel Estrada | Flan de Caramelo | postre | Hugo Marroquin | 1 | recibido | (NULL) +``` + +**5. Ingresos totales por categoria de menu (para decidir en cual +enfocar promociones):** + +```text +categoria ingresos_totales +comida 150.0 +``` + +(Solo "comida" tiene pagos confirmados; bebida y postre todavia no +generaron ingresos reales.) + +## Operaciones de mantenimiento verificadas + +- `UPDATE pedidos SET estado = 'cancelado' WHERE id_pedido = 4 ...;` → el pedido de Cristina Barrios se corrigio despues de confirmarse la cancelacion. +- **DELETE controlado**: se elimino el pago que habia quedado invalido en el pedido 4, apenas se marco `cancelado`. Total de pagos: 3 -> 2. Ningun pago de un pedido `entregado` se toco. + +## Aprendizaje + +Separar catalogos (`clientes`, `menus`, `repartidores`), operacion +(`pedidos`) y resultado (`pagos`) en tablas distintas, tal como pidio +el cliente, hizo que el `UNIQUE (id_pedido)` en `pagos` fuera la unica +restriccion necesaria para evitar pagos duplicados: no hubo que +mezclar esa regla dentro de la tabla de pedidos. La vista +`vista_pedidos_completos`, con `LEFT JOIN` a pagos, mantiene esa +separacion visible en el reporte: un pedido sin pago sigue apareciendo +con `monto_pagado = NULL`, en vez de mezclarse o desaparecer.