Curso de programación GRATIS Modulo IV:SQL, MySQL, CRUD, Relaciones, Joins Y Proyecto Final: Sistema de clientes.
DB · M4 · Bases de Datos
0%
Módulo 4 · Bases de Datos
✅ Módulos anteriores
🚀 Proyecto del módulo
Sistema de Clientes (CRM)
Completa L12–L16 para desbloquearlo
Módulo 4 · Bases de Datos · SQL & MySQL · 2026
Bases de Datos: organiza, consulta y conecta tu información
Los datos son el corazón de toda aplicación real. Aprenderás SQL — el lenguaje universal para hablar con bases de datos —, a diseñar tablas con MySQL, a hacer operaciones CRUD completas, a modelar relaciones entre entidades y a combinar tablas con JOINs potentes. Al final, construirás un sistema de gestión de clientes funcional.
5Lecciones
1Proyecto CRM
5Mini quizzes
∞Consultas SQL
🔖 Marcadores
Sin marcadores — usa el botón dorado en cualquier lección.
L12
Fundamentos
SQL: el lenguaje que hablan las bases de datos
SQL (Structured Query Language) es el lenguaje estándar para crear, consultar y manipular bases de datos relacionales. Lo usan MySQL, PostgreSQL, SQLite, SQL Server y prácticamente cualquier sistema de bases de datos del mercado. Aprenderlo una vez te sirve en todos ellos.
📊
Analogía: una base de datos es como una hoja de cálculo con superpoderes
Imagina Excel: tienes hojas (tablas), filas (registros) y columnas (campos). Una base de datos relacional es exactamente eso, pero con tres ventajas clave: puede manejar millones de filas sin perder velocidad, puede conectar unas hojas con otras (relaciones), y puedes hacerle preguntas precisas con SQL — "dame todos los clientes de México que compraron más de $1000 en los últimos 30 días" — y responde en milisegundos.
Conceptos clave: tabla, fila, columna
🗂 clientes
id_cliente (PK)nombreemailciudadactivo
1Ana Garcíaana@ejemplo.comCiudad de México1
2Luis Pérezluis@ejemplo.comGuadalajara1
3María Lópezmaria@ejemplo.comMonterrey0
Término
En SQL
Equivalente en Excel
Base de datos
DATABASE
Archivo .xlsx completo
Tabla
TABLE
Una hoja dentro del archivo
Columna / Campo
COLUMN
Una columna (A, B, C...)
Fila / Registro
ROW / RECORD
Una fila de datos
Clave primaria
PRIMARY KEY
Un identificador único por fila
Consulta
QUERY
Una fórmula que devuelve datos
Tipos de datos más importantes en SQL
SQL — Tipos de datos
-- ── Tipos de texto ──VARCHAR(255) -- texto de longitud variable, máx. 255 caracteres (nombre, email)TEXT-- texto largo sin límite fijo (descripciones, notas)CHAR(10) -- texto de longitud fija (ej: código postal)-- ── Tipos numéricos ──INT-- entero (hasta ~2 mil millones)BIGINT-- entero muy grande (IDs de alta escala)DECIMAL(10,2) -- número con 2 decimales exactos (precios, dinero)FLOAT-- número de punto flotante (menos preciso, usa DECIMAL para dinero)-- ── Tipos de fecha y tiempo ──DATE-- solo fecha: '2026-06-15'DATETIME-- fecha y hora: '2026-06-15 14:30:00'TIMESTAMP-- como DATETIME, se actualiza automáticamente (ideal para "creado_en")-- ── Tipo booleano ──TINYINT(1) -- MySQL usa esto como booleano: 0=false, 1=trueBOOLEAN-- alias de TINYINT(1) en MySQL-- ── ENUM: solo acepta valores de una lista predefinida ──ENUM('activo', 'inactivo', 'pendiente') -- solo esos 3 valores son válidos
Tu primera consulta SELECT
SQL — SELECT: consultar datos
-- ── Seleccionar TODAS las columnas de una tabla ──SELECT*FROMclientes;
-- ── Seleccionar columnas específicas ──SELECTnombre, email, ciudadFROMclientes;
-- ── Filtrar con WHERE ──SELECT*FROMclientesWHEREciudad='Ciudad de México';
-- ── Múltiples condiciones con AND / OR ──SELECTnombre, emailFROMclientesWHEREactivo=1ANDciudadIN ('Guadalajara', 'Monterrey');
-- ── Ordenar resultados ──SELECT*FROMclientesORDER BYnombreASC; -- ASC=A→Z, DESC=Z→A-- ── Limitar el número de resultados ──SELECT*FROMclientesLIMIT10; -- solo los primeros 10 registros-- ── Buscar texto parcial con LIKE ──SELECT*FROMclientesWHEREemailLIKE'%@gmail.com'; -- % es comodín
Demo interactiva: simula consultas SQL
▶ Simulador SELECT — filtra la tabla de clientes
Presiona un botón para simular una consulta SQL.
✅ Buena práctica: siempre nombra las columnas en SELECT
Aunque SELECT * es cómodo para explorar, en producción es mejor listar columnas específicas: SELECT nombre, email FROM clientes. Así tu consulta es más rápida (el motor no carga datos innecesarios), más legible, y no se rompe si alguien agrega o cambia columnas en la tabla.
🧠 Quiz: ¿Qué cláusula SQL usas para filtrar filas y obtener solo los registros que cumplen una condición?
L13
Fundamentos
MySQL: crea y configura tu base de datos
MySQL es el sistema de gestión de bases de datos relacionales (SGBD) más popular del mundo — lo usan Facebook, Twitter, YouTube y millones de aplicaciones web. Es open-source, rápido y tiene una comunidad enorme. SQL es el lenguaje; MySQL es el motor que lo ejecuta.
🏗️
Analogía: SQL es el idioma, MySQL es quien te escucha
SQL es como el español: un lenguaje con gramática y vocabulario. MySQL es como una persona hispanohablante muy eficiente que entiende ese idioma y ejecuta tus instrucciones. Podrías hablar el mismo español con otra persona (PostgreSQL, SQLite), y aunque tienen pequeños acentos o dialectos, la base es la misma. Aprender SQL te da acceso a todos los motores; aprender MySQL te da el motor más común en aplicaciones web.
Crear una base de datos y tabla desde cero
SQL — Crear base de datos y tabla clientes
-- ── Paso 1: crear la base de datos ──CREATE DATABASEsistema_clientesCHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci; -- soporte completo para emojis y acentos-- ── Paso 2: seleccionar la base de datos a usar ──USEsistema_clientes;
-- ── Paso 3: crear la tabla clientes ──CREATE TABLEclientes (
id_clienteINTNOT NULL AUTO_INCREMENT,
nombreVARCHAR(100) NOT NULL,
apellidoVARCHAR(100) NOT NULL,
emailVARCHAR(255) NOT NULL UNIQUE,
telefonoVARCHAR(20),
ciudadVARCHAR(100),
activoTINYINT(1) DEFAULT1,
creado_enTIMESTAMPDEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id_cliente)
) ENGINE=InnoDB;
-- ── Ver la estructura de la tabla ──DESCRIBEclientes;
-- ── Ver todas las tablas de la base de datos ──SHOW TABLES;
Restricciones (Constraints): garantías de integridad
Constraint
Significado
Ejemplo de uso
NOT NULL
El campo no puede estar vacío
nombre, email — campos obligatorios
UNIQUE
No puede haber dos filas con el mismo valor
email — un usuario por correo
PRIMARY KEY
Identificador único de cada fila (NOT NULL + UNIQUE)
id_cliente
FOREIGN KEY
Referencia a la PK de otra tabla (relación)
id_cliente en tabla pedidos
DEFAULT
Valor por defecto si no se proporciona uno
activo DEFAULT 1
AUTO_INCREMENT
El motor genera el valor automáticamente, incrementando
id_cliente: 1, 2, 3...
CHECK
El valor debe cumplir una condición
edad CHECK (edad >= 18)
Modificar una tabla existente con ALTER TABLE
SQL — ALTER TABLE: modificar la estructura
-- ── Agregar una columna ──ALTER TABLEclientesADD COLUMNfecha_nacimientoDATE;
-- ── Agregar columna en posición específica ──ALTER TABLEclientesADD COLUMNrfcVARCHAR(13) AFTERapellido;
-- ── Cambiar el tipo de una columna ──ALTER TABLEclientesMODIFY COLUMNtelefonoVARCHAR(30);
-- ── Eliminar una columna ──ALTER TABLEclientesDROP COLUMNfecha_nacimiento;
-- ── Renombrar una columna ──ALTER TABLEclientesRENAME COLUMNciudadTOciudad_origen;
-- ── Agregar un índice para acelerar búsquedas ──CREATE INDEXidx_emailONclientes(email);
⚠ Cuidado con DROP TABLE y DROP DATABASE
DROP TABLE clientes elimina la tabla y todos sus datos de forma permanente. DROP DATABASE sistema_clientes elimina la base de datos completa. No hay papelera de reciclaje. Antes de ejecutar cualquier comando destructivo en producción, haz un backup. Una práctica útil: usa IF EXISTS para evitar errores: DROP TABLE IF EXISTS clientes.
🧠 Quiz: Quieres que el campo email en tu tabla clientes nunca pueda repetirse entre dos registros distintos. ¿Qué constraint usas?
L14
Intermedio
CRUD: las cuatro operaciones fundamentales
CRUD es el acrónimo de las cuatro operaciones que toda aplicación de datos realiza: Create (insertar), Read (leer), Update (actualizar), Delete (eliminar). En SQL se traducen en INSERT, SELECT, UPDATE y DELETE. Si dominas estas cuatro, puedes construir cualquier sistema de gestión de información.
📋
Analogía: CRUD es como gestionar un directorio telefónico
Cuando das de alta a un nuevo contacto, estás haciendo un Create. Cuando buscas el número de alguien, es un Read. Cuando alguien cambia su número y lo actualizas, es un Update. Y cuando borras un contacto que ya no necesitas, es un Delete. Todo sistema de información — desde un CRM hasta Instagram — hace variantes de estas cuatro operaciones miles de veces por segundo.
C — CREATE: INSERT INTO
SQL — INSERT INTO: insertar registros
-- ── Insertar un solo registro ──INSERT INTOclientes (nombre, apellido, email, telefono, ciudad)
VALUES ('Ana', 'García', 'ana@ejemplo.com', '555-1234', 'Ciudad de México');
-- ── Insertar múltiples registros en una sola consulta (eficiente) ──INSERT INTOclientes (nombre, apellido, email, ciudad)
VALUES
('Luis', 'Pérez', 'luis@ejemplo.com', 'Guadalajara'),
('María', 'López', 'maria@ejemplo.com', 'Monterrey'),
('Carlos','Ruiz', 'carlos@ejemplo.com','Puebla');
-- ── Los campos con DEFAULT (id, activo, creado_en) se generan solos ──-- id_cliente: 1, 2, 3... (AUTO_INCREMENT)-- activo: 1 (DEFAULT 1)-- creado_en: fecha/hora actual (DEFAULT CURRENT_TIMESTAMP)-- ── Ver el ID del último registro insertado ──SELECTLAST_INSERT_ID();
R — READ: SELECT con funciones de agregación
SQL — SELECT avanzado con funciones y GROUP BY
-- ── Funciones de agregación ──SELECTCOUNT(*) AStotal_clientesFROMclientes;
SELECTMAX(monto) ASventa_mayorFROMpedidos;
SELECTMIN(monto) ASventa_menorFROMpedidos;
SELECTSUM(monto) AStotal_ventasFROMpedidos;
SELECTAVG(monto) ASpromedioFROMpedidos;
-- ── GROUP BY: agrupar y contar por categoría ──SELECTciudad,
COUNT(*) ASnum_clientesFROMclientesGROUP BYciudadORDER BYnum_clientesDESC;
-- Resultado esperado:-- ciudad | num_clientes-- Ciudad de México | 45-- Guadalajara | 28-- Monterrey | 19-- ── HAVING: filtrar DESPUÉS de agrupar (WHERE no sirve con agregaciones) ──SELECTciudad, COUNT(*) AStotalFROMclientesGROUP BYciudadHAVINGCOUNT(*) >5-- solo ciudades con más de 5 clientesORDER BYtotalDESC;
U — UPDATE: actualizar registros
SQL — UPDATE: modificar datos existentes
-- ── Actualizar UN campo de UN registro ──UPDATEclientesSETtelefono='555-9999'WHEREid_cliente=1;
-- ── Actualizar múltiples campos a la vez ──UPDATEclientesSETciudad='Guadalajara',
activo=1WHEREid_cliente=3;
-- ── Actualizar TODOS los registros que cumplen una condición ──UPDATEclientesSETactivo=0WHEREciudad='Puebla'; -- desactiva todos los clientes de Puebla-- ── Verificar ANTES de actualizar ──SELECT*FROMclientesWHEREid_cliente=1; -- primero revisa qué vas a modificar
D — DELETE: eliminar registros
SQL — DELETE: eliminar filas
-- ── Eliminar UN registro específico ──DELETE FROMclientesWHEREid_cliente=5;
-- ── Eliminar con condición múltiple ──DELETE FROMclientesWHEREactivo=0ANDcreado_en<'2024-01-01'; -- clientes inactivos antes de 2024-- ⚠ Sin WHERE eliminas TODOS los registros:DELETE FROMclientes; -- ¡PELIGROSO! Vacía la tabla completamente-- ── Soft delete: mejor práctica en producción ──-- En vez de borrar, marca como inactivo (los datos se conservan)UPDATEclientesSETactivo=0, eliminado_en=NOW()
WHEREid_cliente=5; -- "eliminado" sin perder el historial
🚨 La regla de oro del UPDATE y DELETE
Siempre incluye WHERE. Un UPDATE clientes SET activo = 0 sin WHERE desactiva a todos tus clientes. Un DELETE FROM clientes sin WHERE borra toda la tabla. Antes de ejecutar cualquier UPDATE o DELETE en producción: (1) convierte el UPDATE/DELETE en un SELECT con el mismo WHERE, (2) verifica que sean los registros correctos, (3) luego ejecuta la modificación.
Demo interactiva: CRUD en acción
▶ Mini sistema CRUD — simula INSERT, UPDATE y DELETE
Agrega un cliente para comenzar.
🧠 Quiz: Ejecutas UPDATE clientes SET ciudad = 'Tijuana' sin la cláusula WHERE. ¿Qué ocurre?
L15
Avanzado
Relaciones: conectar tablas con claves foráneas
La verdadera potencia de las bases de datos relacionales está en las relaciones entre tablas. En lugar de repetir datos (el nombre del cliente en cada pedido), usas una clave foránea (Foreign Key) que apunta al registro en la otra tabla. Esto evita la duplicación, garantiza la consistencia y permite hacer preguntas que abarcan múltiples entidades.
🔗
Analogía: las claves foráneas son como citas bibliográficas
En un libro académico, en vez de copiar un artículo completo cada vez que lo mencionas, escribes una referencia: "(García, 2024, p.45)". La base de datos hace lo mismo: en la tabla pedidos, en vez de repetir todos los datos del cliente, guardas su id_cliente. Si el cliente cambia su teléfono, lo actualizas en un solo lugar y todos los pedidos que lo referencian tienen automáticamente el dato correcto.
Tipos de relaciones
Tipo
Descripción
Ejemplo real
Uno a Uno (1:1)
Un registro en A tiene exactamente un registro relacionado en B
Un usuario tiene un perfil extendido
Uno a Muchos (1:N)
Un registro en A puede tener muchos en B, pero cada B pertenece a un solo A
Un cliente puede tener muchos pedidos
Muchos a Muchos (N:M)
Un registro en A puede relacionarse con muchos en B, y viceversa
Un pedido puede tener muchos productos; un producto puede estar en muchos pedidos
Diagrama de relaciones del proyecto
📋 clientes
🔑 id_cliente (PK)
nombre
apellido
email
ciudad
──────
1 : N
──────▶
🛒 pedidos
🔑 id_pedido (PK)
🔗 id_cliente (FK)
fecha
total
estado
──────
1 : N
──────▶
📦 productos
🔑 id_producto (PK)
nombre
precio
stock
Crear tablas relacionadas con FOREIGN KEY
SQL — Tabla pedidos con relación a clientes
-- ── Primero debe existir la tabla padre (clientes) ──CREATE TABLEpedidos (
id_pedidoINTNOT NULL AUTO_INCREMENT,
id_clienteINTNOT NULL, -- FK hacia clientesfechaDATETIMEDEFAULT CURRENT_TIMESTAMP,
totalDECIMAL(10,2) NOT NULL,
estadoENUM('pendiente','procesando','enviado','entregado','cancelado')
DEFAULT'pendiente',
notasTEXT,
PRIMARY KEY (id_pedido),
-- ── Definir la restricción de clave foránea ──CONSTRAINTfk_pedido_clienteFOREIGN KEY (id_cliente)
REFERENCESclientes(id_cliente)
ON DELETE RESTRICT-- impide borrar un cliente que tenga pedidosON UPDATE CASCADE-- si cambia el id del cliente, se actualiza aquí también
) ENGINE=InnoDB;
-- ── Tabla intermedia para relación N:M pedidos ↔ productos ──CREATE TABLEpedido_productos (
id_pedidoINTNOT NULL,
id_productoINTNOT NULL,
cantidadINTNOT NULL DEFAULT1,
precio_unitDECIMAL(10,2) NOT NULL,
PRIMARY KEY (id_pedido, id_producto), -- clave primaria compuestaFOREIGN KEY (id_pedido) REFERENCESpedidos(id_pedido) ON DELETE CASCADE,
FOREIGN KEY (id_producto) REFERENCESproductos(id_producto) ON DELETE RESTRICT
) ENGINE=InnoDB;
Opciones ON DELETE y ON UPDATE
Opción
Comportamiento
Cuándo usarla
RESTRICT
Impide la operación si hay filas relacionadas
No borrar clientes con pedidos activos
CASCADE
Propaga la operación a las filas relacionadas
Borrar pedido → borrar sus líneas de detalle
SET NULL
Pone NULL en la FK de las filas relacionadas
Categoría eliminada → producto sin categoría
NO ACTION
Como RESTRICT pero evaluado al final de la transacción
Comportamiento por defecto en MySQL
💡 Normalización: evitar la redundancia
Diseñar bien las relaciones se llama normalización. La regla básica de la 3FN (Tercera Forma Normal): cada campo debe depender directamente de la clave primaria, y no de otros campos no clave. En la práctica: si te encuentras repitiendo el nombre del cliente en cada pedido, eso debe ser una FK. Si una tabla tiene más de ~7 columnas y notas que algunos grupos de columnas "se repiten juntos", probablemente deberías dividirla.
🧠 Quiz: Tienes una tabla pedidos con una FK id_cliente que apunta a clientes, configurada con ON DELETE RESTRICT. Intentas eliminar un cliente que tiene 5 pedidos. ¿Qué ocurre?
L16
Avanzado
JOINs: consultar múltiples tablas a la vez
Los JOINs son el corazón de las consultas relacionales: te permiten combinar filas de dos o más tablas basándote en una condición (generalmente la relación FK→PK). En lugar de hacer dos consultas y unir los datos en el código, le pides al motor de la base de datos que haga ese trabajo — de forma mucho más eficiente.
🧩
Analogía: un JOIN es como unir dos hojas de Excel con VLOOKUP/BUSCARV
Si tienes los datos de clientes en una hoja y los pedidos en otra, en Excel usarías BUSCARV para traer el nombre del cliente a la tabla de pedidos. En SQL, un INNER JOIN hace exactamente eso — y mucho más: une las hojas, filtra, agrupa y ordena todo en una sola instrucción, para millones de filas, en milisegundos. Cuando ves que un reporte "tiene datos de clientes y pedidos juntos", siempre hay un JOIN detrás.
Los 4 tipos de JOIN: visual comparativo
📋 clientes (A)
1 · Ana García
2 · Luis Pérez
3 · María López
4 · Carlos Ruiz
🛒 pedidos (B)
P01 · cliente 1
P02 · cliente 2
P03 · cliente 2
P04 · cliente 3
P05 · cliente 99
INNER JOIN (intersección)
Ana · P01
Luis · P02
Luis · P03
María · P04
Carlos (sin pedidos) y P05 (cliente inexistente) no aparecen.
INNER JOIN: solo los que coinciden en ambas tablas
SQL — INNER JOIN: clientes con sus pedidos
-- ── Sintaxis básica del INNER JOIN ──SELECTc.nombre,
c.apellido,
c.email,
p.id_pedido,
p.total,
p.estado,
p.fechaFROMclientescINNER JOINpedidospONc.id_cliente=p.id_clienteORDER BYp.fechaDESC;
-- ── JOIN con filtro adicional ──SELECTc.nombre,
COUNT(p.id_pedido) ASnum_pedidos,
SUM(p.total) AStotal_gastadoFROMclientescINNER JOINpedidospONc.id_cliente=p.id_clienteWHEREp.estado='entregado'GROUP BYc.id_clienteORDER BYtotal_gastadoDESCLIMIT10; -- top 10 clientes por gasto total
LEFT JOIN: todos de la izquierda, coincidan o no
SQL — LEFT JOIN: clientes con o sin pedidos
-- ── LEFT JOIN: TODOS los clientes, tengan o no pedidos ──SELECTc.nombre,
c.email,
COUNT(p.id_pedido) ASnum_pedidosFROMclientescLEFT JOINpedidospONc.id_cliente=p.id_clienteGROUP BYc.id_cliente;
-- ── Encontrar clientes SIN ningún pedido ──SELECTc.nombre,
c.email,
c.ciudadFROMclientescLEFT JOINpedidospONc.id_cliente=p.id_clienteWHEREp.id_pedidoIS NULL; -- si el JOIN no encontró pedido, el ID es NULL-- ── JOIN de tres tablas: clientes + pedidos + productos ──SELECTc.nombreAScliente,
p.id_pedido,
pr.nombreASproducto,
pp.cantidad,
pp.precio_unit,
(pp.cantidad*pp.precio_unit) ASsubtotalFROMclientescINNER JOINpedidospONc.id_cliente=p.id_clienteINNER JOINpedido_productosppONp.id_pedido=pp.id_pedidoINNER JOINproductosprONpp.id_producto=pr.id_productoORDER BYp.id_pedido;
Resumen visual: todos los JOINs
JOIN
Devuelve
Caso de uso típico
INNER JOIN
Solo filas con coincidencia en AMBAS tablas
Clientes que tienen pedidos
LEFT JOIN
TODOS de la izquierda + coincidencias de la derecha (NULL si no hay)
Todos los clientes, con o sin pedidos
RIGHT JOIN
Coincidencias de la izquierda + TODOS de la derecha
Todos los pedidos, incluso de clientes eliminados
FULL OUTER JOIN
TODO de ambas tablas (MySQL lo simula con UNION)
Auditoría completa sin perder nada
CROSS JOIN
Producto cartesiano: cada fila de A con cada fila de B
Combinatorias, generación de pares posibles
SELF JOIN
Una tabla unida consigo misma
Jerarquías: empleado → supervisor (misma tabla)
✅ Usa alias siempre en JOINs con múltiples tablas
Cuando joineas varias tablas, los alias hacen el código legible: FROM clientes c INNER JOIN pedidos p. Así escribes c.nombre en lugar del verboso clientes.nombre. Elige alias de una o dos letras que evoquen la tabla: c para clientes, p para pedidos, pr para productos. Siempre especifica el alias de la tabla en cada columna del SELECT cuando hay JOIN — evita ambigüedades y errores difíciles de depurar.
🧠 Quiz: Quieres obtener todos los clientes registrados, incluyendo aquellos que aún no han hecho ningún pedido, junto con el total de pedidos de cada uno. ¿Qué tipo de JOIN usas?
🏆
Proyecto Final
Proyecto Final: Sistema de Gestión de Clientes (CRM)
Es hora de unir todo lo aprendido. Construirás un sistema de gestión de clientes (CRM básico) con base de datos MySQL, que incluye clientes, pedidos y productos relacionados entre sí, con consultas CRUD completas y reportes con JOINs.
✅ SQL SELECT/WHERE✅ MySQL y tablas✅ CRUD completo✅ Relaciones FK✅ JOINs📤 CRM funcional
Vista previa del sistema
🔒 localhost/crm-clientes
CRM · Sistema de Clientes
ClientesPedidosProductosReportes
📋 Clientes registrados
ID
Nombre
Ciudad
Pedidos
Estado
1
Ana García
CDMX
3
Activo
2
Luis Pérez
Guadalajara
1
Activo
3
María López
Monterrey
0
Inactivo
📊 Reporte: Top clientes por ventas
Cliente
Pedidos
Total gastado
Ana García
3
$4,250.00
Luis Pérez
1
$899.00
Estructura del proyecto
Estructura del proyecto CRM
crm-clientes/│├── sql/│ ├── 01_crear_bd.sql# CREATE DATABASE + USE│ ├── 02_crear_tablas.sql# CREATE TABLE clientes, pedidos, productos, pedido_productos│ ├── 03_datos_prueba.sql# INSERT INTO con datos de ejemplo│ ├── 04_crud_clientes.sql# SELECT, INSERT, UPDATE, DELETE para clientes│ ├── 05_crud_pedidos.sql# CRUD completo para pedidos│ └── 06_reportes.sql# Consultas con JOINs y GROUP BY para reportes│├── php/# (opcional) conexión desde PHP│ ├── conexion.php# PDO connection string│ ├── clientes.php# funciones CRUD para clientes│ └── reportes.php# ejecuta los JOINs y devuelve resultados│└── README.md# instrucciones de instalación y uso
Script completo de la base de datos
SQL — 02_crear_tablas.sql completo del proyecto
CREATE DATABASE IF NOT EXISTSsistema_clientesCHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USEsistema_clientes;
CREATE TABLEclientes (
id_clienteINTNOT NULL AUTO_INCREMENT PRIMARY KEY,
nombreVARCHAR(100) NOT NULL,
apellidoVARCHAR(100) NOT NULL,
emailVARCHAR(255) NOT NULL UNIQUE,
telefonoVARCHAR(20),
ciudadVARCHAR(100),
activoTINYINT(1) DEFAULT1,
creado_enTIMESTAMPDEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLEproductos (
id_productoINTNOT NULL AUTO_INCREMENT PRIMARY KEY,
nombreVARCHAR(150) NOT NULL,
descripcionTEXT,
precioDECIMAL(10,2) NOT NULL,
stockINTDEFAULT0
) ENGINE=InnoDB;
CREATE TABLEpedidos (
id_pedidoINTNOT NULL AUTO_INCREMENT PRIMARY KEY,
id_clienteINTNOT NULL,
fechaDATETIMEDEFAULT CURRENT_TIMESTAMP,
totalDECIMAL(10,2) NOT NULL DEFAULT0.00,
estadoENUM('pendiente','procesando','enviado','entregado','cancelado')
DEFAULT'pendiente',
notasTEXT,
CONSTRAINTfk_pedido_clienteFOREIGN KEY (id_cliente) REFERENCESclientes(id_cliente)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLEpedido_productos (
id_pedidoINTNOT NULL,
id_productoINTNOT NULL,
cantidadINTNOT NULL DEFAULT1,
precio_unitDECIMAL(10,2) NOT NULL,
PRIMARY KEY (id_pedido, id_producto),
FOREIGN KEY (id_pedido) REFERENCESpedidos(id_pedido) ON DELETE CASCADE,
FOREIGN KEY (id_producto) REFERENCESproductos(id_producto) ON DELETE RESTRICT
) ENGINE=InnoDB;
Consultas de reporte con JOINs
SQL — 06_reportes.sql: consultas analíticas del CRM
Instala MySQL Server + MySQL Workbench (GUI visual) o usa XAMPP (incluye MySQL + phpMyAdmin). En Linux puedes usar sudo apt install mysql-server y gestionar desde la terminal o DBeaver.
2
Crea la base de datos y tablas
Ejecuta los scripts SQL en orden: primero 01_crear_bd.sql, luego 02_crear_tablas.sql. Verifica con SHOW TABLES y DESCRIBE clientes que todo se creó correctamente.
3
Carga datos de prueba
Ejecuta 03_datos_prueba.sql con al menos 10 clientes, 5 productos y 15 pedidos distribuidos entre los clientes. Incluye casos borde: clientes sin pedidos, pedidos cancelados, productos con stock en cero.
4
Implementa el CRUD completo
Escribe y prueba los 4 scripts CRUD: INSERT para nuevos clientes, SELECT con múltiples filtros, UPDATE para actualizar datos y estado, y DELETE (o soft delete) para desactivar clientes. Asegúrate de probar el RESTRICT: intenta borrar un cliente con pedidos.
5
Construye los reportes con JOINs
Implementa al menos 3 reportes: top clientes por ventas (INNER JOIN + GROUP BY), clientes sin actividad (LEFT JOIN + IS NULL), y productos más vendidos (triple JOIN). Verifica que los resultados sean correctos cruzando con tus datos de prueba.
6
(Opcional) Conecta desde PHP
Si completaste el Módulo 3, conecta la base de datos a una interfaz web con PHP y PDO (o usa Node.js con mysql2). Crea páginas para listar clientes, agregar nuevos, ver el detalle de pedidos y mostrar los reportes de forma visual.
7
Documenta y versiona con Git
Sube el proyecto a GitHub con el README completo: instrucciones de instalación, descripción del esquema, capturas de los reportes. Usa ramas: main para el esquema estable y feature/reportes mientras desarrollas las consultas analíticas.
Lista de verificación final
✅
Base de datos creada con charset utf8mb4 y al menos 4 tablas correctamente estructuradas.
✅
Claves primarias y foráneas definidas en todas las tablas relacionadas, con InnoDB.
✅
CRUD completo para clientes y pedidos: INSERT, SELECT con filtros, UPDATE, DELETE/soft delete.
✅
Relaciones probadas: verificado que RESTRICT impide borrar clientes con pedidos y CASCADE elimina líneas de detalle al borrar un pedido.
✅
Al menos 3 consultas con JOIN: INNER JOIN, LEFT JOIN, y un join de 3 tablas con GROUP BY.
✅
Reportes analíticos: top clientes, clientes inactivos, productos más vendidos.
✅
Datos de prueba realistas con al menos 10 clientes, 5 productos y 15 pedidos.
✅
Scripts SQL organizados, comentados y subidos a GitHub con README.
🎓 Lo que acabas de lograr
Dominas el núcleo de cualquier aplicación backend real. El 90% de las aplicaciones web del mundo — desde e-commerce hasta redes sociales — usa exactamente estas mismas operaciones: modelar entidades con tablas relacionadas, manipularlas con CRUD, y extraer valor con consultas analíticas. Con esta base, estás listo para aprender frameworks como Laravel, Django o Express que simplemente añaden una capa sobre lo que ya sabes.
🚀 ¿Qué sigue? Módulo 5
El Módulo 5 conectará las bases de datos con el mundo web: aprenderás a construir una API REST que exponga los datos del CRM a través de endpoints HTTP, conectando el backend con el frontend del Módulo 3. El círculo completo: base de datos → API → interfaz web.
🏆
¡Módulo 4 completado!
Dominas las bases de datos relacionales: diseñas esquemas con MySQL, escribes SQL fluido, implementas CRUD completo, modelas relaciones con claves foráneas y extraes información con JOINs complejos. Tu CRM de clientes lo demuestra.
0 Comentarios