El FULL OUTER JOIN completa la família dels joins externs: conserva totes les files de totes dues taules, casin o no. És la unió d'un LEFT JOIN i un RIGHT JOIN en una sola operació.
És també, amb diferència, el JOIN que menys vegades escriuràs. I per una raó interessant que aquesta lliçó explica: en una base de dades amb integritat referencial ben declarada, com la de BotigaVerda, els orfes només poden existir en un costat. Un FULL OUTER JOIN entre dues taules relacionades per una clau forana degenera gairebé sempre en un LEFT JOIN. El seu veritable terreny de joc és en un altre lloc: la conciliació de dues fonts de dades independents, on cap de les dues no mana sobre l'altra.
Contingut
- La regla i el diagrama de conjunts
FULL OUTER JOINsobre BotigaVerda: per què només apareixen orfes en un costat- El cas real: conciliar dues fonts de dades
- El patró "només el que no casa en cap dels dos costats"
- Emulació en motors que no el suporten
- Suport per motor i cost
- Errors habituals i consells
- Exercicis
- Conclusió
- La regla i el diagrama de conjunts
flowchart LR
subgraph R[" "]
direction LR
A(("només a l'esquerra<br/>✅ amb NULL a la dreta"))
I(("casen<br/>✅ resultat"))
B(("només a la dreta<br/>✅ amb NULL a l'esquerra"))
end
style A fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
style I fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
style B fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
El quadre complet de la família, ja amb els quatre tipus:
| Tipus | Esquerra òrfena | Casen | Dreta òrfena | Files del resultat |
|---|---|---|---|---|
INNER JOIN |
❌ | ✅ | ❌ | casen |
LEFT JOIN |
✅ | ✅ | ❌ | casen + òrfenes esq. |
RIGHT JOIN |
❌ | ✅ | ✅ | casen + òrfenes dre. |
FULL OUTER JOIN |
✅ | ✅ | ✅ | casen + òrfenes esq. + òrfenes dre. |
Sintaxi: FULL OUTER JOIN i FULL JOIN són el mateix, perquè OUTER és opcional igual que a LEFT i RIGHT. En aquest curs escrivim FULL OUTER JOIN complet, perquè la seva raresa justifica ser explícits.
I una propietat que el distingeix dels altres dos externs: el FULL OUTER JOIN sí que és commutatiu. A FULL OUTER JOIN B i B FULL OUTER JOIN A retornen el mateix conjunt de files, perquè tots dos costats reben el mateix tracte.
| Propietat | INNER |
LEFT |
RIGHT |
FULL OUTER |
|---|---|---|---|---|
| Commutatiu | ✅ | ❌ | ❌ | ✅ |
FULL OUTER JOIN sobre BotigaVerda: per què només apareixen orfes en un costat
FULL OUTER JOIN sobre BotigaVerda: per què només apareixen orfes en un costatProvem el cas més evident: catàleg enfront de vendes.
SELECT p.id AS producte_id,
p.nom AS producte,
lc.id AS linia_id,
lc.quantitat
FROM productes AS p
FULL OUTER JOIN linies_comanda AS lc ON lc.producte_id = p.id
ORDER BY p.id, lc.id;50 files. Exactament les mateixes que retornava el LEFT JOIN de 03-03:
| Consulta | Files |
|---|---|
productes INNER JOIN linies_comanda |
47 |
productes LEFT JOIN linies_comanda |
50 |
productes FULL OUTER JOIN linies_comanda |
50 |
productes RIGHT JOIN linies_comanda |
47 |
El FULL OUTER JOIN no ha aportat ni una fila sobre el LEFT JOIN. Per què?
Perquè perquè aparegués un orfe pel costat dret hauria d'existir una línia de comanda el producte_id de la qual no correspongués a cap producte. I això és precisament el que la clau forana impedeix:
Dues restriccions actuen alhora:
| Restricció | Què impedeix |
|---|---|
REFERENCES productes(id) |
Inserir una línia amb un producte_id que no existeixi a productes |
NOT NULL |
Inserir una línia sense producte_id |
ON DELETE RESTRICT |
Esborrar un producte que tingui línies, deixant-les òrfenes |
flowchart LR
A["Pot haver-hi un producte<br/>sense línies de venda?"] -->|"Sí: 13, 19 i 20"| B["orfes<br/>per l'ESQUERRA"]
C["Pot haver-hi una línia<br/>sense producte?"] -->|"No: ho impedeix la FK"| D["orfes per la DRETA:<br/>impossibles"]
D'aquí en surt una regla general molt útil:
Entre dues taules unides per una clau forana
NOT NULLamb integritat referencial declarada, unFULL OUTER JOINés sempre equivalent a unLEFT JOINdes de la taula pare. Escriure'l és redundant, i a més paga un cost que no necessita.
El mateix passa amb clients i comandes: comandes.client_id és NOT NULL REFERENCES clients(id), així que clients FULL OUTER JOIN comandes retorna les mateixes 23 files que el LEFT JOIN.
L'únic cas de BotigaVerda en què el FULL OUTER sí que aportaria alguna cosa és comandes amb empleats, perquè empleat_id admet NULL:
- Orfes per l'esquerra: les 10 comandes web sense empleat.
- Orfes per la dreta: els 5 empleats sense comandes.
- Total: 10 emparellaments + 10 + 5 = 25 files.
És l'únic FULL OUTER JOIN amb sentit en aquest esquema, i tot i així seria més clar escriure dues consultes separades: "comandes sense comercial" i "empleats sense comandes" responen a preguntes de negoci diferents, i barrejar-les en una taula amb nuls pels dos costats no ajuda ningú.
- El cas real: conciliar dues fonts de dades
El FULL OUTER JOIN brilla quan les dues taules no estan unides per una clau forana: quan són dues fonts independents que haurien de coincidir i cal esbrinar en què no coincideixen. Exemples habituals:
| Conciliació | Font A | Font B | Què es busca |
|---|---|---|---|
| Catàleg enfront de vendes externes | Catàleg propi | Fitxer d'un marketplace | SKU que no existeixen, productes no publicats |
| Inventari enfront de comptabilitat | Recompte físic de magatzem | Sistema comptable | Diferències d'existències |
| Nòmines enfront de directori | Sistema de RH | Directori d'usuaris | Altes i baixes no propagades |
| Cobraments enfront de factures | Extracte bancari | Factures emeses | Cobraments sense factura, factures sense cobrament |
En tots ells la clau és la mateixa: cap de les dues fonts no és l'autoritat. Cadascuna pot tenir files que l'altra no té, i l'objectiu de l'informe és precisament trobar-les.
L'escenari: vendes del marketplace
BotigaVerda ha començat a vendre també en un marketplace extern, que cada mes envia un fitxer CSV amb les unitats venudes per SKU. Carreguem aquest fitxer en una taula temporal:
Avís:
vendes_marketplaceno forma part de l'esquema de BotigaVerda. És una taula auxiliar creada només per a aquest exemple. No la facis servir a la resta d'exercicis del curs, i esborra-la en acabar (o desconnecta't: en serTEMP, desapareix amb la sessió).
CREATE TEMP TABLE vendes_marketplace (
sku INTEGER,
unitats INTEGER
);
INSERT INTO vendes_marketplace (sku, unitats) VALUES
( 1, 14), ( 2, 9), ( 3, 6), ( 4, 11), ( 5, 22),
( 6, 4), ( 7, 7), ( 8, 3), ( 9, 12), (10, 5),
(11, 8), (12, 6), (14, 15), (15, 2), (16, 10),
(17, 4), (18, 19), (101, 7), (102, 3);19 files. Fixa't en dues coses: no hi ha cap clau forana entre sku i productes.id —el fitxer ve d'un altre sistema, no li podem imposar restriccions—, i apareixen dos SKU estranys, el 101 i el 102.
La conciliació completa
SELECT p.id AS producte_id,
p.nom AS producte,
vm.sku,
vm.unitats
FROM productes AS p
FULL OUTER JOIN vendes_marketplace AS vm ON vm.sku = p.id
ORDER BY COALESCE(p.id, vm.sku);| producte_id | producte | sku | unitats |
|---|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 1 | 14 |
| 2 | Arròs integral ecològic 1 kg | 2 | 9 |
| 3 | Mel de tarongina crua 500 g | 3 | 6 |
| 4 | Pasta d'espelta 500 g | 4 | 11 |
| 5 | Tomàquet triturat ecològic 400 g | 5 | 22 |
| 6 | Crema facial d'àloe vera 50 ml | 6 | 4 |
| 7 | Xampú sòlid de romaní 80 g | 7 | 7 |
| 8 | Oli corporal d'ametlles 200 ml | 8 | 3 |
| 9 | Bàlsam labial de calèndula 15 ml | 9 | 12 |
| 10 | Detergent ecològic concentrat 1 L | 10 | 5 |
| 11 | Fregall vegetal de lufa (pack 3) | 11 | 8 |
| 12 | Bosses reutilitzables de cotó (pack 5) | 12 | 6 |
| 13 | Espelmes de cera de soja (pack 2) | (null) | (null) |
| 14 | Infusió de camamilla ecològica 20 u | 14 | 15 |
| 15 | Te verd matcha cerimonial 30 g | 15 | 2 |
| 16 | Kombutxa de gingebre 750 ml | 16 | 10 |
| 17 | Suc de taronja premsat en fred 1 L | 17 | 4 |
| 18 | Raspall de dents de bambú | 18 | 19 |
| 19 | Desodorant natural en barra 50 g | (null) | (null) |
| 20 | Càpsules d'espirulina 120 u | (null) | (null) |
| (null) | (null) | 101 | 7 |
| (null) | (null) | 102 | 3 |
22 files = 17 emparellaments + 3 productes sense vendes al marketplace + 2 SKU desconeguts.
Ara sí que hi ha orfes pels dos costats, i cadascun explica una història diferent:
- Els productes 13, 19 i 20 no s'han venut al marketplace. Coincideixen amb els que tampoc no es venen a la botiga pròpia, cosa que reforça el diagnòstic: sense estoc, descatalogat i amb problema comercial.
- Els SKU 101 i 102 han generat vendes al marketplace però no existeixen al catàleg. Això és una alerta operativa seriosa: pot ser una referència antiga amb una altra numeració, un error de mapatge entre sistemes o vendes que ningú no està imputant a cap producte.
Un detall d'escriptura: l'ORDER BY COALESCE(p.id, vm.sku) ordena per l'identificador "vingui d'on vingui". Si ordenessis només per p.id, les dues files del marketplace tindrien NULL en aquella columna i se n'anirien al final (o al principi, segons NULLS FIRST/LAST, com vas veure a 02-05). COALESCE retorna el primer valor no nul de la llista, i s'estudia a fons a 06-04.
- El patró "només el que no casa en cap dels dos costats"
La conciliació completa està bé per revisar, però el que s'envia a operacions és només la llista de discrepàncies. És l'equivalent a l'anti-join de 03-03, ara per partida doble:
SELECT p.id AS producte_id,
p.nom AS producte,
vm.sku,
vm.unitats
FROM productes AS p
FULL OUTER JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE p.id IS NULL
OR vm.sku IS NULL
ORDER BY COALESCE(p.id, vm.sku);| producte_id | producte | sku | unitats |
|---|---|---|---|
| 13 | Espelmes de cera de soja (pack 2) | (null) | (null) |
| 19 | Desodorant natural en barra 50 g | (null) | (null) |
| 20 | Càpsules d'espirulina 120 u | (null) | (null) |
| (null) | (null) | 101 | 7 |
| (null) | (null) | 102 | 3 |
5 files: les tres discrepàncies del catàleg i les dues del fitxer. Això és un informe de conciliació de debò, i en un sistema real arribaria per correu cada matí.
En termes de conjunts, aquest patró retorna la diferència simètrica:
flowchart LR
subgraph R[" "]
direction LR
A(("només catàleg<br/>✅"))
I(("casen<br/>❌ excloses pel WHERE"))
B(("només marketplace<br/>✅"))
end
style A fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
style I fill:#f8f8f8,stroke:#bbb,stroke-dasharray: 4 4
style B fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
Dos detalls importants d'escriptura:
| Detall | Per què |
|---|---|
L'operador és OR, no AND |
Amb AND demanaries files on faltessin les dues claus alhora, cosa impossible: tota fila del resultat ve d'almenys un costat. AND retorna sempre 0 files |
| Es comparen les claus de cada costat | p.id i vm.sku. Igual que a l'anti-join simple, cal triar columnes que no puguin ser NULL de forma legítima a les seves taules d'origen |
I les tres variants del filtre, segons el que vulguis:
WHERE |
Retorna | Files aquí |
|---|---|---|
| (cap) | Conciliació completa | 22 |
p.id IS NULL OR vm.sku IS NULL |
Només discrepàncies (diferència simètrica) | 5 |
vm.sku IS NULL |
Només el que és al catàleg i no al fitxer | 3 |
p.id IS NULL |
Només el que és al fitxer i no al catàleg | 2 |
- Emulació en motors que no el suporten
MySQL i MariaDB no implementen FULL OUTER JOIN. L'emulació clàssica consisteix a combinar un LEFT JOIN i un RIGHT JOIN amb UNION:
-- Emulació de FULL OUTER JOIN a MySQL
SELECT p.id AS producte_id, p.nom AS producte, vm.sku, vm.unitats
FROM productes AS p
LEFT JOIN vendes_marketplace AS vm ON vm.sku = p.id
UNION
SELECT p.id, p.nom, vm.sku, vm.unitats
FROM productes AS p
RIGHT JOIN vendes_marketplace AS vm ON vm.sku = p.id;La lògica és directa:
flowchart TD
A["LEFT JOIN<br/>casen + orfes esquerra<br/>= 20 files"] --> C["UNION<br/>apila i elimina duplicats"]
B["RIGHT JOIN<br/>casen + orfes dreta<br/>= 19 files"] --> C
C --> D["22 files<br/>= FULL OUTER JOIN"]
Les 17 files que casen apareixen a totes dues consultes. Per això cal fer servir UNION i no UNION ALL: UNION elimina els duplicats i deixa 20 + 19 − 17 = 22 files. Amb UNION ALL obtindries 39 files, amb les 17 coincidències repetides dues vegades.
UNION, UNION ALL i les seves regles són el tema de la lliçó 03-07; aquí n'hi ha prou de saber que existeix l'emulació i per què necessita eliminar duplicats.
Nota de dialecte: a MySQL l'emulació té una volta de rosca desagradable. Com que
UNIONcompara files completes, dues files que difereixin en qualsevol columna es consideren diferents; si al teu conjunt de dades hi hagués files repetides de forma legítima,UNIONles col·lapsaria i perdries informació. L'alternativa robusta és ferLEFT JOINcompletUNION ALLamb només els orfes de la dreta (RIGHT JOIN ... WHERE p.id IS NULL), que no genera solapament i no necessita deduplicar.
- Suport per motor i cost
| Motor | FULL OUTER JOIN |
Nota |
|---|---|---|
| PostgreSQL | ✅ Sí | Des de versions molt antigues, sense restriccions |
| MySQL / MariaDB | ❌ No | Cal emular-lo amb UNION de LEFT i RIGHT |
| SQLite | ✅ Sí, des de la 3.39 (juny del 2022) | Igual que RIGHT JOIN |
| SQL Server | ✅ Sí | Sense restriccions |
| Oracle | ✅ Sí | La sintaxi antiga (+) no pot expressar un full outer |
Per què es fa servir poc
Quatre raons, en ordre d'importància:
- La integritat referencial el fa innecessari dins d'un mateix esquema. És l'argument de la secció 2, i explica la majoria dels casos.
- Gairebé sempre el que es vol és una de les dues meitats. "Productes sense vendes" o "vendes sense producte" són preguntes concretes que un
LEFT JOINamb anti-join respon millor, i amb un resultat més llegible. - El resultat és incòmode de llegir. Una taula amb nuls pels dos costats obliga a
COALESCEper ordenar, per agrupar i per presentar. Cada columna clau existeix per duplicat. - És el
JOINmés car. El motor ha de recórrer totes dues relacions completament i marcar els no emparellats als dos costats: no pot aturar-se abans ni descartar aviat. A PostgreSQL només s'implementa amb hash join o merge join; no existeix un pla denested loopper a unFULL OUTER JOIN, cosa que de vegades obliga a materialitzar i ordenar tots dos costats. Amb taules grans es nota (mòdul 8).
Regla pràctica: abans d'escriure un
FULL OUTER JOIN, pregunta't si de debò necessites els orfes de tots dos costats al mateix resultat. Nou de cada deu vegades la resposta és no, i unLEFT JOINés més clar i més ràpid. La desena vegada —una conciliació real entre dos sistemes— és exactament per al que existeix.
Errors habituals i consells
- Fer servir
FULL OUTER JOINentre dues taules unides per una FKNOT NULL. No aporta cap fila sobre elLEFT JOINi costa més. La integritat referencial ja garanteix que no hi ha orfes del costat fill. - Escriure
ANDen lloc d'ORal filtre de discrepàncies.WHERE p.id IS NULL AND vm.sku IS NULLretorna sempre 0 files: cap fila del resultat no pot faltar pels dos costats alhora. - Ordenar per una sola de les dues claus. Les files òrfenes de l'altre costat tenen aquella columna a
NULLi se'n van a l'extrem del llistat. Fes servirORDER BY COALESCE(a.clau, b.clau). - Oblidar que les columnes clau estan duplicades. A la conciliació tens
p.idivm.sku. Per presentar un identificador únic calCOALESCE(p.id, vm.sku). - Emular-lo amb
UNION ALLa MySQL. Duplica totes les files que casen: 39 en comptes de 22 a l'exemple d'aquesta lliçó. - Suposar que existeix a MySQL. No existeix, i no està previst. Si el teu SQL ha de ser portable, evita'l.
- Confondre "sense parella" amb "dada buida". Igual que a 03-03: comprova sempre contra una columna que no pugui ser
NULLlegítimament a la seva taula d'origen. - Consell: comença pel
LEFT JOINi comprova si necessites més. Executa elLEFT, compta files, executa elFULL OUTERi compara. Si el número no canvia, elFULL OUTERsobra. - Consell: en una conciliació, afegeix una columna que digui d'on ve cada fila. Amb
CASE(mòdul 6) pots etiquetar cada fila com a "només catàleg", "només fitxer" o "coincideix". L'informe es torna autoexplicatiu. - Consell: desa el patró de discrepàncies.
FULL OUTER JOIN ... WHERE a.clau IS NULL OR b.clau IS NULLés una plantilla que reutilitzaràs cada vegada que dos sistemes hagin de quadrar.
Exercicis
Exercici 1
Executa aquestes dues consultes i compara el nombre de files:
SELECT c.id, c.nom, co.id AS comanda_id
FROM clients AS c
LEFT JOIN comandes AS co ON co.client_id = c.id;
SELECT c.id, c.nom, co.id AS comanda_id
FROM clients AS c
FULL OUTER JOIN comandes AS co ON co.client_id = c.id;- Quantes files retorna cadascuna?
- Explica el resultat en termes de les restriccions declarades sobre
comandes.client_id. - Què hauria de canviar a l'esquema perquè les dues consultes donessin resultats diferents?
Exercici 2
Sobre la taula temporal vendes_marketplace de la secció 3, escriu dues consultes separades:
- Els productes del catàleg que no apareixen al fitxer del marketplace, amb el seu nom, preu i estoc.
- Els SKU del fitxer que no existeixen al catàleg, amb les unitats venudes.
Escriu la primera amb un LEFT JOIN i la segona amb un RIGHT JOIN, i després raona: per què en aquest cas resulten més útils dues consultes separades que el FULL OUTER JOIN de la secció 4?
Exercici 3
La teva empresa migra a MySQL una consulta de conciliació escrita en PostgreSQL:
SELECT p.id AS producte_id, p.nom AS producte, vm.sku, vm.unitats
FROM productes AS p
FULL OUTER JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE p.id IS NULL OR vm.sku IS NULL;- Reescriu-la per a MySQL fent servir
UNION. - Podries fer servir
UNION ALLen aquesta reescriptura concreta? Raona la resposta mirant què retorna cada meitat.
Solucions
Solució 1
1. Totes dues retornen 23 files.
2. La columna està declarada així:
Les tres parts actuen juntes:
REFERENCES clients(id)impedeix inserir una comanda amb unclient_idinexistent.NOT NULLimpedeix inserir una comanda sense client.ON DELETE RESTRICTimpedeix esborrar un client que tingui comandes, que és l'altra forma de generar orfes.
Conclusió: no pot existir cap comanda òrfena, així que el costat dret no aporta res i el FULL OUTER JOIN degenera en el LEFT JOIN. Les 23 files són les mateixes: 20 comandes + 3 clients sense comandes.
3. N'hi hauria prou que client_id admetés NULL —per exemple, per registrar comandes de convidats sense compte—. Aquestes comandes no casarien amb cap client i apareixerien com a orfes per la dreta, només visibles amb FULL OUTER JOIN (o amb RIGHT JOIN). És exactament la situació de comandes.empleat_id, que sí que admet nuls: comandes FULL OUTER JOIN empleats retorna 25 files enfront de les 20 del LEFT JOIN.
Solució 2
1. Productes del catàleg que no són al fitxer:
SELECT p.id,
p.nom AS producte,
p.preu,
p.stock
FROM productes AS p
LEFT JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE vm.sku IS NULL
ORDER BY p.id;| id | producte | preu | stock |
|---|---|---|---|
| 13 | Espelmes de cera de soja (pack 2) | 13.75 | 0 |
| 19 | Desodorant natural en barra 50 g | 7.80 | 75 |
| 20 | Càpsules d'espirulina 120 u | 16.40 | 55 |
2. SKU del fitxer que no existeixen al catàleg:
SELECT vm.sku,
vm.unitats
FROM productes AS p
RIGHT JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE p.id IS NULL
ORDER BY vm.sku;| sku | unitats |
|---|---|
| 101 | 7 |
| 102 | 3 |
Per què dues consultes separades són més útils aquí:
| Motiu | Explicació |
|---|---|
| Columnes diferents | Un producte sense vendes interessa amb el seu preu i el seu estoc; un SKU desconegut interessa amb les unitats venudes. En una sola taula caldria retornar la unió de tots dos jocs de columnes, amb la meitat a NULL a cada fila |
| Destinataris diferents | La primera llista va a màrqueting; la segona, a l'equip d'integracions. Són dues incidències amb dos responsables |
| Urgència diferent | Un producte sense vendes és una observació comercial; un SKU venut que no existeix al catàleg és un error de dades que cal corregir avui |
| Llegibilitat | Cada consulta té un significat únic i s'explica sola. El FULL OUTER obliga a llegir els nuls per saber de quin tipus és cada fila |
El FULL OUTER JOIN continua sent útil per a la visió de conjunt —el quadre de 22 files de la secció 3, per revisar d'un cop d'ull—, però la feina operativa es despatxa millor amb dues consultes dirigides.
Solució 3
1. Reescriptura per a MySQL:
-- Meitat esquerra: productes del catàleg sense SKU al fitxer
SELECT p.id AS producte_id, p.nom AS producte, vm.sku, vm.unitats
FROM productes AS p
LEFT JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE vm.sku IS NULL
UNION
-- Meitat dreta: SKU del fitxer sense producte al catàleg
SELECT p.id, p.nom, vm.sku, vm.unitats
FROM productes AS p
RIGHT JOIN vendes_marketplace AS vm ON vm.sku = p.id
WHERE p.id IS NULL;Retorna les mateixes 5 files que la versió PostgreSQL.
2. Es pot fer servir UNION ALL? Sí, i a més és preferible.
El raonament és el que importa: UNION (sense ALL) cal quan les dues meitats poden produir les mateixes files, i s'han d'eliminar els duplicats. Aquest és el cas de l'emulació general de la secció 5, on tots dos JOIN retornen les 17 files que casen.
Aquí no passa. Cada meitat porta el seu propi WHERE que la restringeix als orfes del seu costat:
| Meitat | Filtre | Retorna | Solapament |
|---|---|---|---|
| Esquerra | vm.sku IS NULL |
3 files: productes sense SKU | — |
| Dreta | p.id IS NULL |
2 files: SKU sense producte | cap |
Cap fila no pot complir les dues condicions alhora, així que els conjunts són disjunts i no hi ha res a deduplicar. UNION ALL és més ràpid perquè s'estalvia el pas d'ordenació o de taula hash que UNION necessita per detectar duplicats. És un exemple perfecte de la regla que veuràs a 03-07: fes servir UNION ALL llevat que tinguis una raó concreta per eliminar duplicats.
Conclusió
Tanques la família dels joins externs:
- El
FULL OUTER JOINconserva les files òrfenes de tots dos costats: és unLEFTi unRIGHTalhora. És l'únic join extern commutatiu. - Sobre BotigaVerda gairebé mai no aporta res, i saps explicar per què: la integritat referencial (
REFERENCES+NOT NULL+ON DELETE RESTRICT) impedeix que existeixin orfes del costat fill.productes FULL OUTER JOIN linies_comandaretorna les mateixes 50 files que elLEFT JOIN. - El seu terreny real és la conciliació de dues fonts independents, on cap no mana sobre l'altra: catàleg contra fitxer d'un marketplace, inventari contra comptabilitat, extracte bancari contra factures.
- Has construït aquesta conciliació completa (22 files) i l'informe de discrepàncies amb el patró
WHERE a.clau IS NULL OR b.clau IS NULL(5 files): tres productes que el marketplace no ven i dos SKU que no existeixen al catàleg. Recorda: l'operador ésOR, maiAND. - Saps emular-lo amb
UNIONd'unLEFT JOINi unRIGHT JOINper a MySQL, i per què aquesta emulació necessita eliminar duplicats llevat que cada meitat estigui restringida als seus propis orfes. - I coneixes el seu cost: no admet pla de nested loop, obliga a recórrer totes dues relacions senceres i produeix un resultat que necessita
COALESCEper a gairebé tot. Fes-lo servir quan de debò necessitis els dos costats.
A la lliçó següent, SELF JOIN i CROSS JOIN, veurem els dos JOIN "rars" que més confonen qui comença. El SELF JOIN et permetrà recórrer per fi les dues relacions reflexives de BotigaVerda: la jerarquia d'empleats amb cap_id —on tornaràs a necessitar un LEFT JOIN per no perdre la Rosa, la directora sense cap— i la xarxa de referits de clients. El CROSS JOIN, que fins ara només ha aparegut com a accident, es convertirà en una eina deliberada per generar combinacions completes.
Curs de SQL
Mòdul 1: Introducció a SQL
- Què és SQL?
- Configurar el teu entorn SQL
- Sintaxi bàsica de SQL
- Entendre bases de dades i taules
- El model relacional: claus primàries i foranes
- La base de dades del curs: BotigaVerda
Mòdul 2: Consultes bàsiques de SQL
- Instrucció SELECT
- Àlies, expressions i columnes calculades
- Filtrar dades amb WHERE
- DISTINCT i eliminació de duplicats
- Ordenar dades amb ORDER BY
- Limitar resultats amb LIMIT
Mòdul 3: Treballar amb múltiples taules
- Operacions JOIN
- INNER JOIN
- LEFT JOIN
- RIGHT JOIN
- FULL OUTER JOIN
- SELF JOIN i CROSS JOIN
- Unions de conjunts: UNION, INTERSECT i EXCEPT
Mòdul 4: Filtratge avançat de dades
- Utilitzar LIKE per a coincidència de patrons
- Operadors IN i BETWEEN
- Valors NULL i IS NULL
- Funcions d'agregació: COUNT, SUM, AVG, MIN i MAX
- Agregar dades amb GROUP BY
- Clàusula HAVING
Mòdul 5: Manipulació de dades
- Crear taules i restriccions amb CREATE TABLE
- Instrucció INSERT
- Instrucció UPDATE
- Instrucció DELETE
- Instrucció UPSERT (MERGE)
- Modificar l'esquema: ALTER TABLE i migracions segures
Mòdul 6: Funcions avançades de SQL
- Funcions de cadena
- Funcions numèriques
- Funcions de data i hora
- Conversió de tipus i tractament dels NULL: CAST i COALESCE
- Expressions condicionals
Mòdul 7: Subconsultes i consultes imbricades
- Introducció a les subconsultes
- Subconsultes correlacionades
- EXISTS i NOT EXISTS
- Utilitzar subconsultes en les clàusules SELECT, FROM i WHERE
- Subconsultes o JOIN: quin triar
Mòdul 8: Índexs i optimització del rendiment
- Entendre els índexs
- Creació i gestió d'índexs
- Tipus d'índex i quan no indexar
- Tècniques d'optimització de consultes
- Anàlisi del rendiment de les consultes
Mòdul 9: Transaccions i concurrència
- Introducció a les transaccions
- Propietats ACID
- Instruccions de control de transaccions
- Nivells d'aïllament i anomalies de concurrència
- Gestió de la concurrència: bloquejos i interbloquejos
Mòdul 10: Temes avançats
- Vistes
- Expressions de taula comunes (CTE)
- Funcions de finestra
- Procediments emmagatzemats
- Disparadors (triggers)
- JSON i dades semiestructurades
Mòdul 11: SQL a la pràctica
- Casos d'ús al món real
- Bones pràctiques
- Seguretat: injecció SQL, permisos i rols
- SQL per a l'anàlisi de dades
- SQL en el desenvolupament web
