Tienes una lista de clientes y una lista de pedidos, y quieres verlas juntas. Eso se llama hacer un JOIN (unir): juntar las filas de dos tablas que se corresponden. Pides «todos los clientes con su pedido» y uno de los clientes no sale. Tu consulta (la petición que le escribes a la base de datos) funciona, pero no responde a lo que preguntabas: has elegido un tipo de JOIN que descarta a quien no tiene pareja. Hay dos que vas a usar sobre todo, INNER JOIN y LEFT JOIN, y se distinguen por una sola cosa: qué hacen con quien no tiene pareja.
## Una tienda, dos tablas
Una tabla es una cuadrícula de datos: cada fila es una cosa (un cliente, un pedido) y cada columna es una característica de esa cosa (su nombre, su importe). Una base de datos es un montón de tablas, cada una con lo suyo. Vamos a trabajar con dos de una tienda online ficticia, inventada para este ejemplo. Primero, los clientes:
| id | nombre |
|---|---|
| 1 | Ana |
| 2 | Bruno |
| 3 | Carla |
| 4 | Dani |
Y ahora los pedidos. Fíjate en la columna `cliente_id`: guarda el `id` del cliente que hizo el pedido. Y fíjate en quién no aparece: nadie tiene un pedido con `cliente_id` 3. Carla se registró y todavía no ha comprado nada.
| id | cliente_id | importe (€) |
|---|---|---|
| 101 | 1 | 25 |
| 102 | 1 | 40 |
| 103 | 2 | 15 |
| 104 | 4 | 60 |
## Unir es juntar filas que se corresponden
Unir tablas (hacer un JOIN, la palabra en inglés que verás en muchas ofertas de empleo de análisis de datos) es juntar en una sola tabla las filas que se corresponden. Para saber cuáles se corresponden hace falta una clave: un dato que aparece en las dos tablas. Aquí es `clientes.id` frente a `pedidos.cliente_id`. Esa columna `cliente_id`, que apunta al `id` de otra tabla, tiene nombre propio: clave foránea (*foreign key* en inglés). Es como el número de socio: el pedido no lleva el nombre del cliente, lleva su número.
## INNER JOIN: solo lo que tiene pareja
Pidamos «cada cliente con sus pedidos». Se hace en SQL, el lenguaje con el que se piden datos a una base de datos. La consulta tiene piezas con nombre. `SELECT` elige las columnas que quieres ver. `FROM` dice de qué tabla partes: la izquierda. `JOIN` añade otra tabla: la derecha. `ON` dice cuál es la clave: junta las filas donde `pedidos.cliente_id` sea igual a `clientes.id`. Esa notación, `tabla.columna`, indica de qué tabla es cada columna. `AS pedido` es un alias: un nombre nuevo para esa columna en el resultado. `ORDER BY` solo ordena las filas para que el resultado sea siempre el mismo.
1SELECT clientes.nombre, pedidos.id AS pedido, pedidos.importe2FROM clientes3INNER JOIN pedidos ON pedidos.cliente_id = clientes.id4ORDER BY clientes.id, pedidos.id;
INNER JOIN: se queda solo con las filas que tienen pareja en las dos tablas.
| nombre | pedido | importe |
|---|---|---|
| Ana | 101 | 25 |
| Ana | 102 | 40 |
| Bruno | 103 | 15 |
| Dani | 104 | 60 |
Ana aparece dos veces porque tiene dos pedidos: una fila por cada pareja encontrada. Y Carla ha desaparecido. INNER JOIN solo deja pasar filas que encuentran pareja en la otra tabla, y Carla no tiene ningún pedido con quien emparejarse. Se queda fuera sin hacer ruido.
## LEFT JOIN: todos los de la izquierda
La consulta es casi idéntica. Solo cambia una palabra. Con LEFT JOIN se conserva toda la tabla de la izquierda (la que escribes tras `FROM`, aquí `clientes`), tenga pareja o no. Si no la tiene, las columnas de la otra tabla (la derecha, `pedidos`) se rellenan con NULL, que significa «no hay valor».
1SELECT clientes.nombre, pedidos.id AS pedido, pedidos.importe2FROM clientes3LEFT JOIN pedidos ON pedidos.cliente_id = clientes.id4ORDER BY clientes.id, pedidos.id;
LEFT JOIN: conserva todos los clientes, tengan pedidos o no.
| nombre | pedido | importe |
|---|---|---|
| Ana | 101 | 25 |
| Ana | 102 | 40 |
| Bruno | 103 | 15 |
| Carla | NULL | NULL |
| Dani | 104 | 60 |
Un NULL es un dato que falta. No es un cero ni un texto vacío. Carla no ha comprado por 0 euros: no ha comprado, y por eso su pedido es NULL. Esa palabra aparecerá mucho en tu trabajo, así que acostúmbrate a leerla como «aquí no hay nada».
## Para qué sirve el que desaparece
Mi regla: si la pregunta es «¿a quién le falta algo?», LEFT JOIN. Si es «dame solo los que sí lo tienen», INNER JOIN. «¿Qué clientes se registraron y no han comprado nunca?» se contesta de forma habitual con un LEFT JOIN, porque necesitas ver a Carla para poder señalarla. Con INNER JOIN esa pregunta no tiene respuesta: lo que buscas es justo lo que el JOIN tira a la basura. Para quedarte solo con ellos, añade un `WHERE`, que filtra: deja únicamente las filas que cumplen la condición. La condición es `IS NULL` («es NULL») sobre una columna de la derecha:
1SELECT clientes.nombre2FROM clientes3LEFT JOIN pedidos ON pedidos.cliente_id = clientes.id4WHERE pedidos.id IS NULL;
Los clientes sin ningún pedido. Resultado: una sola fila, Carla.
Cuando dudes entre los dos, empieza por LEFT JOIN y mira si hay NULL. Si salen filas con NULL, ya sabes que hay elementos sin pareja. Con INNER JOIN no te enteras nunca de lo que se ha ido.
| Aspecto | INNER JOIN | LEFT JOIN |
|---|---|---|
| Filas del ejemplo | 4 | 5 |
| Cliente sin pedidos | Desaparece | Sale con NULL |
| Conserva | Solo las parejas | Toda la tabla izquierda |
| Devuelve | Solo a quien compró | A todos, con o sin pedidos |
| Aislar a quien no compró | No se puede | Añade IS NULL |
## La trampa del WHERE
Quieres a todos los clientes y, de sus pedidos, solo los de más de 20 euros. Lo natural es poner la condición en un `WHERE`. Mira qué pasa:
1SELECT clientes.nombre, pedidos.id AS pedido, pedidos.importe2FROM clientes3LEFT JOIN pedidos ON pedidos.cliente_id = clientes.id4WHERE pedidos.importe > 205ORDER BY clientes.id, pedidos.id;
Condición en el WHERE: se aplica después de unir.
| nombre | pedido | importe |
|---|---|---|
| Ana | 101 | 25 |
| Ana | 102 | 40 |
| Dani | 104 | 60 |
Carla ya no está: su importe es NULL y NULL no es mayor que 20. El LEFT JOIN se ha comportado como un INNER JOIN. Si mueves la condición al `ON`, el filtro se aplica al unir y nadie desaparece:
1SELECT clientes.nombre, pedidos.id AS pedido, pedidos.importe2FROM clientes3LEFT JOIN pedidos ON pedidos.cliente_id = clientes.id AND pedidos.importe > 204ORDER BY clientes.id, pedidos.id;
Condición en el ON: se aplica al unir.
| nombre | pedido | importe |
|---|---|---|
| Ana | 101 | 25 |
| Ana | 102 | 40 |
| Bruno | NULL | NULL |
| Carla | NULL | NULL |
| Dani | 104 | 60 |
Un LEFT JOIN puede convertirse en INNER JOIN sin avisar. Si filtras en el `WHERE` una columna de la tabla de la derecha, las filas con NULL se pierden. Los filtros sobre esa tabla, a no ser que busques justo los NULL, van dentro del `ON`.
En resumen: un JOIN junta filas de dos tablas usando una clave. INNER JOIN deja solo las que tienen pareja, y LEFT JOIN conserva todas las de la izquierda y rellena con NULL lo que no encuentra. Consejo para esta semana: coge dos tablas cualesquiera, haz primero el INNER JOIN y después el LEFT JOIN, y cuenta cuántas filas se pierden. Para practicarlo sin instalar nada, usa el editor SQL de la plataforma, que funciona en el navegador, en la lección «JOINs: cruzar tablas para responder preguntas complejas».