JOIN en SQLite
Entender, dominar y aplicar uniones entre tablas
Las bases de datos relacionales, como SQLite, almacenan información dividida en tablas. Esto es una gran ventaja: hace que los datos estén ordenados, limpios y no se repitan innecesariamente.
Pero esta partición crea una pregunta:
¿Cómo puedo obtener información que está repartida en dos o más tablas diferentes?
La respuesta es: con un JOIN.
¿Qué es realmente un JOIN?
Un JOIN es una operación que permite combinar datos de varias tablas en una sola consulta.
La definición académica dice:
JOIN es una operación relacional que produce un resultado combinando filas de dos tablas cuando sus valores cumplen una condición.
Pero vamos a explicarlo de manera más intuitiva.
1.1 Explicación intuitiva
Imagina dos listas:
- Una lista de clientes
- Una lista de pedidos
Los pedidos incluyen quién los hizo, pero no incluyen el nombre del cliente, solo un “id”.
En forma de tablas:
clients
| id | name |
|---|---|
| 1 | Ana |
| 2 | Luis |
| 3 | Marta |
orders
| id | client_id | total |
|---|---|---|
| 1 | 1 | 49.99 |
| 2 | 1 | 89.90 |
| 3 | 2 | 120.00 |
Si quieres obtener esta información:
Ana hizo un pedido de 49.99
Ana hizo un pedido de 89.90
Luis hizo un pedido de 120.00
Necesitas “cruzar” ambas tablas.
Eso es un JOIN.
1.2 Una frase para recordarlo
JOIN responde a la pregunta: ¿Qué ocurre cuando combino estos datos que están relacionados, pero están en tablas separadas?
Sin JOIN, una base relacional no tiene sentido completo.
¿Por qué existen los JOINs?
Los JOINs existen por tres razones fundamentales:
2.1 Evitar duplicación de datos
Si en cada pedido guardáramos el nombre del cliente, repetiríamos datos cientos de veces:
| id | client_name | total |
|---|---|---|
| 1 | Ana | 49.99 |
| 2 | Ana | 89.90 |
| 3 | Ana | 120.00 |
Esto es malo porque:
- Si Ana cambia de nombre o email → habría que actualizar cientos de filas.
- Ocupa espacio innecesario.
- Provoca inconsistencias.
Con JOINs:
- mantienes los datos limpios
- cada cosa vive donde debe vivir
- y recuperas la información combinada solo cuando la necesitas
2.2 Convertir datos “separados” en información realmente útil
En tu tabla de pedidos aparece:
client_id = 1
Sólo con eso, un humano no sabe quién es.
JOIN te da contexto:
client_id = 1 → Ana
JOIN convierte referencias en información clara.
2.3 Porque así funcionan TODOS los sistemas profesionales
Cualquier sistema real (ERP, CRM, ecommerce, apps de reservas, bancos, redes sociales…) funciona con múltiples tablas relacionadas.
Sin JOIN:
- no puedes hacer informes
- no puedes relacionar entidades
- no puedes construir APIs completas
- no puedes escalar un proyecto
JOIN es la llave que abre toda la potencia del SQL relacional.
¿Cuándo usar un JOIN?
Siempre que necesites una información que está repartida en varias tablas.
Ejemplos reales:
3.1 Ecommerce
- “Mostrar productos con el nombre de su categoría”
- “Mostrar pedidos con el nombre del cliente”
- “Calcular la suma total de pedidos por cliente”
3.2 Gestión de empleados
- “Mostrar empleados junto a su departamento”
- “Mostrar tareas asignadas a cada empleado”
3.3 Cursos y alumnos
- “Alumnos matriculados en cada curso”
- “Cursos disponibles para un alumno”
3.4 Para el backend:
Cualquier endpoint tipo:
GET /orders → debe devolver información del cliente y del pedido
GET /products → debe incluir categoría
Para estos casos, JOIN no es opcional: es obligatorio.
Tipos de JOIN que necesitas como principiante
Hay muchos tipos de JOIN, pero en SQLite como principiante solo necesitas dominar dos:
- INNER JOIN
- LEFT JOIN
Y con esos dos puedes resolver el 95% de los problemas reales.
Preparación del entorno en DB Browser for SQLite
Vamos a crear un entorno controlado para aprender.
- Abre DB Browser for SQLite.
- Crea una base nueva:
- Menú superior
- Archivo
- Nueva base de datos
- Ponle el nombre
join_tutorial.db - Guarda el archivo
- Ve a la pestaña Ejecutar SQL.
- Asegúrate de tener activadas las claves foráneas añadiendo esto antes de crear las tablas:
PRAGMA foreign_keys = ON;
Ejecuta esta instrucción con el botón Play.
5.1 Crear tablas
En el panel Ejecutar SQL, escribe y ejecuta:
CREATE TABLE clients (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
client_id INTEGER NOT NULL,
total REAL NOT NULL,
FOREIGN KEY (client_id) REFERENCES clients(id)
);
Después de ejecutar, puedes comprobar la estructura en la pestaña Estructura de DB Browser.
5.2 Insertar datos
En la misma pestaña Ejecutar SQL, borra el contenido anterior y pega:
INSERT INTO clients (name)
VALUES ('Ana'), ('Luis'), ('Marta');
INSERT INTO orders (client_id, total)
VALUES
(1, 49.99),
(1, 89.90),
(2, 120.00);
Después de ejecutarlos, revisa los datos en la pestaña Examinar datos.
Perfecto.
Ahora sí vamos a aprender JOIN.
INNER JOIN — unir datos solamente cuando hay coincidencias
INNER JOIN responde a:
“Dame solo las filas que tengan relación en ambas tablas.”
Ejecuta:
SELECT
o.id AS order_id,
c.name AS client_name,
o.total
FROM orders o
INNER JOIN clients c
ON o.client_id = c.id;
Explicación detallada
FROM orders o→ tabla principalINNER JOIN clients c→ tabla que quiero unirON o.client_id = c.id→ regla de unión
El motor hace esto:
- Toma cada pedido de la tabla
orders. - Busca el cliente cuyo
idcoincida. - Si lo encuentra → combina la fila.
- Si NO lo encuentra → descarta esa fila.
Resultado:
| order_id | client_name | total |
|---|---|---|
| 1 | Ana | 49.99 |
| 2 | Ana | 89.90 |
| 3 | Luis | 120.00 |
Observa:
-
Marta no aparece.
No tiene pedidos, así que no hay coincidencias.
INNER JOIN es perfecto para:
- informes de actividad
- pedidos de clientes existentes
- datos “completos”
LEFT JOIN — incluir incluso los que no tienen coincidencia
LEFT JOIN responde a:
“Dame todas las filas de la tabla izquierda, tengan coincidencia o no.”
Ejecuta:
SELECT
c.name AS client,
o.total
FROM clients c
LEFT JOIN orders o
ON c.id = o.client_id;
Explicación detallada
- La tabla izquierda es
clients - Se devolverá siempre cada cliente
- Si tiene pedidos → aparecerán
- Si no tiene → aparecerá con
NULL
Resultado:
| client | total |
|---|---|
| Ana | 49.99 |
| Ana | 89.90 |
| Luis | 120.00 |
| Marta | NULL |
Esto es extremadamente útil para:
- detectar clientes sin actividad
- informes completos
- reportes administrativos que necesitan incluir “todos”
JOIN + WHERE = consultas expresivas
Ejemplos reales:
8.1 Pedidos mayores de 80€
SELECT
c.name,
o.total
FROM clients c
JOIN orders o ON c.id = o.client_id
WHERE o.total > 80;
8.2 Pedidos de Ana
SELECT
o.id,
o.total
FROM orders o
JOIN clients c ON o.client_id = c.id
WHERE c.name = 'Ana';
8.3 Clientes sin pedidos
SELECT
c.name
FROM clients c
LEFT JOIN orders o ON c.id = o.client_id
WHERE o.id IS NULL;
Errores comunes al usar JOIN
Error 1 — No usar alias
Escribir tablas completas hace la consulta ilegible.
Error 2 — Unir por columnas incorrectas
Hay que unir por las claves relacionadas, no por columnas arbitrarias.
Error 3 — No especificar ON
Sin ON, el JOIN no sabe qué unir.
Error 4 — Usar SELECT * en JOIN complejos
Es difícil distinguir columnas.
Buenas prácticas
- Usa alias cortos:
clients c,orders o - Escribe el ON debajo del JOIN para legibilidad
- Selecciona solo columnas necesarias
- Siempre revisa los NULL en LEFT JOIN
- Haz SELECT antes de consultas complejas para entender los datos
EJERCICIO FINAL
Vamos a realizar un ejercicio realista, paso a paso.
Paso 1 — Crear base
- Abre DB Browser for SQLite.
- Crea una base de datos nueva:
- Archivo
- Nueva base de datos
- Escríbela como
join_ejercicio.db - Guarda
- Ve a la pestaña Ejecutar SQL.
- Activa claves foráneas ejecutando:
PRAGMA foreign_keys = ON;
Paso 2 — Crear tablas
En la pestaña Ejecutar SQL, pega y ejecuta:
CREATE TABLE authors (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
author_id INTEGER,
FOREIGN KEY (author_id) REFERENCES authors(id)
);
Puedes comprobar la estructura en la pestaña Estructura.
Paso 3 — Insertar datos
Borra el contenido del panel SQL para evitar confusión y pega:
INSERT INTO authors (name)
VALUES ('Cervantes'), ('Orwell'), ('Márquez');
INSERT INTO books (title, author_id)
VALUES
('El Quijote', 1),
('1984', 2),
('Cien Años de Soledad', 3),
('Libro huérfano', NULL);
Revisa los datos en Examinar datos.
Paso 4 — Obtener libro + autor
SELECT
b.title,
a.name AS author
FROM books b
LEFT JOIN authors a ON b.author_id = a.id;
Deberías ver:
| title | author |
|---|---|
| El Quijote | Cervantes |
| 1984 | Orwell |
| Cien Años de Soledad | Márquez |
| Libro huérfano | NULL |
Paso 5 — Obtener solo libros con autor
SELECT
b.title,
a.name
FROM books b
JOIN authors a ON b.author_id = a.id;
Paso 6 — Mostrar autores sin libros
SELECT
a.name
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE b.id IS NULL;