Base de datos de ejemplo
A lo largo de este tutorial trabajaremos con una base de datos llamada tienda_mysql. Es una base de datos que simula una tienda online con productos, clientes, pedidos y empleados. La hemos diseñado para que sea lo suficientemente rica como para practicar todas las consultas y funcionalidades que cubriremos, pero lo suficientemente sencilla como para que puedas entender cada tabla y cada relación sin perderte.
En este artículo crearás la base de datos desde cero, tabla por tabla, con datos de ejemplo ya incluidos. Al final tendrás un entorno completo para practicar.
Esquema general
La base de datos tiene 8 tablas que se relacionan entre sí:
categorias almacena las categorías de productos (Electrónica, Ropa, Hogar...). Cada categoría puede tener una categoría padre, lo que permite crear una jerarquía de categorías. Por ejemplo, "Smartphones" es una subcategoría de "Electrónica".
productos contiene el catálogo de productos con su nombre, precio, stock y la categoría a la que pertenecen. Cada producto está asociado a exactamente una categoría.
clientes guarda la información de los clientes: nombre, email, teléfono y dirección. El email es único para cada cliente.
empleados almacena los datos de los empleados de la tienda. Incluye una referencia al supervisor de cada empleado, lo que permite crear una jerarquía organizacional. Un empleado puede supervisar a otros empleados.
pedidos registra cada pedido realizado por un cliente. Incluye la fecha, el estado del pedido (pendiente, procesando, enviado, entregado o cancelado), el total y el empleado que gestionó el pedido.
detalle_pedidos es la tabla intermedia que conecta pedidos con productos. Cada fila representa un producto dentro de un pedido, con la cantidad solicitada y el precio unitario en el momento de la compra.
resenas almacena las valoraciones que los clientes dejan sobre los productos, con una puntuación del 1 al 5 y un comentario opcional.
etiquetas_producto permite asignar etiquetas libres a los productos (como "oferta", "novedad", "más vendido"), creando una relación muchos-a-muchos entre productos y sus etiquetas.
Crear la base de datos
Conéctate al servidor MySQL como root o con un usuario que tenga permisos para crear bases de datos, y ejecuta:
CREATE DATABASE tienda_mysql;
USE tienda_mysql;El comando CREATE DATABASE crea la base de datos y USE la selecciona como base de datos activa. A partir de este momento, todas las sentencias SQL que ejecutes se aplicarán sobre tienda_mysql.
Tabla categorias
La tabla de categorías utiliza una referencia a sí misma para crear una jerarquía. El campo categoria_padre_id apunta al id de otra categoría, o es NULL si la categoría es de nivel superior.
CREATE TABLE categorias (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
descripcion TEXT,
categoria_padre_id INT,
FOREIGN KEY (categoria_padre_id) REFERENCES categorias(id)
ON DELETE SET NULL
);Insertamos las categorías. Primero las de nivel superior y después las subcategorías, ya que estas últimas necesitan que exista la categoría padre:
INSERT INTO categorias (nombre, descripcion, categoria_padre_id) VALUES
('Electrónica', 'Dispositivos electrónicos y accesorios', NULL),
('Ropa', 'Ropa y complementos para hombre y mujer', NULL),
('Hogar', 'Artículos para el hogar y decoración', NULL),
('Deportes', 'Equipamiento deportivo y fitness', NULL),
('Libros', 'Libros físicos y digitales', NULL);
INSERT INTO categorias (nombre, descripcion, categoria_padre_id) VALUES
('Smartphones', 'Teléfonos inteligentes', 1),
('Portátiles', 'Ordenadores portátiles', 1),
('Accesorios electrónicos', 'Fundas, cargadores, cables', 1),
('Camisetas', 'Camisetas de manga corta y larga', 2),
('Pantalones', 'Pantalones y vaqueros', 2),
('Muebles', 'Muebles para salón, dormitorio y oficina', 3),
('Cocina', 'Utensilios y electrodomésticos de cocina', 3),
('Running', 'Zapatillas y ropa para correr', 4),
('Fitness', 'Equipamiento de gimnasio y ejercicio', 4),
('Programación', 'Libros de programación y tecnología', 5),
('Novelas', 'Ficción y literatura', 5);Tenemos 16 categorías en total: 5 categorías principales y 11 subcategorías. Esta estructura jerárquica será especialmente útil cuando practiquemos self joins y consultas recursivas.
Tabla productos
Cada producto pertenece a una categoría y tiene un precio, un stock disponible y una fecha de creación que se registra automáticamente:
CREATE TABLE productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
descripcion TEXT,
precio DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
categoria_id INT,
activo BOOLEAN DEFAULT TRUE,
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (categoria_id) REFERENCES categorias(id)
ON DELETE SET NULL
);El campo activo permite "desactivar" un producto sin borrarlo, algo habitual en aplicaciones reales. Los campos creado_en y actualizado_en registran automáticamente cuándo se creó y cuándo se modificó por última vez cada producto.
INSERT INTO productos (nombre, descripcion, precio, stock, categoria_id) VALUES
('iPhone 15 Pro', 'Smartphone Apple con chip A17 Pro y cámara de 48MP', 1299.99, 45, 6),
('Samsung Galaxy S24', 'Smartphone Samsung con Galaxy AI y pantalla AMOLED', 899.99, 62, 6),
('Google Pixel 8', 'Smartphone Google con procesador Tensor G3', 699.00, 38, 6),
('Xiaomi 14', 'Smartphone Xiaomi con cámara Leica', 599.99, 80, 6),
('MacBook Air M3', 'Portátil Apple ultraligero con chip M3', 1399.00, 25, 7),
('Lenovo ThinkPad X1', 'Portátil empresarial con Intel Core Ultra', 1549.00, 18, 7),
('ASUS ROG Zephyrus', 'Portátil gaming con RTX 4070 y pantalla 240Hz', 1899.99, 12, 7),
('Funda iPhone silicona', 'Funda oficial de silicona para iPhone 15', 49.99, 200, 8),
('Cargador USB-C 65W', 'Cargador rápido universal USB-C', 35.99, 150, 8),
('Cable USB-C a Lightning', 'Cable de carga y datos de 2 metros', 19.99, 300, 8),
('Camiseta algodón básica', 'Camiseta 100% algodón orgánico, varios colores', 24.99, 500, 9),
('Camiseta técnica running', 'Camiseta transpirable para deporte', 34.99, 180, 9),
('Vaqueros slim fit', 'Vaqueros elásticos de corte ajustado', 59.99, 120, 10),
('Pantalón chino', 'Pantalón chino clásico de algodón', 49.99, 90, 10),
('Sofá 3 plazas', 'Sofá modular tapizado en tela gris', 599.00, 8, 11),
('Estantería modular', 'Estantería de madera con 5 baldas', 149.99, 22, 11),
('Escritorio ajustable', 'Escritorio eléctrico regulable en altura', 399.00, 15, 11),
('Sartén antiadherente 28cm', 'Sartén de aluminio forjado con recubrimiento cerámico', 39.99, 75, 12),
('Robot de cocina', 'Robot multifunción con 12 velocidades', 249.99, 30, 12),
('Zapatillas running pro', 'Zapatillas con amortiguación de gel para asfalto', 129.99, 65, 13),
('Zapatillas trail', 'Zapatillas todoterreno con suela Vibram', 149.99, 40, 13),
('Mancuernas ajustables', 'Set de mancuernas de 2 a 24 kg', 199.99, 20, 14),
('Esterilla yoga premium', 'Esterilla antideslizante de 6mm', 29.99, 100, 14),
('Banda elástica set x5', 'Kit de 5 bandas de resistencia variable', 19.99, 200, 14),
('Clean Code', 'Robert C. Martin - Código limpio', 39.99, 55, 15),
('Eloquent JavaScript', 'Marijn Haverbeke - JavaScript moderno', 34.99, 42, 15),
('Diseño de APIs', 'Guía práctica para diseñar APIs REST', 29.99, 60, 15),
('Cien años de soledad', 'Gabriel García Márquez', 14.99, 85, 16),
('1984', 'George Orwell - Edición especial', 12.99, 110, 16),
('El nombre del viento', 'Patrick Rothfuss - Crónica del asesino de reyes', 16.99, 70, 16);Son 30 productos distribuidos entre las diferentes subcategorías. Los precios y stocks son variados para que al practicar funciones de agregación y filtros obtengamos resultados interesantes.
Tabla clientes
La tabla de clientes almacena la información de contacto. El email es único, ya que se utiliza como identificador de cuenta:
CREATE TABLE clientes (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
apellidos VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
telefono VARCHAR(20),
direccion VARCHAR(255),
ciudad VARCHAR(100),
codigo_postal VARCHAR(10),
fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);INSERT INTO clientes (nombre, apellidos, email, telefono, direccion, ciudad, codigo_postal) VALUES
('María', 'García López', 'maria.garcia@email.com', '612345678', 'Calle Mayor 15, 3ºA', 'Madrid', '28001'),
('Carlos', 'Rodríguez Martín', 'carlos.rodriguez@email.com', '623456789', 'Av. Diagonal 220', 'Barcelona', '08018'),
('Ana', 'Martínez Ruiz', 'ana.martinez@email.com', '634567890', 'Calle Sierpes 42', 'Sevilla', '41004'),
('Pedro', 'Fernández Castro', 'pedro.fernandez@email.com', '645678901', 'Gran Vía 88', 'Madrid', '28013'),
('Laura', 'López Sánchez', 'laura.lopez@email.com', '656789012', 'Paseo de Gracia 55', 'Barcelona', '08007'),
('David', 'Sánchez Moreno', 'david.sanchez@email.com', '667890123', 'Calle Larios 10', 'Málaga', '29005'),
('Carmen', 'Ruiz Jiménez', 'carmen.ruiz@email.com', '678901234', 'Av. de la Constitución 5', 'Valencia', '46002'),
('Javier', 'Moreno Díaz', 'javier.moreno@email.com', '689012345', 'Calle Alfonso I 18', 'Zaragoza', '50003'),
('Lucía', 'Díaz Hernández', 'lucia.diaz@email.com', '690123456', 'Calle Real 30', 'A Coruña', '15001'),
('Miguel', 'Hernández Torres', 'miguel.hernandez@email.com', '601234567', 'Gran Capitán 22', 'Córdoba', '14001'),
('Sara', 'Torres Vega', 'sara.torres@email.com', '612345670', 'Calle Estafeta 9', 'Pamplona', '31001'),
('Andrés', 'Vega Romero', 'andres.vega@email.com', '623456780', 'Av. de la Libertad 3', 'San Sebastián', '20004'),
('Elena', 'Romero Navarro', 'elena.romero@email.com', '634567891', 'Plaza del Pilar 7', 'Zaragoza', '50001'),
('Roberto', 'Navarro Gil', 'roberto.navarro@email.com', '645678902', 'Calle Colón 48', 'Valencia', '46004'),
('Isabel', 'Gil Molina', 'isabel.gil@email.com', '656789013', 'Rambla Nova 12', 'Tarragona', '43001'),
('Fernando', 'Molina Ortega', 'fernando.molina@email.com', '667890124', 'Paseo del Espolón 1', 'Burgos', '09003'),
('Paula', 'Ortega Serrano', 'paula.ortega@email.com', NULL, 'Calle Toro 25', 'Salamanca', '37002'),
('Alejandro', 'Serrano Blanco', 'alejandro.serrano@email.com', '689012346', NULL, 'Bilbao', '48001'),
('Marta', 'Blanco Castro', 'marta.blanco@email.com', NULL, NULL, 'Gijón', '33201'),
('Diego', 'Castro Iglesias', 'diego.castro@email.com', '601234568', 'Calle Triana 60', 'Las Palmas', '35002');Tenemos 20 clientes con datos variados. Observa que algunos clientes tienen el teléfono o la dirección como NULL. Esto es intencional: nos servirá para practicar las consultas con IS NULL, COALESCE y otras funciones que manejan valores nulos.
Tabla empleados
Los empleados tienen una estructura jerárquica. El campo supervisor_id apunta al id de otro empleado que actúa como su jefe directo:
CREATE TABLE empleados (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
apellidos VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
puesto VARCHAR(100) NOT NULL,
salario DECIMAL(10, 2) NOT NULL,
fecha_contratacion DATE NOT NULL,
supervisor_id INT,
FOREIGN KEY (supervisor_id) REFERENCES empleados(id)
ON DELETE SET NULL
);INSERT INTO empleados (nombre, apellidos, email, puesto, salario, fecha_contratacion, supervisor_id) VALUES
('Ricardo', 'Alonso Pérez', 'ricardo.alonso@tienda.com', 'Director General', 75000.00, '2020-01-15', NULL),
('Sofía', 'Méndez Ruiz', 'sofia.mendez@tienda.com', 'Directora de Ventas', 55000.00, '2020-03-01', 1),
('Alberto', 'Prieto García', 'alberto.prieto@tienda.com', 'Director de Logística', 52000.00, '2020-06-10', 1),
('Natalia', 'Herrera López', 'natalia.herrera@tienda.com', 'Vendedora Senior', 38000.00, '2021-02-20', 2),
('Daniel', 'Vargas Martín', 'daniel.vargas@tienda.com', 'Vendedor', 32000.00, '2021-09-05', 2),
('Patricia', 'Cano Sánchez', 'patricia.cano@tienda.com', 'Vendedora', 32000.00, '2022-01-10', 2),
('Raúl', 'Peña Torres', 'raul.pena@tienda.com', 'Vendedor Junior', 28000.00, '2023-04-15', 4),
('Cristina', 'Guerrero Díaz', 'cristina.guerrero@tienda.com', 'Responsable de Almacén', 35000.00, '2021-05-12', 3),
('Marcos', 'Delgado Romero', 'marcos.delgado@tienda.com', 'Operario de Almacén', 26000.00, '2022-08-20', 8),
('Inés', 'Pascual Moreno', 'ines.pascual@tienda.com', 'Operaria de Almacén', 26000.00, '2023-01-08', 8);La jerarquía queda así: Ricardo es el Director General y no tiene supervisor (NULL). Sofía y Alberto reportan a Ricardo. Natalia, Daniel y Patricia reportan a Sofía. Raúl reporta a Natalia. Cristina reporta a Alberto, y Marcos e Inés reportan a Cristina. Esta estructura es perfecta para practicar self joins y consultas recursivas con CTEs.
Tabla pedidos
Cada pedido está asociado a un cliente y a un empleado que lo gestionó. El estado del pedido sigue un flujo típico de e-commerce:
CREATE TABLE pedidos (
id INT AUTO_INCREMENT PRIMARY KEY,
cliente_id INT NOT NULL,
empleado_id INT,
fecha_pedido DATETIME DEFAULT CURRENT_TIMESTAMP,
estado ENUM('pendiente', 'procesando', 'enviado', 'entregado', 'cancelado') DEFAULT 'pendiente',
total DECIMAL(10, 2) NOT NULL DEFAULT 0,
notas TEXT,
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
ON DELETE CASCADE,
FOREIGN KEY (empleado_id) REFERENCES empleados(id)
ON DELETE SET NULL
);Usamos el tipo ENUM para el estado, lo que garantiza que solo pueda contener uno de los cinco valores definidos. El total se almacena directamente en el pedido por eficiencia, aunque también podría calcularse sumando los detalles.
INSERT INTO pedidos (cliente_id, empleado_id, fecha_pedido, estado, total, notas) VALUES
(1, 4, '2025-10-05 09:30:00', 'entregado', 1349.98, NULL),
(2, 5, '2025-10-08 14:15:00', 'entregado', 899.99, 'Envío urgente'),
(3, 4, '2025-10-12 11:00:00', 'entregado', 94.97, NULL),
(1, 6, '2025-10-20 16:45:00', 'entregado', 449.98, NULL),
(4, 4, '2025-11-02 10:20:00', 'entregado', 1899.99, 'Cliente VIP'),
(5, 5, '2025-11-05 13:30:00', 'entregado', 179.97, NULL),
(6, 6, '2025-11-10 09:00:00', 'enviado', 259.98, NULL),
(7, 4, '2025-11-15 17:20:00', 'enviado', 1399.00, NULL),
(2, 5, '2025-11-18 12:00:00', 'enviado', 129.99, NULL),
(8, 4, '2025-11-22 08:45:00', 'procesando', 599.99, 'Verificar dirección'),
(3, 6, '2025-11-25 15:10:00', 'procesando', 84.97, NULL),
(9, 5, '2025-12-01 10:30:00', 'procesando', 199.99, NULL),
(10, 4, '2025-12-03 14:00:00', 'pendiente', 39.99, NULL),
(4, 6, '2025-12-05 11:15:00', 'pendiente', 79.98, NULL),
(11, 5, '2025-12-08 09:30:00', 'pendiente', 1549.00, NULL),
(12, 4, '2025-12-10 16:00:00', 'cancelado', 699.00, 'Cliente canceló por duplicado'),
(1, 5, '2025-12-12 13:45:00', 'cancelado', 49.99, NULL),
(13, 6, '2025-12-15 10:00:00', 'pendiente', 329.98, NULL),
(14, 4, '2025-12-18 14:30:00', 'pendiente', 59.99, NULL),
(15, 5, '2025-12-20 11:00:00', 'entregado', 74.97, NULL),
(6, 4, '2025-12-22 09:15:00', 'enviado', 249.99, NULL),
(16, 6, '2025-12-28 15:30:00', 'procesando', 399.00, NULL),
(17, 5, '2026-01-03 10:45:00', 'pendiente', 64.98, NULL),
(18, 4, '2026-01-05 12:00:00', 'pendiente', 29.99, NULL),
(19, 6, '2026-01-08 14:20:00', 'cancelado', 149.99, 'Sin stock');Tenemos 25 pedidos con fechas que abarcan desde octubre de 2025 hasta enero de 2026. Los estados están distribuidos de forma que siempre haya pedidos en cada fase del proceso. Algunos pedidos tienen notas y otros no, lo que nos dará variedad al filtrar.
Tabla detalle_pedidos
Esta tabla conecta pedidos con productos. Cada fila indica qué producto se pidió, en qué cantidad y a qué precio. Almacenar el precio unitario aquí (en lugar de consultarlo de la tabla productos) es una práctica estándar, ya que el precio de un producto puede cambiar con el tiempo, pero el precio al que se vendió debe quedar registrado:
CREATE TABLE detalle_pedidos (
id INT AUTO_INCREMENT PRIMARY KEY,
pedido_id INT NOT NULL,
producto_id INT NOT NULL,
cantidad INT NOT NULL,
precio_unitario DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (pedido_id) REFERENCES pedidos(id)
ON DELETE CASCADE,
FOREIGN KEY (producto_id) REFERENCES productos(id)
ON DELETE RESTRICT
);La restricción ON DELETE CASCADE en pedido_id significa que si se borra un pedido, se borran automáticamente sus líneas de detalle. La restricción ON DELETE RESTRICT en producto_id impide borrar un producto que aparezca en algún pedido, protegiendo la integridad del historial de ventas.
INSERT INTO detalle_pedidos (pedido_id, producto_id, cantidad, precio_unitario) VALUES
(1, 1, 1, 1299.99),
(1, 8, 1, 49.99),
(2, 2, 1, 899.99),
(3, 11, 2, 24.99),
(3, 12, 1, 34.99),
(3, 10, 1, 19.99),
(4, 17, 1, 399.00),
(4, 8, 1, 49.99),
(5, 7, 1, 1899.99),
(6, 20, 1, 129.99),
(6, 23, 1, 29.99),
(6, 24, 1, 19.99),
(7, 19, 1, 249.99),
(7, 10, 1, 9.99),
(8, 5, 1, 1399.00),
(9, 20, 1, 129.99),
(10, 4, 1, 599.99),
(11, 25, 1, 39.99),
(11, 26, 1, 34.99),
(11, 10, 1, 9.99),
(12, 22, 1, 199.99),
(13, 18, 1, 39.99),
(14, 13, 1, 59.99),
(14, 24, 1, 19.99),
(15, 6, 1, 1549.00),
(16, 3, 1, 699.00),
(17, 8, 1, 49.99),
(18, 28, 2, 14.99),
(18, 30, 1, 16.99),
(18, 27, 1, 29.99),
(18, 25, 1, 39.99),
(19, 13, 1, 59.99),
(20, 11, 1, 24.99),
(20, 14, 1, 49.99),
(21, 19, 1, 249.99),
(22, 17, 1, 399.00),
(23, 26, 1, 34.99),
(23, 29, 1, 12.99),
(23, 10, 1, 19.99),
(24, 23, 1, 29.99),
(25, 21, 1, 149.99);Hay 41 líneas de detalle repartidas entre los 25 pedidos. Algunos pedidos tienen un solo producto y otros tienen varios, lo que da variedad a las consultas de agrupación y joins.
Tabla resenas
Las reseñas permiten que los clientes valoren los productos que han comprado. La puntuación va de 1 a 5, y el comentario es opcional:
CREATE TABLE resenas (
id INT AUTO_INCREMENT PRIMARY KEY,
producto_id INT NOT NULL,
cliente_id INT NOT NULL,
puntuacion TINYINT NOT NULL CHECK (puntuacion BETWEEN 1 AND 5),
comentario TEXT,
fecha TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (producto_id) REFERENCES productos(id)
ON DELETE CASCADE,
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
ON DELETE CASCADE
);La restricción CHECK garantiza que la puntuación sea un valor válido entre 1 y 5. MySQL 8.0.16 y versiones posteriores aplican esta restricción de forma efectiva.
INSERT INTO resenas (producto_id, cliente_id, puntuacion, comentario) VALUES
(1, 1, 5, 'Excelente teléfono, la cámara es espectacular'),
(2, 2, 4, 'Muy buen móvil, pero la batería podría durar más'),
(1, 4, 4, 'Gran rendimiento, aunque es caro'),
(5, 7, 5, 'El mejor portátil que he tenido, ligero y potente'),
(7, 4, 5, 'Brutal para gaming, los gráficos son increíbles'),
(11, 3, 3, 'Calidad aceptable por el precio'),
(11, 1, 4, 'Buena camiseta, tela suave'),
(20, 5, 5, 'Muy cómodas para correr, buena amortiguación'),
(20, 9, 4, 'Buenas zapatillas, pero tallan un poco grande'),
(25, 3, 5, 'Libro imprescindible para cualquier programador'),
(25, 11, 4, 'Muy útil, aunque algunos ejemplos están algo anticuados'),
(19, 7, 4, 'Buen robot de cocina, muchas funciones'),
(22, 12, 5, 'Las mancuernas perfectas para casa, muy versátiles'),
(17, 8, 4, 'Buen escritorio, el motor es silencioso'),
(28, 15, 5, 'Una obra maestra, nunca me canso de releerla'),
(29, 20, 4, 'Clásico imprescindible'),
(4, 6, 4, 'Buena relación calidad-precio'),
(3, 10, 3, 'Buen móvil pero la interfaz de Google no me convence'),
(9, 1, 5, 'Carga rapidísimo, compatible con todo'),
(23, 5, 4, 'Buena esterilla, no resbala nada');Tenemos 20 reseñas con puntuaciones variadas. Algunos productos tienen varias reseñas y otros no tienen ninguna, lo que será útil para practicar LEFT JOIN (encontrar productos sin reseñas) y funciones de agregación como AVG (calcular la puntuación media).
Tabla etiquetas_producto
Las etiquetas son una forma flexible de clasificar productos más allá de las categorías. Un producto puede tener múltiples etiquetas:
CREATE TABLE etiquetas_producto (
id INT AUTO_INCREMENT PRIMARY KEY,
producto_id INT NOT NULL,
etiqueta VARCHAR(50) NOT NULL,
FOREIGN KEY (producto_id) REFERENCES productos(id)
ON DELETE CASCADE,
UNIQUE KEY uk_producto_etiqueta (producto_id, etiqueta)
);La restricción UNIQUE compuesta sobre producto_id y etiqueta impide que un producto tenga la misma etiqueta duplicada.
INSERT INTO etiquetas_producto (producto_id, etiqueta) VALUES
(1, 'premium'), (1, 'más vendido'), (1, 'novedad'),
(2, 'más vendido'), (2, 'oferta'),
(3, 'oferta'), (3, 'recomendado'),
(4, 'oferta'), (4, 'más vendido'),
(5, 'premium'), (5, 'novedad'),
(7, 'premium'), (7, 'gaming'),
(11, 'básico'), (11, 'más vendido'),
(12, 'deporte'),
(15, 'premium'),
(17, 'novedad'), (17, 'ergonómico'),
(19, 'más vendido'),
(20, 'recomendado'), (20, 'deporte'),
(22, 'fitness'), (22, 'recomendado'),
(25, 'bestseller'), (25, 'recomendado'),
(28, 'clásico'), (28, 'bestseller'),
(29, 'clásico');Script completo
Si prefieres ejecutar todo de una sola vez en lugar de tabla por tabla, puedes guardar todas las sentencias anteriores en un archivo llamado tienda_mysql.sql y ejecutarlo desde la terminal:
mysql -u root -p < tienda_mysql.sqlO desde dentro del cliente MySQL:
SOURCE /ruta/al/archivo/tienda_mysql.sql;Verificar la instalación
Después de ejecutar todas las sentencias, verifica que las tablas se crearon correctamente y que contienen datos:
SHOW TABLES;| Tables_in_tienda_mysql |
|---|
| categorias |
| clientes |
| detalle_pedidos |
| empleados |
| etiquetas_producto |
| pedidos |
| productos |
| resenas |
Para ver cuántos registros tiene cada tabla, ejecuta estas consultas:
SELECT 'categorias' AS tabla, COUNT(*) AS registros FROM categorias
UNION ALL SELECT 'productos', COUNT(*) FROM productos
UNION ALL SELECT 'clientes', COUNT(*) FROM clientes
UNION ALL SELECT 'empleados', COUNT(*) FROM empleados
UNION ALL SELECT 'pedidos', COUNT(*) FROM pedidos
UNION ALL SELECT 'detalle_pedidos', COUNT(*) FROM detalle_pedidos
UNION ALL SELECT 'resenas', COUNT(*) FROM resenas
UNION ALL SELECT 'etiquetas_producto', COUNT(*) FROM etiquetas_producto;| tabla | registros |
|---|---|
| categorias | 16 |
| productos | 30 |
| clientes | 20 |
| empleados | 10 |
| pedidos | 25 |
| detalle_pedidos | 41 |
| resenas | 20 |
| etiquetas_producto | 29 |
Si tus resultados coinciden con estos números, la base de datos está correctamente configurada. Prueba una consulta que combine varias tablas para confirmar que las relaciones funcionan:
SELECT
c.nombre AS cliente,
COUNT(p.id) AS total_pedidos,
COALESCE(SUM(p.total), 0) AS gasto_total
FROM clientes c
LEFT JOIN pedidos p ON c.id = p.cliente_id
GROUP BY c.id, c.nombre
ORDER BY gasto_total DESC
LIMIT 5;| cliente | total_pedidos | gasto_total |
|---|---|---|
| María | 3 | 1849.96 |
| Carlos | 2 | 1029.98 |
| Pedro | 2 | 1979.97 |
| Laura | 1 | 179.97 |
| David | 2 | 509.97 |
Con esta base de datos instalada, tienes todo lo necesario para seguir el resto del tutorial. Cada artículo utilizará estas tablas y estos datos para sus ejemplos, de modo que podrás ejecutar cada consulta en tu propio servidor y obtener exactamente los mismos resultados.
En el siguiente artículo comenzaremos con las consultas básicas: la sentencia SELECT.
Escrito por Eduardo Lázaro
