JOINs
Què és un JOIN?
Un JOIN combina files de dues o mes taules basant-se en una condició de jointura (normalment la relació entre una FK i una PK). El JOIN és el mecanisme que permet recuperar informació distribuïda entre múltiples taules d'un esquema normalitzat.
Cada tipus de JOIN retorna un conjunt diferent de files. La visualització clàssica usa cercles superposats: la part ressaltada en verd indica les files que apareixen al resultat.
INNER JOIN
El INNER JOIN (o simplement JOIN) retorna només les files que tenen correspondència en les dues taules. Les files que no coincideixen s'exclouen del resultat.
-- Nom del client i les seves comandes
SELECT
c.nom || ' ' || c.cognoms AS client,
co.id_comanda,
co.data_com,
co.total
FROM clients c
INNER JOIN comandes co ON c.id_client = co.id_client
ORDER BY c.cognoms, co.data_com DESC;
-- Tres taules
SELECT
c.nom || ' ' || c.cognoms AS client,
co.id_comanda,
p.nom AS producte,
lc.quantitat,
lc.preu_unitari,
lc.quantitat * lc.preu_unitari AS subtotal
FROM clients c
JOIN comandes co ON c.id_client = co.id_client
JOIN linies_comanda lc ON co.id_comanda = lc.id_comanda
JOIN productes p ON lc.id_producte = p.id_producte
WHERE co.estat = 'confirmada'
ORDER BY co.id_comanda, lc.num_linia;
SELECT
c.nom || ' ' || c.cognoms AS client,
co.id_comanda,
co.data_com,
co.total
FROM clients c
INNER JOIN comandes co ON c.id_client = co.id_client
ORDER BY c.cognoms, co.data_com DESC;
-- Tres taules
SELECT
c.nom || ' ' || c.cognoms AS client,
co.id_comanda,
p.nom AS producte,
lc.quantitat,
lc.preu_unitari,
lc.quantitat * lc.preu_unitari AS subtotal
FROM clients c
JOIN comandes co ON c.id_client = co.id_client
JOIN linies_comanda lc ON co.id_comanda = lc.id_comanda
JOIN productes p ON lc.id_producte = p.id_producte
WHERE co.estat = 'confirmada'
ORDER BY co.id_comanda, lc.num_linia;
INNER JOIN vs JOIN
JOIN sense qualificador és equivalent a INNER JOIN. La majoria de programadors professionals escriuen simplement JOIN per concisió.
Visualització pas a pas: com processa el motor un JOIN
clients té 3 files; comandes en té 4. Volem combinar-les per saber quines comandes pertanyen a cada client.
| id_client | nom |
|---|---|
| 1 | Joan |
| 2 | Maria |
| 3 | Pere |
| id_com | id_client | total |
|---|---|---|
| 101 | 1 | 50 € |
| 102 | 1 | 30 € |
| 103 | 3 | 80 € |
| 104 | 3 | 15 € |
clients amb les files de comandes. Joan (id=1) té dues comandes coincidents: 101 i 102. Ambdues files apareixen al resultat.
| id_client | nom |
|---|---|
| 1 | Joan |
| 2 | Maria |
| 3 | Pere |
| nom | id_com | total |
|---|---|---|
| Joan | 101 | 50 € |
| Joan | 102 | 30 € |
comandes. En un INNER JOIN, les files sense coincidència s'exclouen del resultat. Maria desapareix.
| id_client | nom |
|---|---|
| 1 | Joan |
| 2 | Maria |
| 3 | Pere |
| nom | id_com | total |
|---|---|---|
| Joan | 101 | 50 € |
| Joan | 102 | 30 € |
| nom | id_com | total |
|---|---|---|
| Joan | 101 | 50 € |
| Joan | 102 | 30 € |
| Pere | 103 | 80 € |
| Pere | 104 | 15 € |
NULL als camps de comandes. El LEFT JOIN garanteix que totes les files de la taula esquerra surten sempre.
| nom | id_com | total |
|---|---|---|
| Joan | 101 | 50 € |
| Joan | 102 | 30 € |
| Maria | NULL | NULL |
| Pere | 103 | 80 € |
| Pere | 104 | 15 € |
LEFT JOIN (LEFT OUTER JOIN)
El LEFT JOIN retorna totes les files de la taula esquerra (LEFT), i les files coincidents de la taula dreta. On no hi ha coincidència, les columnes de la taula dreta contenen NULL.
flowchart LR
subgraph Resultado
A["Files de A\n(totes)"]
B["Files de B\n(només coincidents\no NULL)"]
end
A --- B
-- Tots els clients, tinguin o no comandes
SELECT
c.nom || ' ' || c.cognoms AS client,
c.email,
COUNT(co.id_comanda) AS num_comandes,
COALESCE(SUM(co.total), 0) AS total_gastat
FROM clients c
LEFT JOIN comandes co ON c.id_client = co.id_client
GROUP BY c.id_client, c.nom, c.cognoms, c.email
ORDER BY total_gastat DESC;
-- Clients SENSE cap comanda (registres orfes)
SELECT c.nom, c.cognoms, c.email
FROM clients c
LEFT JOIN comandes co ON c.id_client = co.id_client
WHERE co.id_comanda IS NULL;
SELECT
c.nom || ' ' || c.cognoms AS client,
c.email,
COUNT(co.id_comanda) AS num_comandes,
COALESCE(SUM(co.total), 0) AS total_gastat
FROM clients c
LEFT JOIN comandes co ON c.id_client = co.id_client
GROUP BY c.id_client, c.nom, c.cognoms, c.email
ORDER BY total_gastat DESC;
-- Clients SENSE cap comanda
SELECT c.nom, c.cognoms, c.email
FROM clients c
LEFT JOIN comandes co ON c.id_client = co.id_client
WHERE co.id_comanda IS NULL;
RIGHT JOIN (RIGHT OUTER JOIN)
El RIGHT JOIN és el simètric del LEFT JOIN: retorna totes les files de la taula dreta (RIGHT), i les coincidents de l'esquerra. En la pràctica, s'usa poc; quasi sempre es prefereix reescriure com un LEFT JOIN canviant l'ordre de les taules.
-- Totes les comandes amb les dades del client (si existeix)
SELECT co.id_comanda, co.data_com, c.nom, c.cognoms
FROM clients c
RIGHT JOIN comandes co ON c.id_client = co.id_client;
-- Equivalent a:
SELECT co.id_comanda, co.data_com, c.nom, c.cognoms
FROM comandes co
LEFT JOIN clients c ON co.id_client = c.id_client;
FULL OUTER JOIN
El FULL OUTER JOIN retorna totes les files de les dues taules, amb NULL on no hi ha coincidència.
CROSS JOIN
El CROSS JOIN (producte cartesià) combina cada fila de la primera taula amb cada fila de la segona, sense cap condició de jointura. Si la taula A té M files i B té N files, el resultat té M × N files.
Usos típics: generar combinacions per a preus, horaris, tests de càrrega.
-- Totes les combinacions de colors i talles (per a un sistema de roba)
SELECT c.nom AS color, t.nom AS talla
FROM colors c
CROSS JOIN talles t;
-- Si hi ha 5 colors i 6 talles → 30 files al resultat
CROSS JOIN i l'explosió combinatoria
Un CROSS JOIN accidental (condició ON incorrecta o absent) pot generar milions de files i bloquejar el servidor. Sempre verifiqueu que les vostres condicions JOIN són correctes en les vostres consultes.
Self JOIN (AUTO-JOIN)
Un self JOIN és un JOIN d'una taula consigo mateixa. Útil per a relacions jeràrquiques (empleats i els seus managers, categories i subcategories).
Resum comparatiu
| JOIN | Files retornades | Cas d'ús típic | NULL? |
|---|---|---|---|
INNER JOIN |
Coincidències d'ambdues taules | Llistat complet amb dades relacionades | No |
LEFT JOIN |
Tot A + coincidències de B | Clients amb o sense comandes | Sí (B) |
RIGHT JOIN |
Coincidències d'A + tot B | Equivalent a LEFT amb ordre de taules intercanviat | Sí (A) |
FULL OUTER JOIN |
Tot A + tot B | Detectar orfes en qualsevol de les dues taules | Sí (ambdós) |
CROSS JOIN |
Totes les combinacions (M × N) | Generar combinacions (colors × talles, etc.) | No |
LEFT + WHERE b.id IS NULL |
Files d'A sense cap coincidència | Clients que mai han fet cap comanda | — |
Truc professional
En la pràctica, el RIGHT JOIN gairebé mai s'usa: sempre és possible reescriure'l com un LEFT JOIN intercanviant l'ordre de les taules, cosa que millora la llegibilitat. La taula "principal" hauria de ser sempre la de l'esquerra.
AC0372/04/04 — Miniactivitat
RA4 · CA4.3, CA4.6
Amb la vostra BD de pràctiques, escriviu:
- JOIN que mostri totes les reserves amb el nom d'usuari i el nom de la instal·lació.
- LEFT JOIN que llisti tots els usuaris i el nombre de reserves que han fet (inclou els que no n'han feta cap).
- Usuaris que mai han fet cap reserva (usant LEFT JOIN + WHERE IS NULL).
- Reserves del mes actual amb totes les dades: usuari, instal·lació, data, hora i preu calculat.
- La instal·lació que s'ha reservat mes vegades (GROUP BY + JOIN + ORDER BY + LIMIT 1).