Ejercicios de SQL en SQLite
Ejercicio 1: Sistema de Matrículas (Relación M:N)
Contexto
La institución desea registrar alumnos, cursos y las matrículas de cada alumno en los cursos.
Actualmente no existe un sistema que evite duplicados ni permita responder preguntas como
“¿cuántos alumnos tiene cada curso?” o “¿qué cursos no tienen inscriptos?”.
Objetivos de aprendizaje
Modelar una relación muchos-a-muchos (M:N) con una tabla puente.
Aplicar PRIMARY KEY, UNIQUE, CHECK y FOREIGN KEY en SQLite.
Practicar JOIN, GROUP BY, HAVING y consultas frecuentes de análisis.
Requisitos técnicos
Motor: SQLite (SQLiteStudio, DB Browser for SQLite o sqlite3).
Activar claves foráneas: PRAGMA foreign_keys=ON;
Formato de fechas recomendado: YYYY-MM-DD.
Modelo de datos (mínimo)
Alumnos(id INTEGER PK, nombre TEXT NOT NULL, documento TEXT UNIQUE, fecha_nac
TEXT)
Cursos(id INTEGER PK, nombre TEXT NOT NULL, horas INTEGER CHECK(horas >= 10), cupo
INTEGER CHECK(cupo >= 0))
Matriculas(id INTEGER PK, alumno_id INTEGER FK→Alumnos(id), curso_id INTEGER
FK→Cursos(id), fecha TEXT DEFAULT date('now'), UNIQUE(alumno_id, curso_id))
Consignas (paso a paso)
1. Crear las tres tablas con las restricciones indicadas.
2. Insertar al menos 4 alumnos y 3 cursos (puedes usar los datos semilla).
3. Registrar al menos 5 matrículas que evidencien cursos con diferente demanda.
4. Escribir consultas SQL que respondan: a) Listado de alumnos con sus cursos y la fecha de
matrícula (ordenado por alumno, curso). b) Ranking: cantidad de alumnos por curso (desc).
c) Cursos sin alumnos (LEFT JOIN o subconsulta). d) Alumnos no duplicados por documento
(demostrar que documento evita duplicados). e) Top-1 curso por cantidad de alumnos
(resolver con LIMIT 1).
5. Crear una vista llamada vw_matriculas que muestre: alumno, curso, fecha, y un campo
calculado antiguedad_dias (julianday('now') - julianday(fecha)).
Datos semilla sugeridos
Alumnos
(1, 'Ana López', '5551234', '2007-05-12')
(2, 'Carlos Ruiz', '5559876', '2006-11-03')
(3, 'María Pérez', '5553333', '2008-01-25')
(4, 'Jorge Gómez', '5557777', '2007-09-09')
Cursos
(101, 'Programación I', 120, 35)
(102, 'Bases de Datos', 90, 30)
(103, 'Redes I', 100, 25)
Matrículas
(1,101), (2,101), (3,102), (1,102), (4,101)
Extensiones (opcional, suma puntos)
Agregar trigger que impida matricular si el cupo del curso está completo.
Agregar CHECK para que la fecha de matrícula no sea futura.
Reporte por cohortes (agrupar por año de fecha_nac).
Entregables
Archivo: ej1_matriculas_apellido_nombre.sql (script reproducible).
Export de resultados en CSV o capturas de pantalla de las consultas (a–e) y de la vista.
Rúbrica (100%)
Criterio Peso
Modelo y restricciones (PK, FK, UNIQUE, 30%
CHECK)
Población de datos coherente (no romper 15%
restricciones)
Consultas (a–e) correctas y ordenadas 35%
Vista vw_matriculas con cálculo correcto 10%
Presentación/entregables (nombres, claridad, 10%
reproducibilidad)
Ejercicio 2: Cantina Escolar (Inventario, Ventas y Triggers)
Contexto
La cantina necesita controlar stock, ventas y detalles de venta. Debe evitar vender por encima
del stock disponible, calcular totales y generar reportes rápidos (productos más vendidos, bajo
stock, etc.).
Objetivos de aprendizaje
Diseñar un flujo de ventas con tablas relacionadas.
Implementar triggers para reglas de negocio (control de stock y actualización automática).
Practicar JOIN, agrupaciones, vistas y reportes.
Requisitos técnicos
Motor: SQLite con PRAGMA foreign_keys=ON;
Triggers BEFORE INSERT y AFTER INSERT en VentasDetalle.
Modelo de datos (mínimo)
Productos(id INTEGER PK, nombre TEXT NOT NULL, precio REAL CHECK(precio>0), stock
INTEGER CHECK(stock>=0))
Ventas(id INTEGER PK, fecha TEXT DEFAULT datetime('now'))
VentasDetalle(id INTEGER PK, venta_id INTEGER FK→Ventas(id), producto_id INTEGER
FK→Productos(id), cantidad INTEGER CHECK(cantidad>0), precio_unitario REAL
CHECK(precio_unitario>0))
Reglas de negocio (obligatorias)
Trigger 1 (BEFORE INSERT): abortar si cantidad > stock del producto.
Trigger 2 (AFTER INSERT): descontar del stock la cantidad vendida.
El precio_unitario del detalle debe conservar el precio al momento de la venta (no recalcular
si cambia luego en Productos).
Consignas (paso a paso)
6. Crear las tres tablas con restricciones.
7. Crear los triggers de control de stock y descuento automático.
8. Insertar al menos 4 productos con stock y precio.
9. Registrar 1 venta con 3 ítems usando subconsultas para capturar el precio_unitario vigente.
10. Escribir consultas SQL que respondan: a) Detalle de la venta 1 con importe por ítem. b) Total
de la venta 1. c) Top 3 productos más vendidos. d) Bajo stock (stock < 5). e) Ventas por día.
11. Crear la vista vw_detalle_ventas (venta, fecha, producto, cantidad, precio_unitario,
importe).
Datos semilla sugeridos
Productos
(1,'Sándwich',10.0,20)
(2,'Jugo 300ml',6.5,50)
(3,'Galletitas',4.0,100)
(4,'Empanada',7.5,15)
Venta 1 / Detalle
Venta: id=1
Detalle: (1,1,2,precio de [Link]=1), (1,2,1,precio id=2), (1,3,3,precio id=3)
Extensiones (opcional, suma puntos)
Trigger para impedir precios unitarios negativos o 0.
Trigger para evitar detalle con producto o venta inexistente (además de FK).
Agregar descuentos por producto o venta (campo descuento y cálculo de total con
descuento).
Entregables
Archivo: ej2_cantina_apellido_nombre.sql (script reproducible).
Export de resultados en CSV o capturas de las consultas (a–e) y de la vista.
Rúbrica (100%)
Criterio Peso
Modelo y restricciones correctas 25%
Triggers (dos) funcionando y probados 35%
Consultas (a–e) correctas 25%
Vista vw_detalle_ventas correcta 10%
Presentación/entregables 5%
Notas generales para ambos ejercicios
Ejecutar al inicio: PRAGMA foreign_keys=ON;
Incluir al comienzo DROP TABLE IF EXISTS (y DROP TRIGGER IF EXISTS) para facilitar la re-
ejecución.
Comentar el script para que el docente pueda corregir rápido.
Mantener nombres de archivo y formato de entrega.