Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-83/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Ejercicio 83: WHERE Nivel Basico

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

## Tema central

WHERE

## Descripcion del problema

Una cafeteria necesita filtrar sus ventas de distintas formas: por
nombre de producto, por rango de precio, por fecha y combinando varias
condiciones a la vez, para poder responder preguntas puntuales sin
revisar todos los registros a mano.

## Tablas y relaciones

- `clientes`: catalogo de clientes.
- `productos`: catalogo de productos, con precio y categoria.
- `ventas`: relaciona un cliente con un producto en una fecha.
`clientes` 1—N `ventas`; `productos` 1—N `ventas`.

## Uso de WHERE

En `dql/consultas.sql`:

1. Filtro por texto: `WHERE nombre_producto LIKE 'Ca%'` encuentra los
productos cuyo nombre empieza con "Ca" (Cafe Americano,
Cappuccino), usando el comodin `%`.
2. Filtro por numero: `WHERE precio BETWEEN 12 AND 18` selecciona
productos de rango de precio medio.
3. Filtro por fecha simulada: `WHERE fecha_venta >= '2026-08-02'`
compara fechas guardadas como texto en formato ISO, que ordenan
igual que fechas reales.
4. Operadores logicos combinados: la consulta 5 junta `BETWEEN`,
comparacion de fecha e `IN` con `AND`, de forma que una fila solo
aparece si cumple las tres condiciones a la vez.

## Otras restricciones aplicadas

- `PRIMARY KEY` autoincremental en las 3 tablas.
- `FOREIGN KEY`: `ventas.id_cliente`, `ventas.id_producto`.
- `NOT NULL` en todas las columnas obligatorias.
- `UNIQUE`: `clientes.telefono`, `productos.nombre_producto`.
- `CHECK`: `productos.precio >= 0`, `productos.categoria IN (...)`,
`ventas.cantidad > 0`.
- `DEFAULT` en `ventas.fecha_venta`.
- `PRAGMA foreign_keys = ON;` activado al inicio del script.

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

`SELECT * FROM productos WHERE preci > 15;` falla porque `preci` no
es el nombre real de la columna (falta la "o" de `precio`). Se valido
con Python (`sqlite3`): lanza `no such column: preci`.

## 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 clientes, 5 productos, 6 ventas. El filtro
combinado de la consulta 5 deja una sola venta que cumple las tres
condiciones a la vez.

## Como ejecutar

```bash
sqlite3 ejercicio-83.db < ddl/schema.sql
sqlite3 ejercicio-83.db < dml/inserts.sql
sqlite3 ejercicio-83.db < dql/consultas.sql
```

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

-- Ejercicio 83: WHERE Nivel Basico
-- Tema central: WHERE
-- Contexto: ventas diarias de una cafeteria.

CREATE TABLE clientes (
id_cliente INTEGER PRIMARY KEY AUTOINCREMENT,
nombre_cliente TEXT NOT NULL,
telefono TEXT NOT NULL UNIQUE
);

CREATE TABLE productos (
id_producto INTEGER PRIMARY KEY AUTOINCREMENT,
nombre_producto TEXT NOT NULL UNIQUE,
precio REAL NOT NULL CHECK (precio >= 0),
categoria TEXT NOT NULL CHECK (categoria IN ('bebida', 'comida'))
);

CREATE TABLE ventas (
id_venta INTEGER PRIMARY KEY AUTOINCREMENT,
id_cliente INTEGER NOT NULL,
id_producto INTEGER NOT NULL,
cantidad INTEGER NOT NULL CHECK (cantidad > 0),
fecha_venta TEXT NOT NULL DEFAULT (date('now')),

FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente),
FOREIGN KEY (id_producto) REFERENCES productos (id_producto)
);
24 changes: 24 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-83/dml/inserts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
PRAGMA foreign_keys = ON;

-- Ejercicio 83: WHERE Nivel Basico
-- Datos de prueba.

INSERT INTO clientes (nombre_cliente, telefono) VALUES
('Manuel Estrada', '5555-9001'),
('Alejandra Chinchilla', '5555-9002'),
('Byron Xicay', '5555-9003');

INSERT INTO productos (nombre_producto, precio, categoria) VALUES
('Cafe Americano', 15.00, 'bebida'),
('Cappuccino', 20.00, 'bebida'),
('Te Chai', 12.00, 'bebida'),
('Croissant', 18.00, 'comida'),
('Sandwich Jamon', 35.00, 'comida');

INSERT INTO ventas (id_cliente, id_producto, cantidad, fecha_venta) VALUES
(1, 1, 2, '2026-08-01'),
(2, 2, 1, '2026-08-01'),
(3, 3, 3, '2026-08-02'),
(1, 4, 1, '2026-08-02'),
(2, 5, 1, '2026-08-03'),
(3, 1, 2, '2026-08-03');
47 changes: 47 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-83/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.headers on
.mode column

-- Ejercicio 83: WHERE Nivel Basico
-- Consultas de validacion.

-- 1. Mostrar todos los datos principales.
SELECT v.id_venta, c.nombre_cliente, p.nombre_producto, v.cantidad, v.fecha_venta
FROM ventas v
JOIN clientes c ON c.id_cliente = v.id_cliente
JOIN productos p ON p.id_producto = v.id_producto;

-- 2. WHERE por texto: productos cuyo nombre empieza con "Ca"
-- (Cafe Americano, Cappuccino), usando LIKE con comodin.
SELECT nombre_producto, precio
FROM productos
WHERE nombre_producto LIKE 'Ca%';

-- 3. Consulta con ORDER BY: ventas ordenadas por fecha.
SELECT id_venta, fecha_venta
FROM ventas
ORDER BY fecha_venta;

-- 4. Conteo o resumen: ventas por categoria de producto.
SELECT p.categoria, COUNT(*) AS total_ventas
FROM ventas v
JOIN productos p ON p.id_producto = v.id_producto
GROUP BY p.categoria;

-- 5. Validacion especifica de WHERE: filtro por numero (BETWEEN),
-- por fecha (comparacion de texto en formato ISO) y con operadores
-- logicos combinados (AND, IN), todo en la misma consulta.
-- - precio BETWEEN 12 AND 18: solo productos de rango medio.
-- - fecha_venta >= '2026-08-02': solo ventas desde esa fecha.
-- - id_cliente IN (1, 2): solo esos dos clientes.
SELECT v.id_venta, c.nombre_cliente, p.nombre_producto, p.precio, v.fecha_venta
FROM ventas v
JOIN clientes c ON c.id_cliente = v.id_cliente
JOIN productos p ON p.id_producto = v.id_producto
WHERE p.precio BETWEEN 12 AND 18
AND v.fecha_venta >= '2026-08-02'
AND v.id_cliente IN (1, 2);

-- Caso comentado que debe fallar (no ser recomendable), dejar
-- comentado: escribir mal el nombre de una columna en el WHERE
-- (typo: "preci" en vez de "precio").
-- SELECT * FROM productos WHERE preci > 15;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Evidencias - Ejercicio 83

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

## Resultados

**2. Productos cuyo nombre empieza con "Ca" (`LIKE 'Ca%'`):**

```text
nombre_producto precio
Cafe Americano 15.0
Cappuccino 20.0
```

**Caso comentado verificado:**

- `SELECT * FROM productos WHERE preci > 15;` → `no such column: preci` (falta la "o" en `precio`).

**5. Filtro combinado (numero + fecha + operador logico IN):**

```text
id_venta | nombre_cliente | nombre_producto | precio | fecha_venta
4 | Manuel Estrada | Croissant | 18.0 | 2026-08-02
```

Solo la venta 4 cumple las tres condiciones a la vez: producto con
precio entre 12 y 18 (Croissant, 18.00), fecha desde el 2026-08-02 en
adelante, y cliente entre los ids 1 o 2 (Manuel Estrada). Las demas
ventas quedan fuera porque fallan al menos una condicion (por ejemplo,
la venta 2 es de Cappuccino a 20.00, fuera del rango de precio).

## Aprendizaje

`WHERE` filtra con distintos tipos de dato y distintos operadores en
una sola condicion: texto con `LIKE` y comodines (`%`), numeros con
`BETWEEN`, fechas simuladas como texto ISO comparadas con `>=`
(funciona porque `'2026-08-02'` ordena igual como texto que como
fecha), y varias condiciones combinadas con `AND` e `IN`. Cuando se
combinan varias condiciones con `AND`, una fila solo aparece en el
resultado si cumple todas a la vez, lo que reduce el resultado a un
subconjunto muy especifico. El caso comentado recuerda que un error de
escritura en el nombre de una columna dentro de `WHERE` tambien hace
fallar la consulta completa.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Solicitud SQL - Ejercicio 083: Viajes y Paracaidismo

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

## Solicitud del cliente

Una agencia vende experiencias de viaje, turismo y saltos en
paracaidas. El cliente quiere evitar registros incompletos porque
despues no puede hacer reportes confiables. 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

El problema no es "guardar reservas", es garantizar que ninguna
reserva quede duplicada y que el estado de sus pagos siempre sea
visible y confiable, incluso cuando falta informacion (una reserva sin
pago todavia). 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`: catalogo de clientes.
- `experiencias`: catalogo de experiencias disponibles.
- `instructores`: catalogo de instructores.
- `reservas`: tabla transaccional. `UNIQUE (id_cliente, id_experiencia,
fecha_reserva)` impide registrar la misma reserva dos veces.
- `pagos`: resultado de una reserva. `UNIQUE (id_reserva)` garantiza
un solo pago oficial por reserva.

## Vista SQL

`vista_resumen_reservas` (definida en
[ddl/schema.sql](ddl/schema.sql)) junta reserva, cliente, experiencia,
instructor y pago con `LEFT JOIN`. Esto ataca directamente el problema
de "reportes no confiables": una reserva sin pago no desaparece del
reporte, aparece con `monto_pagado = NULL`, visible y explicito.

## Como se relacionan

`clientes` 1:N `reservas`; `experiencias` 1:N `reservas`;
`instructores` 1:N `reservas`; `reservas` 1:1 `pagos`. El diagrama
esta en [diagramas/diagrama-er.svg](diagramas/diagrama-er.svg).

## Que datos de prueba use

4 clientes, 3 experiencias, 2 instructores, 4 reservas (2 `realizada`
con pago, 1 `confirmada` sin pago, 1 marcada `realizada` por error con
un pago que se corrige despues) y 3 pagos. Tambien un `INSERT`
comentado que reproduce el problema de duplicar una reserva 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 reserva que se marco realizada por error pasa a `cancelada`) y un
`DELETE` controlado que elimina el pago invalido de esa reserva, sin
tocar ningun pago de una reserva ya `realizada`.

## Que consultas responden al cliente

En [dql/consultas.sql](dql/consultas.sql): el resumen completo de
reservas usando la vista, en que estado esta cada reserva, que cliente
tiene mas reservas, las reservas ordenadas por fecha, y un reporte con
`GROUP BY` + `HAVING` (tambien sobre la vista) de ingresos totales por
tipo de experiencia, para decidir en cual invertir mas promocion.

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

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