LABS · JULIÁN LÓPEZ JIMÉNEZ · RECURSO GRATUITO
Tres errores de un informe de tienda: contactos ausentes, totales duplicados y clientes sin compras. SQLite real en el navegador, guía y práctica local sin cuenta ni tarjeta.
Qué necesitas: Desde cero: lee primero el esquema y las pistas. JavaScript/WebAssembly y conexión inicial. Para repetir fuera: SQLite 3 o DB Browser; no necesitas Python.
Tu resultado: Tres consultas explicadas que conservan ausencias y cuentan ventas sin duplicarlas.
Crea una base vacía con SQLite 3 o DB Browser y ejecuta este SQL una vez. Guarda una copia original. Activa PRAGMA foreign_keys=ON en cada conexión. Las consultas posteriores se ejecutan sobre esta base ficticia.
PRAGMA foreign_keys=ON;
CREATE TABLE clientes(id INTEGER PRIMARY KEY,nombre TEXT NOT NULL,ciudad TEXT,email TEXT UNIQUE);
CREATE TABLE productos(id INTEGER PRIMARY KEY,nombre TEXT NOT NULL,categoria TEXT NOT NULL,precio_cent INTEGER NOT NULL CHECK(precio_cent>=0));
CREATE TABLE pedidos(id INTEGER PRIMARY KEY,cliente_id INTEGER NOT NULL REFERENCES clientes(id),fecha TEXT NOT NULL,estado TEXT NOT NULL CHECK(estado IN ('pagado','pendiente','cancelado')));
CREATE TABLE lineas(pedido_id INTEGER NOT NULL REFERENCES pedidos(id),producto_id INTEGER NOT NULL REFERENCES productos(id),cantidad INTEGER NOT NULL CHECK(cantidad>0),precio_cent INTEGER NOT NULL CHECK(precio_cent>=0),PRIMARY KEY(pedido_id,producto_id));
INSERT INTO clientes VALUES(1,'Ana','Sevilla','ana@example.invalid'),(2,'Bruno','Cádiz',NULL),(3,'Carla',NULL,'carla@example.invalid'),(4,'Diego','Sevilla','diego@example.invalid'),(5,'Elena','Cádiz',NULL);
INSERT INTO productos VALUES(1,'Teclado','perifericos',1500),(2,'Ratón','perifericos',2200),(3,'Manual','libros',900),(4,'Cable','cables',700),(5,'Pantalla','pantallas',25000);
INSERT INTO pedidos VALUES(10,1,'2026-01-03','pagado'),(11,1,'2026-01-20','pendiente'),(12,2,'2026-02-01','pagado'),(13,3,'2026-02-28','cancelado'),(14,2,'2026-03-01','pagado'),(15,4,'2026-02-10','pagado');
INSERT INTO lineas VALUES(10,1,2,1000),(10,2,1,2000),(11,3,2,900),(12,2,2,2000),(12,3,1,900),(13,1,1,1000),(14,4,3,700),(15,3,1,900);
No uses tus datos reales.
SELECT COUNT(*) AS clientes FROM clientes;La base de ejemplo tiene cinco clientes; no todos tienen email ni pedidos.
Comprueba: Crea otra base vacía para repetir.
NULL representa un valor desconocido o ausente. Compararlo con = produce desconocido, no verdadero; usa IS NULL o IS NOT NULL. Una cadena vacía es un valor distinto de NULL. El recurso gratuito retoma este problema en un informe de contacto. Devuelve id y nombre de clientes sin email, por id. Incluye NULL, no inventes correos.
-- Escribe tu SQL aquí.
La condición se refiere a email. → No uses = NULL. → WHERE email IS NULL selecciona ausencias.
SELECT id,nombre FROM clientes WHERE email IS NULL ORDER BY id;No rellenar un dato ficticio conserva la diferencia entre desconocido y conocido.
Comprueba: Añade en una copia un cliente con email='' y compara ambos filtros.
Cada pedido se repite por sus líneas al unir pedidos y lineas. Para contar pedidos usa COUNT(DISTINCT p.id); para facturar suma cada importe de línea. SUM(DISTINCT importe) no arregla duplicados: dos ventas distintas pueden costar lo mismo. Las variantes contienen importes iguales adrede. Devuelve una fila pedidos y total_cent de pedidos pagados; cuenta cada pedido una vez y suma todas sus líneas. Cero si no hay ventas.
-- Escribe tu SQL aquí.
El grano de la unión es una línea. → Cuenta DISTINCT p.id, pero no importes DISTINCT. → SUM(l.cantidad*l.precio_cent) conserva ventas distintas de igual precio.
SELECT COUNT(DISTINCT p.id) AS pedidos,COALESCE(SUM(l.cantidad*l.precio_cent),0) AS total_cent FROM pedidos p JOIN lineas l ON l.pedido_id=p.id WHERE p.estado='pagado';COUNT(DISTINCT) corrige una identidad repetida; no es un permiso para eliminar importes que coinciden.
Comprueba: Compara COUNT(*) y COUNT(DISTINCT p.id). El contrato de esta base incluye al menos una línea por pedido cargado.
La consulta SELECT c.nombre,COUNT(*),SUM(DISTINCT l.precio_cent) mezcla personas con igual nombre, cuenta líneas como pedidos, pierde cantidades y elimina importes legítimos. El informe debe declarar población, grano, estado, unidades y orden. Corregir SQL empieza por acordar ese contrato. Devuelve id,nombre,pedidos,total_cent para todos los clientes. Solo ventas pagadas; incluye cero. Cuenta pedidos distintos, suma cantidad*precio_cent de líneas y ordena c.id.
-- Escribe tu SQL aquí.
Empieza por toda la población clientes. → El filtro de pagado pertenece al ON. → COUNT(DISTINCT p.id) y SUM de líneas responden a granos diferentes.
SELECT c.id,c.nombre,COUNT(DISTINCT p.id) AS pedidos,COALESCE(SUM(l.cantidad*l.precio_cent),0) AS total_cent FROM clientes c LEFT JOIN pedidos p ON p.cliente_id=c.id AND p.estado='pagado' LEFT JOIN lineas l ON l.pedido_id=p.id GROUP BY c.id,c.nombre ORDER BY c.id;Agrupar por identidad conserva a dos personas del mismo nombre; el LEFT JOIN conserva a quien no compró.
Comprueba: Crea dos clientes llamados Ana; explica por qué no deben fusionarse.
Prepara un informe de contactos conocidos: id,email de clientes cuyo email no sea NULL, por id. Predice primero quién quedará fuera.
SELECT id,email FROM clientes WHERE email IS NOT NULL ORDER BY id;IS NOT NULL conserva contactos conocidos. No sustituye los ausentes por direcciones inventadas. Una cadena vacía sigue siendo texto: prueba ese caso en una copia.
Repite con datos distintos antes de marcarlo como practicado. Esta es una autoevaluación, no una certificación.
Aprende desde cero a consultar, relacionar y modificar copias con garantías; entrega un informe con evidencia.
Ver el curso De cero a SQL y su temarioFuente: Recurso original de Labs. Puedes guardar este HTML, abrirlo sin conexión o imprimirlo como PDF desde tu navegador. No guarda tus respuestas ni el progreso de tu cuenta.