INNER JOIN (o simplemente JOIN) es el comando de SQL para combinar dos tablas por una clave compartida. El tipo de JOIN más sencillo y más común — con las bases de datos relacionales no llegarás lejos sin él.
Una analogía rápida. Tienes dos tablas:
users — usuarios (id, name)
orders — pedidos (id, user_id, amount)
En orders no hay nombre del comprador — solo user_id. Para obtener parejas de «nombre del comprador + importe del pedido», tienes que volver a pegar estas tablas por una clave común — users.id coincide con orders.user_id. Ese pegado es INNER JOIN.
Por qué importa JOIN
Una base de datos normal reparte los datos entre tablas para evitar la duplicación. El nombre del usuario vive solo en users; orders referencia al usuario mediante user_id. Esto se llama normalización.
Cuando necesitas mostrar «nombre + pedido», tienes que volver a ensamblar los datos — para eso está JOIN. Es uno de los temas más importantes de SQL — sin JOIN no puedes escribir ni una sola consulta del mundo real.
Sintaxis básica
SELECT columns
FROM table_A
INNER JOIN table_B
ON table_A.key = table_B.key;
La palabra INNER es opcional — JOIN sin prefijo significa INNER JOIN. La mayoría del código pone solo JOIN.
Ejemplo: una tienda
Tabla users:
Tabla orders:
| id | user_id | amount | created_at |
|---|
| 10 | 1 | 500 | 2024-03-01 |
| 11 | 1 | 1500 | 2024-03-05 |
| 12 | 2 | 200 | 2024-03-07 |
| 13 | 5 | 3000 | 2024-03-10 |
Consulta — unir y mostrar el nombre del comprador con el importe:
SELECT users.name, orders.amount, orders.created_at
FROM users
INNER JOIN orders
ON users.id = orders.user_id;
Resultado:
| name | amount | created_at |
|---|
| Anna | 500 | 2024-03-01 |
| Anna | 1500 | 2024-03-05 |
| Boris | 200 | 2024-03-07 |
Qué pasó:
- Cada fila de
users se emparejó con cada fila de orders donde users.id = orders.user_id.
- Anna tiene dos pedidos → dos filas en el resultado, con el nombre duplicado.
- Boris tiene un pedido → una fila.
- Vera y Grisha no están — no tienen pedidos. Ese es el rasgo del INNER JOIN: solo parejas con coincidencia en ambas tablas.
- El pedido #13 no está —
user_id = 5 no existe en users. A veces ocurre, p. ej. el usuario se borró pero el pedido se quedó.
Si también quieres los usuarios sin pedidos, eso es LEFT JOIN (artículo aparte).
Alias — nombres cortos de tabla
Escribir users.name, orders.amount cada vez es largo. El código del mundo real usa alias:
SELECT u.name, o.amount, o.created_at
FROM users u
INNER JOIN orders o
ON u.id = o.user_id;
users u significa «dale a la tabla users el alias u». No cambia la lógica — solo es más corto. En consultas grandes con tres o cinco tablas, las consultas sin alias son ilegibles.
Nota: los alias se vuelven obligatorios si ambas tablas tienen una columna con el mismo nombre. De lo contrario la base de datos no puede saber a qué id te refieres.
Unir tres o más tablas
Los JOIN se encadenan: A JOIN B JOIN C. Añade una tabla products:
| id | name | price |
|---|
| 100 | Bosch kettle | 49 |
| 101 | iPhone 15 | 999 |
| 102 | Book "Clean Code" | 22 |
Y amplía orders con product_id:
| id | user_id | product_id | amount |
|---|
| 10 | 1 | 100 | 49 |
| 11 | 1 | 102 | 22 |
| 12 | 2 | 100 | 49 |
Consulta — quién compró qué:
SELECT u.name AS buyer, p.name AS product, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN products p ON p.id = o.product_id;
Resultado:
| buyer | product | amount |
|---|
| Anna | Bosch kettle | 49 |
| Anna | Book "Clean Code" | 22 |
| Boris | Bosch kettle | 49 |
Cómo funciona:
- La base de datos primero une
users y orders por user_id.
- Luego el resultado se une con
products por product_id.
- La salida conserva solo las filas donde ambas uniones coincidieron.
Estas cadenas pueden ser tan largas como necesites. «Usuarios, sus pedidos, los productos de esos pedidos, las categorías de los productos y las tiendas» — eso son cinco JOIN seguidos.
JOIN con WHERE
JOIN suele ir con WHERE — JOIN pega las tablas, WHERE filtra el resultado. P. ej. los pedidos de Anna por encima de 1000:
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.name = 'Anna'
AND o.amount > 1000;
Una combinación extremadamente común — casi cualquier consulta de negocio es «une las tablas + filtra con WHERE + tal vez agrega».
INNER JOIN es la intersección
En términos de teoría de conjuntos: INNER JOIN es la intersección de dos tablas por una clave. Solo las filas presentes en ambas entran.
users orders
┌──┐ ┌──┐
│ │ │ │
│ ●│ ← → │● │ ← parejas: solo estas acaban en el resultado
│ │ │ │
└──┘ └──┘
Un usuario sin pedidos — no está en el resultado. Un pedido sin usuario (clave foránea rota) — tampoco está en el resultado.
Ejemplo más grande: una biblioteca
books:
| id | title | author_id |
|---|
| 1 | War and Peace | 10 |
| 2 | Anna Karenina | 10 |
| 3 | Crime and Punishment | 11 |
| 4 | The Idiot | 11 |
| 5 | (orphan — author not in DB) | 99 |
authors:
| id | full_name | country |
|---|
| 10 | Leo Tolstoy | Russia |
| 11 | Fyodor Dostoevsky | Russia |
| 12 | Erich Maria Remarque | Germany |
Consulta — cada libro con el nombre del autor y el país:
SELECT b.title, a.full_name AS author, a.country
FROM books b
JOIN authors a ON a.id = b.author_id
ORDER BY a.full_name, b.title;
Resultado:
| title | author | country |
|---|
| Anna Karenina | Leo Tolstoy | Russia |
| War and Peace | Leo Tolstoy | Russia |
| Crime and Punishment | Fyodor Dostoevsky | Russia |
| The Idiot | Fyodor Dostoevsky | Russia |
El libro huérfano desapareció — author_id = 99 no está en authors. Remarque desapareció — tiene cero libros en nuestra books. INNER JOIN descartó ambos.
Errores habituales de principiante
1. Olvidar la cláusula ON. Escribir JOIN orders sin ON — la mayoría de las bases de datos o dan error o recurren a un CROSS JOIN (producto cartesiano, cada fila por cada fila). O un error o una consulta muy lenta.
2. Columnas equivocadas en ON. ON u.id = o.id en lugar de ON u.id = o.user_id es una errata clásica. La consulta no se romperá — devolverá un sinsentido (el usuario #5 emparejado con el pedido #5, lo cual no tiene significado).
3. Nombres de columna ambiguos. Si tanto users como orders tienen id, y escribes SELECT id, name FROM users JOIN orders ON …, obtienes «la referencia a la columna "id" es ambigua». Usa users.id o alias.
4. Filas duplicadas en el resultado. Si un usuario tiene 5 pedidos, el resultado tiene 5 filas con el nombre de ese usuario repetido. No es un error — así funciona JOIN. ¿Quieres usuarios únicos? Envuélvelo en SELECT DISTINCT u.name o combínalo con GROUP BY.
5. Pensar que JOIN en sí es lento. Los JOIN son rápidos cuando hay índices en las columnas de unión. Por eso las columnas de clave foránea (user_id y similares) siempre están indexadas en producción.
6. Uniones antiguas con coma. Técnicamente puedes escribir FROM users, orders WHERE users.id = orders.user_id. No lo hagas. La sintaxis moderna JOIN ... ON ... se lee mejor y separa con claridad la unión del filtrado.
Resumen rápido
INNER JOIN combina dos tablas por una clave compartida.
- Solo las parejas con coincidencia en ambas tablas entran en el resultado.
- La condición de unión va después de
ON.
- La palabra
INNER es opcional; la gente escribe simplemente JOIN.
- Puedes encadenar muchos JOIN (las consultas de tres y cinco tablas son normales).
- Usa alias (
users u) para mantener las cosas legibles.
- Si también quieres usuarios sin pedidos, eso es
LEFT JOIN, un escenario distinto.
Pruébalo tú
Sobre las tablas users y orders de arriba:
- Lista parejas de nombre del comprador e importe, ordenadas por importe descendente.
- Encuentra todos los usuarios que hicieron un pedido en marzo de 2024.
- Cuenta el importe total de pedidos por usuario (combinación
JOIN + GROUP BY + SUM).
Cuando JOIN te encaja, es un momento liberador al aprender SQL. Antes — puedes leer una tabla. Después — los datos del mundo real se abren del todo.
INNER JOIN(o simplementeJOIN) es el comando de SQL para combinar dos tablas por una clave compartida. El tipo de JOIN más sencillo y más común — con las bases de datos relacionales no llegarás lejos sin él.Una analogía rápida. Tienes dos tablas:
users— usuarios (id, name)orders— pedidos (id, user_id, amount)En
ordersno hay nombre del comprador — solouser_id. Para obtener parejas de «nombre del comprador + importe del pedido», tienes que volver a pegar estas tablas por una clave común —users.idcoincide conorders.user_id. Ese pegado esINNER JOIN.Por qué importa JOIN
Una base de datos normal reparte los datos entre tablas para evitar la duplicación. El nombre del usuario vive solo en
users;ordersreferencia al usuario medianteuser_id. Esto se llama normalización.Cuando necesitas mostrar «nombre + pedido», tienes que volver a ensamblar los datos — para eso está JOIN. Es uno de los temas más importantes de SQL — sin JOIN no puedes escribir ni una sola consulta del mundo real.
Sintaxis básica
SELECT columns FROM table_A INNER JOIN table_B ON table_A.key = table_B.key;La palabra
INNERes opcional —JOINsin prefijo significaINNER JOIN. La mayoría del código pone soloJOIN.Ejemplo: una tienda
Tabla
users:Tabla
orders:Consulta — unir y mostrar el nombre del comprador con el importe:
SELECT users.name, orders.amount, orders.created_at FROM users INNER JOIN orders ON users.id = orders.user_id;Resultado:
Qué pasó:
usersse emparejó con cada fila deordersdondeusers.id = orders.user_id.user_id = 5no existe enusers. A veces ocurre, p. ej. el usuario se borró pero el pedido se quedó.Si también quieres los usuarios sin pedidos, eso es
LEFT JOIN(artículo aparte).Alias — nombres cortos de tabla
Escribir
users.name,orders.amountcada vez es largo. El código del mundo real usa alias:SELECT u.name, o.amount, o.created_at FROM users u INNER JOIN orders o ON u.id = o.user_id;users usignifica «dale a la tablausersel aliasu». No cambia la lógica — solo es más corto. En consultas grandes con tres o cinco tablas, las consultas sin alias son ilegibles.Nota: los alias se vuelven obligatorios si ambas tablas tienen una columna con el mismo nombre. De lo contrario la base de datos no puede saber a qué
idte refieres.Unir tres o más tablas
Los JOIN se encadenan: A JOIN B JOIN C. Añade una tabla
products:Y amplía
ordersconproduct_id:Consulta — quién compró qué:
SELECT u.name AS buyer, p.name AS product, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id INNER JOIN products p ON p.id = o.product_id;Resultado:
Cómo funciona:
usersyordersporuser_id.productsporproduct_id.Estas cadenas pueden ser tan largas como necesites. «Usuarios, sus pedidos, los productos de esos pedidos, las categorías de los productos y las tiendas» — eso son cinco JOIN seguidos.
JOIN con WHERE
JOINsuele ir conWHERE— JOIN pega las tablas, WHERE filtra el resultado. P. ej. los pedidos de Anna por encima de 1000:SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.name = 'Anna' AND o.amount > 1000;Una combinación extremadamente común — casi cualquier consulta de negocio es «une las tablas + filtra con WHERE + tal vez agrega».
INNER JOIN es la intersección
En términos de teoría de conjuntos: INNER JOIN es la intersección de dos tablas por una clave. Solo las filas presentes en ambas entran.
Un usuario sin pedidos — no está en el resultado. Un pedido sin usuario (clave foránea rota) — tampoco está en el resultado.
Ejemplo más grande: una biblioteca
books:authors:Consulta — cada libro con el nombre del autor y el país:
SELECT b.title, a.full_name AS author, a.country FROM books b JOIN authors a ON a.id = b.author_id ORDER BY a.full_name, b.title;Resultado:
El libro huérfano desapareció —
author_id = 99no está enauthors. Remarque desapareció — tiene cero libros en nuestrabooks. INNER JOIN descartó ambos.Errores habituales de principiante
1. Olvidar la cláusula ON. Escribir
JOIN orderssinON— la mayoría de las bases de datos o dan error o recurren a un CROSS JOIN (producto cartesiano, cada fila por cada fila). O un error o una consulta muy lenta.2. Columnas equivocadas en ON.
ON u.id = o.iden lugar deON u.id = o.user_ides una errata clásica. La consulta no se romperá — devolverá un sinsentido (el usuario #5 emparejado con el pedido #5, lo cual no tiene significado).3. Nombres de columna ambiguos. Si tanto
userscomoorderstienenid, y escribesSELECT id, name FROM users JOIN orders ON …, obtienes «la referencia a la columna "id" es ambigua». Usausers.ido alias.4. Filas duplicadas en el resultado. Si un usuario tiene 5 pedidos, el resultado tiene 5 filas con el nombre de ese usuario repetido. No es un error — así funciona JOIN. ¿Quieres usuarios únicos? Envuélvelo en
SELECT DISTINCT u.nameo combínalo conGROUP BY.5. Pensar que JOIN en sí es lento. Los JOIN son rápidos cuando hay índices en las columnas de unión. Por eso las columnas de clave foránea (
user_idy similares) siempre están indexadas en producción.6. Uniones antiguas con coma. Técnicamente puedes escribir
FROM users, orders WHERE users.id = orders.user_id. No lo hagas. La sintaxis modernaJOIN ... ON ...se lee mejor y separa con claridad la unión del filtrado.Resumen rápido
INNER JOINcombina dos tablas por una clave compartida.ON.INNERes opcional; la gente escribe simplementeJOIN.users u) para mantener las cosas legibles.LEFT JOIN, un escenario distinto.Pruébalo tú
Sobre las tablas
usersyordersde arriba:JOIN+GROUP BY+SUM).Cuando JOIN te encaja, es un momento liberador al aprender SQL. Antes — puedes leer una tabla. Después — los datos del mundo real se abren del todo.