Si quieres aprender MySQL desde cero con SQL DDL, este tutorial práctico es para ti. Hoy convertirás tu diagrama relacional en una base de datos funcional y profesional, paso a paso.
Además, no se trata de teoría abstracta: es un Build-Along donde escribirás cada línea de código, la ejecutarás y verás cómo nace tu sistema de facturación.
Si en el episodio anterior definimos los tipos de datos y reglas de negocio, hoy damos el paso definitivo que separa a los diseñadores de los constructores reales. En este tutorial de MySQL desde cero con SQL DDL, convertirás ese diagrama relacional que diseñaste en Draw.io en una base de datos funcional y profesional.
No se trata de teoría abstracta: hoy es un Build-Along (construye conmigo) donde escribirás cada línea de código, la ejecutarás y verás cómo nace tu sistema de facturación.
Antes de Programar: El Flujo de Trabajo Profesional
Antes de tocar cualquier tecla, necesito explicarte el flujo de trabajo que usaremos.
En primer lugar, el flujo de trabajo que usaremos te evitará errores en cascada.En primer lugar, el flujo de trabajo que usaremos te evitará errores en cascada.
Estrategia de Archivos Separados
Utilizaremos dos archivos SQL independientes para mantener el control total del proceso:
- Archivo 01_contenedor.sql: Base de datos y tablas independientes (categorías, clientes)
- Archivo 02_relaciones.sql: Tablas dependientes con Foreign Keys (productos, facturas, detalle_factura)
¿Por qué esta separación? Porque tú copias, ejecutas, validas y avanzas. Así entiendes el orden lógico de creación y corriges errores sobre la marcha.
Regla de Sintaxis Crítica en MySQL
Antes de continuar, una regla que te ahorrará dolores de cabeza: en MySQL, los comentarios nunca deben cortar las palabras clave. Los comentarios van arriba o al final de una línea completa, nunca en medio de una instrucción.
-- ✅ CORRECTO
CREATE DATABASE sistema_facturacion; -- Crear base de datos
-- ❌ INCORRECTO (causará error de sintaxis)
CREATE /* comentario */ DATABASE sistema_facturacion;
Creando el Contenedor: Base de Datos con Configuración Profesional
Comenzamos con lo más fundamental: crear la base de datos con una configuración que soporte idiomas, caracteres especiales y emojis sin errores de codificación.
Código SQL: Creación de Base de Datos
-- ============================================
-- ARCHIVO: 01_contenedor.sql
-- DESCRIPCIÓN: Base de datos y tablas independientes
-- ============================================
-- Crear base de datos si no existe
CREATE DATABASE IF NOT EXISTS sistema_facturacion
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- Seleccionar la base de datos para trabajar
USE sistema_facturacion;
Explicación Línea por Línea
IF NOT EXISTS: Resiliencia del Script
Esta cláusula evita errores si ejecutas el script múltiples veces. Si la base de datos ya existe, MySQL simplemente continúa sin marcar error. Esto se conoce como resiliencia del script.
utf8mb4: El Estándar Moderno
Anteriormente se usaba utf8 o spanish, pero estos solo soportaban 3 bytes. Con utf8mb4 (4 bytes), tus usuarios pueden escribir nombres completos como «José Luis», direcciones con símbolos especiales, e incluso emojis sin que la base de datos los rechace.
¿Alguna vez has visto páginas donde los acentos aparecen como ? o diamantes extraños? Eso sucede cuando no se usa la codificación correcta.
utf8mb4_unicode_ci: Comparaciones Inteligentes
El collation (reglas de comparación) unicode_ci hace que las búsquedas no distingan entre mayúsculas/minúsculas ni acentos. Esto es crucial para sistemas de login donde un usuario puede escribir:
-
joseluis@email.com(minúsculas) -
JoseLuis@EMAIL.com(mixto)
Ambos casos funcionarán correctamente sin duplicar registros.
"La configuración correcta de caracteres desde el inicio te ahorrará horas de debugging en producción."
IscJoseluischavezG
Foreign Keys y Relaciones en MySQL
Tablas Independientes: El Orden es Ley en SQL
En SQL, el orden de creación no es opcional: es ley. Primero creamos las tablas que no dependen de otras. Si intentas crear una tabla con Foreign Key antes de existir la tabla referenciada, MySQL marcará error.
Tabla 1: Categorías (Sin Dependencias)
-- Crear tabla categorías
CREATE TABLE categorias (
id_categoria INT AUTO_INCREMENT PRIMARY KEY,
nombre_categoria VARCHAR(50) NOT NULL,
descripcion_categoria TEXT,
INDEX idx_nombre_categoria (nombre_categoria)
) ENGINE=InnoDB;
Desglose de Componentes
¿Qué es INT AUTO_INCREMENT PRIMARY KEY?
MySQL genera automáticamente los IDs (1, 2, 3…) sin que tú intervengas. La PRIMARY KEY garantiza unicidad.
¿Cuándo usar VARCHAR vs TEXT?
TEXT: Para descripciones largas. Se almacena en bloques separados para no saturar la fila principal.
¿Por qué crear un INDEX?
Como el índice de un libro, convierte búsquedas lentas en instantáneas. Fundamental para campos que usarás frecuentemente en WHERE.
¿Qué es ENGINE=InnoDB?
Obligatorio si usarás Foreign Keys y transacciones. Es el motor que permite integridad referencial.
Tabla 2: Clientes (Con Campos Únicos)
-- Crear tabla clientes
CREATE TABLE clientes (
id_cliente INT AUTO_INCREMENT PRIMARY KEY,
nombre_cliente VARCHAR(100) NOT NULL,
direccion_cliente VARCHAR(200),
telefono_cliente VARCHAR(20),
email_cliente VARCHAR(100) UNIQUE NOT NULL,
fecha_registro DATE,
INDEX idx_email_cliente (email_cliente),
INDEX idx_nombre_cliente (nombre_cliente)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
La Importancia de UNIQUE
El campo email_cliente tiene la restricción UNIQUE. Esto significa que MySQL bloqueará automáticamente cualquier intento de insertar un email duplicado. Es tu primera línea de defensa contra datos duplicados.
Nota que direccion_cliente y telefono_cliente no tienen NOT NULL. Esto permite flexibilidad: un cliente puede registrarse sin proporcionar su dirección inicialmente.
Tablas Dependientes: Foreign Keys y Reglas de Negocio
Ahora viene la parte donde todo se conecta. Las tablas dependientes usan Foreign Keys para establecer relaciones con las tablas independientes que ya creamos.
Tabla 3: Productos (Depende de Categorías)
-- Crear tabla productos
CREATE TABLE producto (
id_producto INT AUTO_INCREMENT PRIMARY KEY,
nombre_producto VARCHAR(100) NOT NULL,
descripcion_producto TEXT,
precio_unitario DECIMAL(10,2) NOT NULL,
stock INT DEFAULT 0,
id_categoria INT NOT NULL,
-- Foreign Key
FOREIGN KEY (id_categoria)
REFERENCES categorias(id_categoria)
ON DELETE RESTRICT
ON UPDATE CASCADE,
INDEX idx_nombre_producto (nombre_producto),
INDEX idx_id_categoria (id_categoria)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Entendiendo las Reglas de Negocio en Foreign Keys
ON DELETE RESTRICT: Protección de Inventario
Si intentas borrar una categoría que ya tiene productos asignados, MySQL te detendrá y te obligará a reasignar o limpiar los productos primero. Esto protege tu inventario de eliminaciones accidentales.
Ejemplo real: Si borras la categoría «Electrónicos» que tiene 50 productos, MySQL dirá: «Error: No puedes eliminar esta categoría porque tiene productos asociados».
DECIMAL(10,2): Precisión Financiera
Nunca uses FLOAT para dinero. El tipo DECIMAL(10,2) garantiza precisión exacta con 2 decimales. Esto evita errores de redondeo en contabilidad que pueden costar miles de pesos.
Ejemplo: DECIMAL(10,2) permite valores desde -99999999.99 hasta 99999999.99 con precisión exacta.
Tabla 4: Facturas (Depende de Clientes)
-- Crear tabla facturas
CREATE TABLE factura (
id_factura INT AUTO_INCREMENT PRIMARY KEY,
folio VARCHAR(24) UNIQUE NOT NULL,
fecha_emision DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
subtotal DECIMAL(10,2) NOT NULL,
iva DECIMAL(10,2) NOT NULL,
total DECIMAL(10,2) NOT NULL,
id_cliente INT NOT NULL,
id_empleado INT,
-- Foreign Key
FOREIGN KEY (id_cliente)
REFERENCES clientes(id_cliente)
ON DELETE RESTRICT
ON UPDATE CASCADE,
INDEX idx_folio (folio),
INDEX idx_fecha_emision (fecha_emision),
INDEX idx_id_cliente (id_cliente)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CURRENT_TIMESTAMP: Auditoría Nativa
El campo fecha_emision usa DEFAULT CURRENT_TIMESTAMP. Si olvidas pasar la fecha al crear una factura, MySQL automáticamente coloca la fecha y hora exactas del servidor. Esto es auditoría nativa sin código adicional.
Tabla 5: Detalle de Factura (Relación Muchos a Muchos)
Esta es la tabla más importante del sistema. Resuelve la relación muchos a muchos entre facturas y productos: una factura tiene muchos productos, y un producto puede estar en muchas facturas.
-- Crear tabla detalle_factura
CREATE TABLE detalle_factura (
id_detalle INT AUTO_INCREMENT PRIMARY KEY,
id_factura INT NOT NULL,
id_producto INT NOT NULL,
cantidad INT NOT NULL,
precio_unitario DECIMAL(10,2) NOT NULL,
subtotal DECIMAL(10,2) NOT NULL,
-- Foreign Keys
FOREIGN KEY (id_factura)
REFERENCES factura(id_factura)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (id_producto)
REFERENCES producto(id_producto)
ON DELETE RESTRICT
ON UPDATE CASCADE,
INDEX idx_id_factura (id_factura),
INDEX idx_id_producto (id_producto)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ON DELETE CASCADE: Limpieza Automática
Si borras una factura, todas sus líneas de detalle se borran automáticamente. No quedan datos huérfanos ensuciando tus reportes. Esto es integridad de datos en acción.
Precio Unitario Histórico
El campo precio_unitario en detalle_factura guarda el precio al momento de la venta. Si mañana cambias el precio en el catálogo, las facturas de ayer no se alteran. Esto es integridad financiera real.
¿Por qué guardar el precio en detalle_factura si ya está en producto?
Porque los precios cambian. Si vendiste un producto a $100 hoy y mañana sube a $120, tu factura de hoy debe seguir mostrando $100. Esto es auditoría financiera.
Validación y Verificación de tu Base de Datos
Una vez ejecutados ambos scripts, es momento de validar que todo se creó correctamente.
Comando SHOW TABLES
-- Ver todas las tablas creadas
SHOW TABLES;
Deberías ver:
- categorias
- clientes
- producto
- factura
- detalle_factura
Errores Comunes y Cómo Resolverlos
Durante el desarrollo, es normal cometer errores. Aquí te muestro los más comunes y sus soluciones.
Causa: Intentaste crear una Foreign Key antes de crear la tabla referenciada.
Solución: Asegúrate de ejecutar primero el archivo 01_contenedor.sql antes del 02_relaciones.sql.
Causa: Intentaste insertar un texto más largo que el VARCHAR definido.
Solución: Aumenta la longitud del VARCHAR o usa TEXT para campos de longitud variable.
Causa: Intentaste insertar texto en un campo INT.
Solución: Verifica los tipos de datos en tu INSERT y asegúrate de que coincidan con la definición de la tabla.
¿Qué Viene en el Episodio 5?
En el próximo episodio, aplicaremos DML (Data Manipulation Language) en acción:
- Insertar clientes reales con validaciones
- Generar facturas con JOINs complejos
- Actualizar stock automáticamente con triggers
- Actualizar stock automáticamente con triggersActualizar stock automáticamente con triggers
- Eliminar con seguridad transaccional
Verás cómo el esqueleto que construiste hoy se convierte en un sistema real que respira y procesa datos.
Conclusión: De la Teoría a la Práctica Real
Hoy no solo escribiste código SQL: construiste un sistema con reglas de negocio reales. Cada Foreign Key tiene un propósito, cada tipo de dato tiene una decisión de negocio detrás, y cada índice tiene una razón de optimización.
Esta es la diferencia entre seguir tutoriales y entender ingeniería de software. La estructura está lista. Ahora viene lo emocionante: llenarla de vida con datos reales.
Si este tutorial te fue útil, deja un like, suscríbete al canal y dime en los comentarios: ¿qué tabla de tu proyecto te genera más dudas al conectar con Foreign Keys?
Recuerda: la práctica constante es lo que separa a los programadores de los ingenieros. Nos vemos en el próximo episodio.
Soy José Luis Chávez, y esto es MySQL desde cero.