SQL: guía completa desde cero
SQL (Structured Query Language) es un lenguaje de consulta de datos estandarizado, diseñado específicamente para gestionar y manipular bases de datos relacionales. Es la herramienta fundamental para almacenar y recuperar datos, actualizar información, administrar bases de datos y establecer relaciones entre distintos conjuntos de datos.
SQL se ha convertido en el estándar internacional para el manejo de bases de datos relacionales, siendo utilizado por la mayoría de los sistemas de gestión de bases de datos (SGBD) como MySQL, PostgreSQL, SQL Server, Oracle, entre otros.
Repositorio de práctica: data_engineering_modern_stack
Estructura básica de SQL
Los comandos SQL se categorizan según su función:
- DDL (Data Definition Language) para definir estructuras:
CREATE: crear nuevos objetos (tablas, bases de datos, vistas)ALTER: modificar la estructura de objetos existentesDROP: eliminar objetos de la base de datosTRUNCATE: eliminar todos los registros de una tablaRENAME: cambiar el nombre de un objeto
- DML (Data Manipulation Language) para manipular datos:
INSERT: agregar nuevos registros a una tablaUPDATE: modificar registros existentesDELETE: eliminar registros de una tabla
- DQL (Data Query Language) para consultas:
SELECT: recuperar datos de una o más tablasFROM: especificar las tablas de origenWHERE: filtrar registros según condicionesGROUP BY: agrupar registrosHAVING: filtrar grupos de registrosORDER BY: ordenar resultados
Esta estructura modular permite comenzar con operaciones básicas y avanzar gradualmente hacia funcionalidades más complejas.

El orden habitual de una consulta con agregación y join sigue esta estructura:

Tipos de datos en SQL
Los tipos de datos son fundamentales para definir qué clase de información puede almacenar cada columna en una tabla:
- Numéricos:
INTEGER,DECIMAL,FLOATpara números enteros y decimales. - Texto:
VARCHAR,CHAR,TEXTpara cadenas de caracteres. - Fecha/Hora:
DATE,TIME,TIMESTAMPpara valores temporales. - Booleanos:
BOOLEANpara valores verdadero/falso. - Especiales:
BLOB,JSON,XMLpara datos más complejos.
¿Qué son las bases de datos?
Una base de datos es una colección organizada de información estructurada, almacenada y gestionada de manera sistemática. Permite almacenar grandes cantidades de datos de forma ordenada, acceder rápidamente a la información necesaria, mantener su integridad y seguridad, y actualizarla de manera eficiente.
En el contexto empresarial son fundamentales para la gestión de inventarios, el registro de transacciones, la información de clientes, recursos humanos y el análisis de datos en general.
Esquemas: son contenedores lógicos (como carpetas) que agrupan objetos relacionados (tablas, vistas, etc). Ayudan a organizar y separar los datos por áreas o funciones.
Tablas: son estructuras fundamentales que almacenan los datos en forma de filas (registros) y columnas (campos), como si fuera un Excel. Cada tabla representa una entidad específica, formada por:
- Columnas: definen los atributos de la entidad, cada una con un tipo de dato específico.
- Filas: contienen los registros individuales, donde cada fila representa una instancia única de la entidad.

Primary Keys vs. Foreign Keys
Las claves primarias (PK) y foráneas (FK) son fundamentales para establecer relaciones entre tablas:
- Primary Keys (PK): identifican de manera única cada registro en una tabla, como un DNI para cada persona. No pueden contener valores duplicados ni nulos.
- Foreign Keys (FK): son referencias a primary keys de otras tablas, creando conexiones entre ellas. Por ejemplo, en una tabla de pedidos, el ID del cliente sería una FK que se relaciona con la PK de la tabla de clientes.

Las características principales de las bases de datos relacionales son:
- Estructura tabular: los datos se almacenan en tablas con filas y columnas.
- Claves primarias y foráneas: permiten establecer relaciones entre tablas.
- Integridad referencial: mantiene la consistencia entre las relaciones de las tablas.
- Normalización: organiza los datos de manera eficiente para evitar redundancia.
Algunos ejemplos comunes son PostgreSQL, MySQL, Oracle y SQL Server, cada uno con sus propias características y ventajas.
Tipos de bases de datos
- Relacionales (SQL): organizan datos en tablas con relaciones predefinidas. Ejemplos: PostgreSQL, MySQL, Oracle Database, SQL Server, SQLite.
- NoSQL: almacenan datos sin estructura fija, ideal para datos no estructurados y escalabilidad.
- Clave-valor: pares de clave-valor, ideales para caché y configuraciones simples (Redis, DynamoDB, Cassandra).
- Orientadas a grafos: especializadas en relaciones complejas entre datos (Neo4j, Amazon Neptune, ArangoDB).
- En memoria: acceso ultrarrápido desde RAM (Redis, Memcached, Apache Ignite).
- Familia de columnas: optimizadas para el análisis de grandes volúmenes de datos (Cassandra, HBase, Redshift).
- Series temporales: diseñadas para datos secuenciales con marca de tiempo (InfluxDB, TimescaleDB, Prometheus).
Ejemplos prácticos
Crear y gestionar tablas
CREATE TABLE permite crear nuevas tablas definiendo su estructura, columnas y tipos de datos:
-- listings: alojamientos o propiedades publicadas en Airbnb.
CREATE TABLE listings (
id INTEGER PRIMARY KEY,
name VARCHAR(100),
host_id INTEGER,
price DECIMAL(10, 2),
location VARCHAR(50),
availability INTEGER
);
-- users: usuarios registrados en Airbnb que reservan alojamientos.
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100),
join_date DATE
);
-- reservations: reservas de alojamientos realizadas por los usuarios.
CREATE TABLE reservations (
id INTEGER PRIMARY KEY,
listing_id INTEGER,
user_id INTEGER,
start_date DATE,
end_date DATE,
total_price DECIMAL(10, 2),
FOREIGN KEY (listing_id) REFERENCES listings(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
ALTER TABLE modifica la estructura de una tabla existente:
-- Agregar una columna
ALTER TABLE listings ADD COLUMN rating DECIMAL(3, 2);
DROP TABLE elimina completamente una tabla y todos sus datos:
DROP TABLE listings;
Manipulación de datos
INSERT agrega nuevos registros a una tabla:
-- Insertar un único registro
INSERT INTO listings (id, name, host_id, price, location, availability)
VALUES (1, 'Cozy Apartment Downtown', 101, 120.00, 'New York', 30);
-- Insertar múltiples registros
INSERT INTO listings (id, name, host_id, price, location, availability)
VALUES
(2, 'Beach House', 102, 200.00, 'Los Angeles', 15),
(3, 'Mountain Cabin', 103, 150.00, 'Denver', 60);
UPDATE modifica registros existentes:
-- Actualizar el precio de un alojamiento
UPDATE listings
SET price = 130.00
WHERE id = 1;
-- Actualizar múltiples campos
UPDATE listings
SET availability = availability - 5,
price = price * 1.05
WHERE location = 'New York';
DELETE elimina registros de una tabla:
-- Eliminar un registro específico
DELETE FROM listings
WHERE location = 'Denver';
-- Eliminar todos los registros
DELETE FROM listings;
Consultas básicas
SELECT permite recuperar datos específicos de una tabla:
SELECT name, price, location
FROM listings;
WHERE permite establecer condiciones para filtrar registros:
SELECT name, price
FROM listings
WHERE price > 150;
Los operadores condicionales y lógicos permiten crear filtros más complejos usando AND, OR, IN, BETWEEN, etc.:
SELECT name, location
FROM listings
WHERE availability BETWEEN 10 AND 50
AND location IN ('New York', 'Los Angeles');
ORDER BY permite ordenar los resultados según una o más columnas:
SELECT name, price
FROM listings
ORDER BY price DESC;
LIMIT permite especificar el número máximo de registros a retornar:
SELECT name, price
FROM listings
ORDER BY price DESC
LIMIT 3;
Consultas avanzadas
Funciones de cadenas de texto
-- Unir el nombre del alojamiento con la ubicación
SELECT CONCAT(name, ' - ', location) AS full_listing_name
FROM listings;
-- Convertir el nombre del alojamiento a mayúsculas
SELECT UPPER(name) AS name_uppercase
FROM listings;
-- Calcular la longitud del nombre del alojamiento
SELECT name, LENGTH(name) AS name_length
FROM listings;
Funciones de fecha y hora
-- Calcular cuántos años lleva listado el alojamiento
SELECT name,
DATE_PART('year', AGE(CURRENT_DATE, creation_date)) AS years_listed
FROM listings;
-- Extraer el mes de la fecha de creación
SELECT name,
EXTRACT(MONTH FROM creation_date) AS month_listed
FROM listings;
-- Calcular los días restantes de disponibilidad hasta una fecha específica
SELECT name,
availability,
CURRENT_DATE + availability AS last_available_date
FROM listings;
Funciones de agregación
-- Total de alojamientos por ubicación
SELECT location, COUNT(*) AS total_listings
FROM listings
GROUP BY location;
-- Precio promedio por ubicación
SELECT location, AVG(price) AS avg_price
FROM listings
GROUP BY location;
-- Suma total de noches disponibles en todos los alojamientos
SELECT SUM(availability) AS total_nights_available
FROM listings;
-- Precio más alto y más bajo
SELECT MAX(price) AS max_price,
MIN(price) AS min_price
FROM listings;
Combinado con HAVING es posible filtrar los grupos resultantes de un GROUP BY, y con JOIN (INNER, LEFT, RIGHT, FULL OUTER) es posible combinar información de varias tablas relacionadas mediante sus claves primarias y foráneas.
Postgres
PostgreSQL es un sistema de gestión de bases de datos relacional (RDBMS) de código abierto y orientado a objetos. Desarrollado originalmente en la Universidad de California en Berkeley, se ha convertido en uno de los sistemas de bases de datos más populares y confiables del mundo.
Características principales:
- Soporte completo para ACID (Atomicidad, Consistencia, Aislamiento, Durabilidad)
- Capacidades avanzadas de replicación y alta disponibilidad
- Extensibilidad mediante funciones y procedimientos almacenados en múltiples lenguajes
- Soporte nativo para tipos de datos JSON y XML
- Herencia de tablas y particionamiento
- Funciones de ventana y CTEs (Common Table Expressions)
- Potente sistema de índices (B-tree, Hash, GiST, SP-GiST, GIN, BRIN)
Ventajas: gratuito y de código abierto, excelente cumplimiento de estándares SQL, gran capacidad de extensión, sólido soporte para transacciones concurrentes y comunidad activa.
Desventajas: puede ser más lento en operaciones simples comparado con MySQL, mayor consumo de recursos en configuraciones por defecto y curva de aprendizaje más pronunciada para administración.
Tip: la elección del RDBMS adecuado depende de tus necesidades específicas, presupuesto y casos de uso. PostgreSQL destaca especialmente en aplicaciones que requieren integridad de datos, operaciones complejas y extensibilidad.
Instalación de PostgreSQL y pgAdmin con Docker
# Descargar la imagen de PostgreSQL
docker pull postgres:latest
# Crear y ejecutar el contenedor
docker run --name postgres-db -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres
# Descargar la imagen de pgAdmin
docker pull dpage/pgadmin4
# Crear y ejecutar el contenedor
docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=usuario@dominio.com -e PGADMIN_DEFAULT_PASSWORD=admin -p 80:80 -d dpage/pgadmin4
También se pueden gestionar ambos servicios con Docker Compose:
version: '3.8'
services:
postgres:
image: postgres:latest
container_name: postgres-db
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: usuario@dominio.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "80:80"
depends_on:
- postgres
volumes:
postgres-data:
docker-compose up -d
Para conectar pgAdmin con PostgreSQL: accedé a http://localhost:80, iniciá sesión con las credenciales configuradas y creá un nuevo servidor con host postgres (si usás Docker Compose) o localhost, puerto 5432, usuario postgres y la contraseña configurada.
Tip: cambiá las contraseñas por unas seguras en producción y nunca compartas credenciales en control de versiones.
SQLite
SQLite es un sistema de gestión de bases de datos relacional contenido en una biblioteca C. A diferencia de otros sistemas, no es un motor cliente-servidor, sino que se integra directamente en el programa final.
Características principales:
- Base de datos contenida en un único archivo
- No requiere configuración ni servidor
- Transaccional (ACID)
- Alta portabilidad
- Código abierto
- Sin necesidad de administración
Ventajas: configuración cero, portátil (toda la base de datos en un solo archivo), excelente para aplicaciones embebidas, ideal para desarrollo y pruebas, no requiere un proceso servidor separado y mínimo uso de recursos.
Desventajas: no adecuado para aplicaciones multiusuario grandes, limitaciones en la concurrencia, sin gestión de usuarios nativa y limitaciones en el tamaño de la base de datos.
| Característica | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| Tipo | Archivo único | Cliente-Servidor | Cliente-Servidor |
| Configuración | Ninguna | Alta | Alta |
| Escalabilidad | Baja | Alta | Alta |
| Uso ideal | Apps locales, desarrollo, pruebas | Apps empresariales | Apps web medianas |
Instalación
Para más información consultá la guía de sqlite.org.
macOS:
# Usando Homebrew
brew install sqlite3
# Verificar instalación
sqlite3 --version
Linux:
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install sqlite3
# Fedora
sudo dnf install sqlite
Windows: descargá el paquete precompilado desde sqlite.org, extraé los archivos, agregá la ruta al PATH del sistema e instalá DB Browser for SQLite si querés una interfaz gráfica.
Uso básico
-- Crear una nueva base de datos
sqlite3 mydatabase.db
-- Crear una tabla
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
);
-- Insertar datos
INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com');
-- Consultar datos
SELECT * FROM users;
-- Salir
.quit
Comandos útiles: .tables (muestra todas las tablas), .schema (muestra el esquema), .mode column (formatea la salida en columnas), .headers on (muestra encabezados) y .backup [archivo] (crea una copia de seguridad).
Mejores prácticas
Tip: usá transacciones para operaciones múltiples. Garantizan que varias operaciones se completen como una unidad atómica.
BEGIN TRANSACTION;
INSERT INTO usuarios (nombre) VALUES ('Juan');
UPDATE saldo SET cantidad = cantidad - 100 WHERE usuario_id = 1;
COMMIT;
Tip: implementá índices para las consultas frecuentes. Mejoran significativamente el rendimiento de las búsquedas.
-- Crear un índice en la columna email
CREATE INDEX idx_usuarios_email ON usuarios(email);
-- Consulta que se beneficiará del índice
SELECT * FROM usuarios WHERE email = 'juan@example.com';
Las columnas más comunes para crear índices son: claves primarias (automáticamente indexadas en SQLite), columnas de búsqueda frecuente, claves foráneas (para optimizar JOINs) y columnas usadas en ORDER BY o WHERE. Es importante no sobre-indexar, ya que cada índice ocupa espacio adicional en disco y ralentiza las operaciones de escritura.
Tip: realizá copias de seguridad regulares. SQLite ofrece comandos simples para respaldar la base de datos.
.backup 'backup_20250118.db'
-- O desde la línea de comandos
sqlite3 basededatos.db ".backup 'backup_20250118.db'"
Tip: mantené la base de datos lo más pequeña posible, eliminando datos innecesarios y optimizando el espacio.
-- Vaciar espacio no utilizado
VACUUM;
-- Eliminar registros antiguos
DELETE FROM tabla WHERE fecha < date('now', '-30 days');
Tip: usá sentencias preparadas para consultas parametrizadas. Previenen inyección SQL y mejoran el rendimiento.
# Ejemplo en Python con sqlite3
cursor.execute("""
INSERT INTO usuarios (nombre, email)
VALUES (?, ?)
""", ('Juan', 'juan@example.com'))
SQLite es una excelente opción para aplicaciones pequeñas, ambientes de desarrollo y pruebas, y cualquier situación donde la simplicidad y la portabilidad sean prioritarias sobre la escalabilidad y las características avanzadas de los sistemas de bases de datos más grandes.
