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
83 changes: 83 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-77/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Ejercicio 77: DELETE Nivel Basico

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

## Tema central

DELETE

## Descripcion del problema

Una bodega de dispositivos tecnologicos necesita corregir errores de
captura en su historial de movimientos, y tambien dar de baja
productos descontinuados sin perder el historial de lo que ya se
vendio o recibio de ellos. Este ejercicio compara ambos casos: cuando
`DELETE` fisico es seguro, y cuando conviene una baja logica en su
lugar.

## Tablas y relaciones

- `categorias`: catalogo de categorias de producto.
- `productos`: catalogo de productos, con una bandera `activo` para
la baja logica.
- `movimientos`: historial de entradas y salidas de bodega.
`categorias` 1—N `productos`; `productos` 1—N `movimientos`.

## Uso de DELETE

En `dml/inserts.sql`:

1. `DELETE` real (baja fisica): un movimiento de Mouse Inalambrico se
cargo dos veces por error de digitacion. Como `movimientos` no
tiene dependientes, es seguro eliminar de verdad la fila duplicada
con `WHERE id_movimiento = 6`.
2. Baja logica (sin `DELETE`): Teclado Mecanico se descontinua, pero
ya tiene un movimiento asociado por `FOREIGN KEY`. En vez de
intentar borrarlo, se marca `activo = 0` con `UPDATE`, conservando
el historial.

La consulta 5 en `dql/consultas.sql` confirma que el movimiento
duplicado ya no existe, y que el resto de movimientos del mismo
producto sigue intacto.

## Otras restricciones aplicadas

- `PRIMARY KEY` autoincremental en las 3 tablas.
- `FOREIGN KEY`: `productos.id_categoria`, `movimientos.id_producto`.
- `NOT NULL` en todas las columnas obligatorias.
- `UNIQUE`: `categorias.nombre_categoria`, `productos.nombre_producto`.
- `CHECK`: `productos.precio_unitario >= 0`, `productos.activo IN (0, 1)`,
`movimientos.tipo_movimiento IN (...)`, `movimientos.cantidad > 0`.
- `DEFAULT` en `productos.activo`, `movimientos.tipo_movimiento` y
`fecha_movimiento`.
- `PRAGMA foreign_keys = ON;` activado al inicio del script.

## Caso que falla / no recomendable (comentado en `dml/inserts.sql`)

`DELETE FROM productos WHERE id_producto = 4;` falla porque Teclado
Mecanico todavia tiene un movimiento en `movimientos` que depende de
el por `FOREIGN KEY`. Se valido con Python (`sqlite3`): lanza
`FOREIGN KEY constraint failed`. Esto es exactamente lo que justifica
usar baja logica (`UPDATE activo = 0`) en vez de `DELETE` para
productos que ya tienen historial.

## 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: 5 movimientos (sin el duplicado), 4 productos
activos y 1 inactivo (Teclado Mecanico, dado de baja logica).

## Como ejecutar

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

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

-- Ejercicio 77: DELETE Nivel Basico
-- Tema central: DELETE
-- Contexto: inventario de dispositivos tecnologicos en bodega.

CREATE TABLE categorias (
id_categoria INTEGER PRIMARY KEY AUTOINCREMENT,
nombre_categoria TEXT NOT NULL UNIQUE
);

-- productos: "activo" es la bandera de baja logica. Un producto
-- descontinuado no se borra (movimientos todavia lo referencia por
-- FOREIGN KEY), se marca como inactivo.
CREATE TABLE productos (
id_producto INTEGER PRIMARY KEY AUTOINCREMENT,
nombre_producto TEXT NOT NULL UNIQUE,
id_categoria INTEGER NOT NULL,
precio_unitario REAL NOT NULL CHECK (precio_unitario >= 0),
activo INTEGER NOT NULL DEFAULT 1 CHECK (activo IN (0, 1)),

FOREIGN KEY (id_categoria) REFERENCES categorias (id_categoria)
);

-- movimientos: historial de entradas y salidas. A diferencia de
-- productos, un movimiento sin dependientes si puede eliminarse de
-- verdad cuando es un error de captura (ver dml/inserts.sql).
CREATE TABLE movimientos (
id_movimiento INTEGER PRIMARY KEY AUTOINCREMENT,
id_producto INTEGER NOT NULL,
tipo_movimiento TEXT NOT NULL DEFAULT 'entrada'
CHECK (tipo_movimiento IN ('entrada', 'salida')),
cantidad INTEGER NOT NULL CHECK (cantidad > 0),
fecha_movimiento TEXT NOT NULL DEFAULT (datetime('now')),

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

-- Ejercicio 77: DELETE Nivel Basico
-- Datos de prueba y DELETE de validacion.

INSERT INTO categorias (nombre_categoria) VALUES
('Laptops'),
('Perifericos'),
('Almacenamiento');

INSERT INTO productos (nombre_producto, id_categoria, precio_unitario) VALUES
('Laptop Pro 14', 1, 8500.00),
('Laptop Air 13', 1, 6200.00),
('Mouse Inalambrico', 2, 150.00),
('Teclado Mecanico', 2, 320.00),
('Disco SSD 1TB', 3, 480.00);

INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES
(1, 'entrada', 10),
(2, 'entrada', 8),
(3, 'entrada', 50),
(4, 'entrada', 30),
(5, 'entrada', 20);

-- Movimiento cargado dos veces por error de digitacion (misma entrada
-- de Mouse Inalambrico registrada dos veces).
INSERT INTO movimientos (id_producto, tipo_movimiento, cantidad) VALUES
(3, 'entrada', 50);

-- 1. DELETE real (baja fisica): el movimiento duplicado no tiene
-- ningun dependiente y es un error de captura, asi que se elimina de
-- verdad, con WHERE por id especifico.
DELETE FROM movimientos
WHERE id_movimiento = 6;

-- 2. Baja logica (no DELETE): Teclado Mecanico se descontinua, pero
-- no se puede borrar de verdad porque movimientos todavia lo
-- referencia por FOREIGN KEY. En vez de eso, se marca como inactivo.
UPDATE productos
SET activo = 0
WHERE id_producto = 4;

-- Caso comentado que debe fallar (no ser recomendable), dejar
-- comentado: intentar el DELETE fisico de un producto que todavia
-- tiene movimientos asociados. SQLite, con
-- PRAGMA foreign_keys = ON, no lo permite. Esto es justo lo que
-- justifica usar baja logica (UPDATE activo = 0) en vez de DELETE
-- para productos.
-- DELETE FROM productos WHERE id_producto = 4;
44 changes: 44 additions & 0 deletions resoluciones/maria-montepeque/ejercicio-77/dql/consultas.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.headers on
.mode column

-- Ejercicio 77: DELETE Nivel Basico
-- Consultas de validacion.

-- 1. Mostrar todos los datos principales (movimientos con producto y
-- categoria).
SELECT m.id_movimiento,
p.nombre_producto,
c.nombre_categoria,
m.tipo_movimiento,
m.cantidad
FROM movimientos m
JOIN productos p ON p.id_producto = m.id_producto
JOIN categorias c ON c.id_categoria = p.id_categoria;

-- 2. Consulta con WHERE: solo los productos activos.
SELECT id_producto, nombre_producto, activo
FROM productos
WHERE activo = 1;

-- 3. Consulta con ORDER BY: movimientos ordenados por fecha.
SELECT id_movimiento, fecha_movimiento, cantidad
FROM movimientos
ORDER BY fecha_movimiento;

-- 4. Conteo o resumen: total de movimientos por producto.
SELECT id_producto, COUNT(*) AS total_movimientos
FROM movimientos
GROUP BY id_producto;

-- 5. Validacion especifica de DELETE: el movimiento duplicado
-- (id_movimiento = 6) ya no existe, pero el resto de movimientos de
-- Mouse Inalambrico (id_producto = 3) sigue intacto.
SELECT id_movimiento
FROM movimientos
WHERE id_movimiento = 6;
-- Debe devolver 0 filas: el duplicado se elimino con DELETE.

SELECT COUNT(*) AS movimientos_mouse
FROM movimientos
WHERE id_producto = 3;
-- Debe devolver 1: solo queda el movimiento real, no el duplicado.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Evidencias - Ejercicio 77

## Tema

DELETE

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

## Resultados

Estado final tras `dml/inserts.sql` (que incluye el `DELETE` real y la
baja logica de validacion):

```text
movimientos (5 filas, ya sin el duplicado):
id_movimiento | id_producto | tipo_movimiento | cantidad
1 | 1 | entrada | 10
2 | 2 | entrada | 8
3 | 3 | entrada | 50
4 | 4 | entrada | 30
5 | 5 | entrada | 20

productos:
id_producto | nombre_producto | activo
1 | Laptop Pro 14 | 1
2 | Laptop Air 13 | 1
3 | Mouse Inalambrico | 1
4 | Teclado Mecanico | 0
5 | Disco SSD 1TB | 1
```

**Caso comentado verificado:**

- `DELETE FROM productos WHERE id_producto = 4;` → `FOREIGN KEY constraint failed` (Teclado Mecanico todavia tiene un movimiento asociado).

**5. Validacion especifica de DELETE:**

```text
5a. Movimiento id_movimiento = 6: 0 filas -- el duplicado se elimino.
5b. Movimientos de Mouse Inalambrico (id_producto = 3): 1 -- solo el
movimiento real, no el duplicado que se borro.
```

## Aprendizaje

`DELETE` con `WHERE` por id especifico elimina exactamente la fila
equivocada sin arriesgar el resto de movimientos del mismo producto.
Pero `DELETE` no siempre es la herramienta correcta: cuando otras
filas dependen de un registro por `FOREIGN KEY` (como los movimientos
de Teclado Mecanico), SQLite rechaza el `DELETE` fisico, tal como se
confirmo con el caso comentado. Ahi es donde tiene sentido la baja
logica: en vez de borrar el producto, se marca `activo = 0` con
`UPDATE`, conservando el historial de movimientos intacto mientras el
producto deja de aparecer como disponible.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Solicitud SQL - Ejercicio 077: Taller de Motos

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

## Solicitud del cliente

Un taller de motos recibe servicios, repuestos y mecanicos por orden
de trabajo. El cliente pide que el sistema permita corregir estados
sin borrar informacion importante. 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 sobre cuando SI y cuando NO usar `DELETE`: los
estados de una orden se corrigen siempre con `UPDATE`, y un repuesto
solo se puede quitar de una orden mientras esta sigue `recibida`
(antes de que sea parte del historial oficial del trabajo). El nivel
pedido (4, reportes y agrupaciones) exige ademas `JOIN`, `GROUP BY`,
`HAVING`, totales y ranking. El detalle completo del analisis esta en
[analisis/requerimiento.md](analisis/requerimiento.md).

## Que tablas cree y por que

- `clientes`: catalogo de duenos de motos.
- `motos`: catalogo de motos, cada una de un cliente.
- `ordenes_servicio`: tabla transaccional, cada trabajo sobre una
moto. Su `estado` es el dato que mas cambia y siempre se corrige con
`UPDATE`.
- `repuestos`: catalogo de repuestos disponibles.
- `detalle_repuestos`: detalle de cada orden. Aqui esta el
`UNIQUE (id_orden, id_repuesto)` que impide registrar el mismo
repuesto dos veces en la misma orden.

## Como se relacionan

`clientes` 1:N `motos`; `motos` 1:N `ordenes_servicio`;
`ordenes_servicio` 1:N `detalle_repuestos`; `repuestos` 1:N
`detalle_repuestos`. El diagrama esta en
[diagramas/diagrama-er.svg](diagramas/diagrama-er.svg).

## Que datos de prueba use

3 clientes, 3 motos, 5 repuestos, 4 ordenes (2 `finalizada`, 1
`en_reparacion`, 1 `recibida`) y 7 lineas de detalle, incluida una
linea cargada por error en una orden que todavia estaba `recibida`.
Tambien un `INSERT` comentado que reproduce el problema de duplicar
un repuesto en la misma orden 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 el repuesto agregado por error (solo posible porque esa
orden seguia `recibida`) y un `UPDATE` de estado (la orden pasa a
`en_reparacion` una vez que el mecanico empieza a trabajar).

## Que consultas responden al cliente

En [dql/consultas.sql](dql/consultas.sql): que lineas de repuestos
existen (JOIN repuesto-orden-moto), en que estado esta cada orden, que
moto tiene mas ordenes de servicio, las lineas ordenadas por subtotal,
y un reporte con `GROUP BY` + `HAVING` de los repuestos mas usados,
para decidir cuales mantener siempre en stock.

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

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