Aprende MySQL y PostgreSQL desde cero: Instalación, consultas avanzadas, Docker y mejores prácticas
Introducción a las Bases de Datos Relacionales
Las bases de datos relacionales son sistemas que permiten almacenar, organizar y recuperar información de manera estructurada utilizando tablas, filas y columnas. Se basan en el modelo relacional propuesto por E.F. Codd en 1970, y utilizan SQL (Structured Query Language) como lenguaje estándar para interactuar con los datos.
MySQL y PostgreSQL son dos de los SGBD (Sistemas de Gestión de Bases de Datos) más populares del mundo. MySQL es ampliamente utilizado en aplicaciones web como WordPress, mientras que PostgreSQL destaca en entornos empresariales por su robustez y soporte para datos complejos.
💡 Historia Rápida: MySQL fue creado en 1995 por MySQL AB (ahora parte de Oracle), y PostgreSQL en 1986 como un proyecto académico en la Universidad de California, Berkeley.
¿Qué aprenderás en este curso?
Instalación y configuración en distribuciones Linux comunes (Ubuntu, CentOS)
Comandos básicos y avanzados para gestión de datos
Creación, modificación y eliminación de bases de datos y tablas
Consultas avanzadas: JOINs, subconsultas, agregaciones y funciones de ventana
Índices, transacciones, vistas y optimización de rendimiento
Gestión de usuarios, permisos y seguridad
Backups, restauración y replicación básica
Mejores prácticas para entornos de producción con Docker
Requisitos Previos
Conocimientos básicos de Linux (comandos de terminal) y conceptos de programación. No se requiere experiencia previa en bases de datos.
🐬 MySQL
Sistema de gestión de bases de datos relacional de código abierto. Es rápido, confiable y fácil de usar. Ideal para aplicaciones web de alto tráfico. Soporta motores de almacenamiento como InnoDB para transacciones ACID.
🐘 PostgreSQL
Sistema de base de datos objeto-relacional avanzado. Conocido por su robustez, cumplimiento estricto de estándares SQL y características avanzadas como soporte nativo para JSON, GIS y extensiones personalizadas.
🔧 Herramientas Recomendadas: Usa DBeaver o pgAdmin para interfaces gráficas, o la terminal para práctica pura.
Este comando te guiará para configurar la contraseña root, eliminar usuarios anónimos, deshabilitar login root remoto y eliminar la base de datos de prueba.
💡 Consejo: Edita el archivo de configuración en /etc/mysql/my.cnf para ajustar límites como max_connections o innodb_buffer_pool_size.
3. Acceder a MySQL
mysql -u root -p
Ingresa la contraseña cuando se solicite. Para conexiones remotas, usa -h hostname.
4. Comandos Básicos de MySQL
Gestión de Bases de Datos
-- Mostrar todas las bases de datos SHOW DATABASES;
-- Crear una base de datos con codificación UTF-8 CREATE DATABASE mi_tienda CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Usar una base de datos USE mi_tienda;
-- Eliminar una base de datos (¡cuidado!) DROP DATABASE IF EXISTS mi_tienda;
Gestión de Tablas
-- Crear una tabla con restricciones CREATE TABLE productos ( id INT AUTO_INCREMENT PRIMARY KEY, nombre VARCHAR(100) NOT NULL, precio DECIMAL(10,2) CHECK (precio > 0), stock INT DEFAULT 0 CHECK (stock >= 0), fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_nombre (nombre) ) ENGINE=InnoDB;
-- Mostrar todas las tablas SHOW TABLES;
-- Ver estructura detallada DESCRIBE productos; SHOW CREATE TABLE productos;
-- Eliminar una tabla DROP TABLE IF EXISTS productos;
-- SELECT con filtros y ordenamiento SELECT * FROM productos ORDER BY precio DESC LIMIT 5; SELECT nombre, precio FROM productos WHERE precio BETWEEN 50 AND 200 AND stock > 10;
-- UPDATE condicional UPDATE productos SET precio = precio * 1.1, stock = stock - 1 WHERE id IN (1, 3);
-- DELETE con confirmación DELETE FROM productos WHERE stock = 0;
5. Consultas Avanzadas
JOINs y Subconsultas
-- Supongamos una tabla categorías (id, nombre) -- INSERT INTO categorias VALUES (1, 'Electrónicos'), (2, 'Accesorios'); -- Agregar foreign key: ALTER TABLE productos ADD categoria_id INT; UPDATE productos SET categoria_id=1 WHERE id=1;
-- INNER JOIN SELECT p.nombre, c.nombre AS categoria FROM productos p INNER JOIN categorias c ON p.categoria_id = c.id;
-- Subconsulta SELECT * FROM productos WHERE precio > (SELECT AVG(precio) FROM productos);
Agregaciones y Funciones
-- GROUP BY y HAVING SELECT categoria_id, COUNT(*) AS total_productos, AVG(precio) AS precio_promedio FROM productos GROUP BY categoria_id HAVING COUNT(*) > 1;
-- Funciones de fecha SELECT nombre, DATEDIFF(CURDATE(), fecha_creacion) AS dias_desde_creacion FROM productos;
Ejemplos Avanzados
-- Consulta con CTE (Common Table Expression) para ranking de productos por ventas WITH ventas_ranking AS ( SELECT p.id, SUM(v.cantidad) AS total_vendidas FROM productos p JOIN ventas v ON p.id = v.producto_id GROUP BY p.id ) SELECT p.nombre, vr.total_vendidas, RANK() OVER (ORDER BY vr.total_vendidas DESC) AS rank FROM productos p JOIN ventas_ranking vr ON p.id = vr.id;
-- Consulta recursiva para jerarquía de categorías (si tienes tabla con parent_id) WITH RECURSIVE categoria_hierarchy AS ( SELECT id, nombre, parent_id, 0 AS level FROM categorias WHERE parent_id IS NULL UNION ALL SELECT c.id, c.nombre, c.parent_id, ch.level + 1 FROM categorias c JOIN categoria_hierarchy ch ON c.parent_id = ch.id ) SELECT * FROM categoria_hierarchy ORDER BY level, nombre;
-- Pivot table para ventas por mes SELECT YEAR(fecha) AS año, SUM(CASE WHEN MONTH(fecha) = 1 THEN total ELSE 0 END) AS enero, SUM(CASE WHEN MONTH(fecha) = 2 THEN total ELSE 0 END) AS febrero FROM ventas GROUP BY YEAR(fecha);
Estas consultas avanzadas usan CTEs para lógica compleja, recursión para jerarquías y pivoting para reportes. Prueba en tu entorno.
6. Índices y Optimización
-- Crear índice CREATE INDEX idx_precio ON productos(precio);
-- Ver índices SHOW INDEX FROM productos;
-- Analizar consulta EXPLAIN SELECT * FROM productos WHERE precio > 100;
Los índices mejoran la velocidad de las consultas, pero ralentizan las inserciones. Usa EXPLAIN para optimizar.
7. Transacciones y Vistas
-- Transacción START TRANSACTION; UPDATE productos SET stock = stock - 1 WHERE id = 1; INSERT INTO ventas (producto_id, cantidad) VALUES (1, 1); COMMIT; -- o ROLLBACK si hay error
-- Crear vista CREATE VIEW productos_baratos AS SELECT * FROM productos WHERE precio < 100;
-- Crear usuario con host específico CREATE USER 'usuario'@'localhost' IDENTIFIED BY 'contraseña_segura'; CREATE USER 'app_user'@'%' IDENTIFIED BY 'app_pass';
-- Permisos granulares GRANT SELECT, INSERT ON mi_tienda.productos TO 'usuario'@'localhost'; GRANT ALL PRIVILEGES ON mi_tienda.* TO 'app_user'@'%'; REVOKE DELETE ON mi_tienda.* FROM 'usuario'@'localhost';
-- Aplicar y verificar FLUSH PRIVILEGES; SHOW GRANTS FOR 'usuario'@'localhost';
-- Eliminar usuario DROP USER 'usuario'@'localhost';
-- SELECT avanzado SELECT * FROM productos ORDER BY precio DESC LIMIT 5; SELECT nombre, precio FROM productos WHERE precio BETWEEN 50 AND 200 AND stock > 10;
-- UPDATE con RETURNING UPDATE productos SET precio = precio * 1.1 WHERE id = 1 RETURNING *;
-- DELETE DELETE FROM productos WHERE stock = 0 RETURNING *;
5. Consultas Avanzadas
JOINs y Subconsultas
-- FULL OUTER JOIN (PostgreSQL lo soporta nativo) SELECT p.nombre, c.nombre AS categoria FROM productos p FULL OUTER JOIN categorias c ON p.categoria_id = c.id;
-- Subconsulta con EXISTS SELECT * FROM productos p WHERE EXISTS (SELECT 1 FROM ventas v WHERE v.producto_id = p.id);
Agregaciones y Funciones de Ventana
-- GROUP BY con ROLLUP SELECT categoria_id, SUM(stock) AS total_stock FROM productos GROUP BY ROLLUP(categoria_id);
-- Ventana para ranking SELECT nombre, precio, RANK() OVER (ORDER BY precio DESC) AS ranking FROM productos;
-- Diferencia de fechas SELECT nombre, AGE(CURRENT_DATE, fecha_creacion) AS antiguedad FROM productos;
Ejemplos Avanzados
-- Consulta con CTE para análisis de ventas por región WITH ventas_regionales AS ( SELECT r.nombre AS region, SUM(v.total) AS total_ventas FROM regiones r JOIN ventas v ON r.id = v.region_id GROUP BY r.nombre ) SELECT * FROM ventas_regionales WHERE total_ventas > (SELECT AVG(total_ventas) FROM ventas_regionales);
-- Recursión para árbol de empleados (tabla employees con manager_id) WITH RECURSIVE employee_tree AS ( SELECT id, nombre, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.nombre, e.manager_id, et.level + 1 FROM employees e JOIN employee_tree et ON e.manager_id = et.id ) SELECT * FROM employee_tree ORDER BY level, nombre;
-- Full-text search con ranking SELECT nombre, ts_rank(to_tsvector('spanish', descripcion), query) AS rank FROM productos, plainto_tsquery('spanish', 'laptop gaming') query WHERE to_tsvector('spanish', descripcion) @@ query ORDER BY rank DESC LIMIT 5;
PostgreSQL brilla en consultas avanzadas con soporte nativo para CTEs recursivas y full-text search. Usa extensiones como pg_trgm para trigram search.
-- Insertar y consultar UPDATE productos SET metadatos = '{"color": "negro", "garantia": "2 años"}' WHERE id = 1; SELECT nombre, metadatos->>'color' AS color FROM productos WHERE metadatos ? 'garantia';
Búsqueda Full-Text
-- Crear índice GIN CREATE INDEX idx_fts ON productos USING GIN(to_tsvector('spanish', nombre));
-- Buscar SELECT * FROM productos WHERE to_tsvector('spanish', nombre) @@ to_tsquery('laptop & dell');
-- Monitoreo psql -c "SELECT * FROM pg_stat_activity;"
8. Gestión de Usuarios y Roles
-- Crear rol con atributos CREATE ROLE usuario LOGIN PASSWORD 'contraseña' VALID UNTIL '2026-01-01'; CREATE ROLE admin CREATEDB CREATEROLE LOGIN SUPERUSER PASSWORD 'admin_pass';
-- Permisos granulares GRANT SELECT, INSERT ON productos TO usuario; GRANT USAGE ON SCHEMA public TO usuario; REVOKE DELETE ON productos FROM usuario;
-- Ver roles \du
-- Eliminar DROP ROLE usuario;
9. Transacciones y Vistas Materializadas
-- Transacción con SAVEPOINT BEGIN; UPDATE productos SET stock = stock - 1 WHERE id = 1; SAVEPOINT sp1; INSERT INTO ventas VALUES (1, 1); RELEASE SAVEPOINT sp1; COMMIT;
-- Vista materializada CREATE MATERIALIZED VIEW productos_resumen AS SELECT categoria_id, AVG(precio) AS avg_precio FROM productos GROUP BY categoria_id; REFRESH MATERIALIZED VIEW productos_resumen;
⚠️ Nota: Ambas bases soportan SQL estándar, pero verifica sintaxis específica en documentación oficial.
Ejercicios Prácticos
Practica en un entorno local. Crea un directorio de proyecto y usa scripts SQL para automatizar. Incluye soluciones para MySQL y PostgreSQL donde difieran.
Ejercicio 1: Crear Base de Datos de Tienda en Línea
Crea una base de datos "tienda_online" con tablas: productos (id, nombre, precio, stock, categoria_id), categorias (id, nombre), clientes (id, nombre, email), pedidos (id, cliente_id, fecha, total).
-- MySQL
CREATE DATABASE tienda_online;
USE tienda_online;
CREATE TABLE categorias (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(50) NOT NULL
);
CREATE TABLE productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
precio DECIMAL(10,2),
stock INT,
categoria_id INT,
FOREIGN KEY (categoria_id) REFERENCES categorias(id)
);
CREATE TABLE clientes (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
email VARCHAR(100) UNIQUE
);
CREATE TABLE pedidos (
id INT AUTO_INCREMENT PRIMARY KEY,
cliente_id INT,
fecha DATE DEFAULT CURRENT_DATE,
total DECIMAL(10,2),
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
);
-- PostgreSQL: Cambia AUTO_INCREMENT por SERIAL, CURRENT_DATE por CURRENT_DATE
Ejercicio 2: Insertar Datos y Realizar JOINs
Inserta 5 productos en 2 categorías, 3 clientes y 4 pedidos. Consulta todos los pedidos con detalles de cliente y productos (usa una tabla intermedia si es necesario).
-- Insertar categorías
INSERT INTO categorias (nombre) VALUES ('Electrónicos'), ('Ropa');
-- Insertar pedidos (sin detalles de productos por simplicidad)
INSERT INTO pedidos (cliente_id, total) VALUES (1, 599.99), (2, 79.99), (1, 299.99), (3, 49.99);
-- Consulta JOIN
SELECT p.id, p.fecha, c.nombre AS cliente, p.total FROM pedidos p
JOIN clientes c ON p.cliente_id = c.id ORDER BY p.fecha DESC;
💡 Consejo Final: Experimenta con datos reales. Usa Docker para entornos aislados: docker run --name mysql-db -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 mysql o similar para PostgreSQL. La práctica constante es clave para dominar SQL.
Quizzes Interactivos para Refrescar y Enseñar
Prueba tus conocimientos con estos quizzes divididos por nivel. Cada nivel tiene preguntas específicas. Al final, verás tu puntuación y explicaciones.
Nivel Básico (3 preguntas)
Nivel Intermedio (4 preguntas)
Nivel Avanzado (3 preguntas)
💡 Para enseñar: Copia estos quizzes en Google Forms para tracking de progreso grupal, o úsalos en sesiones en vivo.
Asistente IA - Grok DB Helper
¡Hola! Soy Grok, tu asistente IA para bases de datos. Pregúntame sobre MySQL, PostgreSQL, Docker o SQL avanzado. Ej: "¿Cómo uso CTE en PostgreSQL?"