Aquesta lliçó no ensenya sintaxi nova. Ensenya a decidir. Després de quatre lliçons tens dues caixes d'eines que se solapen: gairebé totes les preguntes que saps respondre amb un JOIN admeten també una subconsulta, i a l'inrevés. I el problema de tenir dues formes d'escriure el mateix és que ningú no et diu quina fer servir.
Hi ha una regla base que resol el 80 % dels casos en cinc segons, quatre grans equivalències que convé conèixer amb les seves dues escriptures, una diferència real —no d'estil— entre IN i un INNER JOIN, i un criteri de rendiment que cal manejar amb humilitat: l'optimitzador reescriu moltes d'aquestes consultes, i suposar quina és més ràpida sense mesurar és la manera més comuna de perdre el temps.
Contingut
- La regla base
- Equivalència 1:
INenfront d'INNER JOIN(i la diferència real) - Equivalència 2: les quatre formes de respondre "què no casa"
- Equivalència 3: escalar al
SELECTenfront deLEFT JOIN+GROUP BY - Equivalència 4: correlacionada enfront de taula derivada
- Què fa l'optimitzador per sota
- Llegibilitat i manteniment, criteri de primera classe
- Taula-guia: vull X → escriu Y
- Errors habituals i consells
- Exercicis
- Conclusió del mòdul
- La regla base
Si necessites columnes de l'altra taula al resultat, és un
JOIN. Si només necessites filtrar o calcular un valor, sol ser una subconsulta.
És sorprenentment fiable. Llegeix-la com una pregunta sobre el SELECT, no sobre el WHERE: mira quines columnes vols mostrar i d'on surten.
| La pregunta | Què vols mostrar | Eina |
|---|---|---|
| "Comandes amb el nom del seu client" | Columnes de comandes i de clients |
JOIN |
| "Clients que han comprat alguna vegada" | Només columnes de clients |
Subconsulta (EXISTS) |
| "Productes per sobre del preu mitjà" | Només columnes de productes |
Subconsulta escalar |
| "Línies de comanda amb producte i categoria" | Tres taules al resultat | JOIN |
| "Categories que facturen més que la mitjana" | categories + un agregat propi |
GROUP BY + subconsulta al HAVING |
I el seu corol·lari, que és igual d'útil: si et descobreixes escrivint un JOIN seguit d'un DISTINCT, gairebé sempre volies un EXISTS. El DISTINCT és el símptoma que has portat files que no necessitaves per respondre una pregunta de sí o no.
- Equivalència 1:
IN enfront d'INNER JOIN (i la diferència real)
IN enfront d'INNER JOIN (i la diferència real)La pregunta: clients que han fet alguna comanda.
-- Versió A: subconsulta
SELECT c.id, c.nom || ' ' || c.cognoms AS client, c.pais
FROM clients AS c
WHERE c.id IN (SELECT client_id FROM comandes)
ORDER BY c.id;
-- Versió B: INNER JOIN
SELECT c.id, c.nom || ' ' || c.cognoms AS client, c.pais
FROM clients AS c
JOIN comandes AS co ON co.client_id = c.id
ORDER BY c.id;Semblen la mateixa consulta. No ho són. La versió A retorna una fila per client:
| id | client | pais |
|---|---|---|
| 1 | Lucía Martínez Soler | Espanya |
| 2 | Carlos Ferrer Ibáñez | Espanya |
| 3 | Marta Sanchis Gil | Espanya |
| 4 | Javier Ortega Ruiz | Espanya |
(4 primeres de 12 files.)
Versió A (IN) |
Versió B (JOIN) |
|
|---|---|---|
| Files retornades | 12 | 20 |
| Clients diferents | 12 | 12 |
| Lucía Martínez Soler hi apareix… | 1 vegada | 3 vegades |
La versió B retorna les 20 files de comandes amb les dades del client repetides:
| id | client | pais |
|---|---|---|
| 1 | Lucía Martínez Soler | Espanya |
| 1 | Lucía Martínez Soler | Espanya |
| 1 | Lucía Martínez Soler | Espanya |
| 2 | Carlos Ferrer Ibáñez | Espanya |
| 2 | Carlos Ferrer Ibáñez | Espanya |
| 3 | Marta Sanchis Gil | Espanya |
(6 primeres de 20 files.) Aquesta és la diferència de fons entre les dues eines, i no és qüestió de gust:
INés una prova de pertinença: pregunta "aquest valor és a la llista?" i respon una vegada per fila externa. No pot multiplicar.JOINés un producte filtrat: genera una fila per cada parella que casa. Si la taula dreta té tres coincidències, en surten tres files.
Per igualar-les cal afegir SELECT DISTINCT a la versió B — i és aquí on el DISTINCT delata el problema. A més, DISTINCT obliga a ordenar o a construir una taula hash sobre les 20 files ja generades: has fet feina de més per després desfer-la.
El cas en què el
JOINsí que és la resposta correcta: si volguessis "clients amb la data de cada comanda", les 20 files són el resultat que busques, perquèdata_comandaés una columna de l'altra taula. La regla base en acció.
- Equivalència 2: les quatre formes de respondre "què no casa"
Aquí es tanca definitivament el fil obert a 03-07 i desenvolupat a 07-03. La pregunta —clients que no han comprat mai— té quatre escriptures, totes correctes, totes identificant els mateixos tres clients (Núria, Hugo i Inés):
WHERE NOT EXISTS (SELECT 1 FROM comandes AS co WHERE co.client_id = c.id) -- 1
WHERE c.id NOT IN (SELECT client_id FROM comandes) -- 2
LEFT JOIN comandes AS co ON co.client_id = c.id WHERE co.id IS NULL -- 3
SELECT id FROM clients EXCEPT SELECT client_id FROM comandes -- 4NOT EXISTS |
NOT IN |
Anti-join | EXCEPT |
|
|---|---|---|---|---|
Seguretat amb NULL |
✅ Total | ❌ 0 files si hi ha nuls | ✅ Total | ✅ Total |
| Columnes al resultat | Totes les externes | Totes les externes | Totes les externes | ❌ Només les comparades |
| Duplicats | No en crea | No en crea | No en crea | Els elimina sempre |
| Llegibilitat | ✅ Es llegeix com la pregunta | ✅ Molt alta… fins que falla | ⚠️ Cal conèixer la fórmula | ✅ Alta |
| Pla típic a PostgreSQL | Anti-join | Pitjor: filtre no anti-joinable | Anti-join | Sort + dedup |
| Portabilitat | ✅ Universal | ✅ Universal | ✅ Universal | ⚠️ MINUS a Oracle |
| Veredicte | Per omissió | Només amb NOT NULL garantit |
Bona si ja unies | Comparar conjunts |
La recomanació, en una línia: NOT EXISTS per omissió; anti-join si ja estaves unint aquelles taules per un altre motiu; EXCEPT per comparar conjunts; NOT IN, mai per costum.
La raó de descartar NOT IN com a opció per omissió no és que sigui pitjor avui: és que la seva correcció depèn d'una propietat de l'esquema —que la columna sigui NOT NULL— que pot canviar sense que ningú revisi les teves consultes. Una migració que permeti nuls a empleat_id no donarà cap error i els teus informes començaran a retornar zero files.
- Equivalència 3: escalar al
SELECT enfront de LEFT JOIN + GROUP BY
SELECT enfront de LEFT JOIN + GROUP BYJa vas veure les dues escriptures a 07-04. Aquí interessa el criteri:
| Nombre de mètriques | Escriptura preferible | Per què |
|---|---|---|
| 1 | Subconsulta al SELECT |
Es llegeix d'un cop d'ull; no toca el FROM |
| 2 | Qualsevol | Empat tècnic |
| 3 o més | LEFT JOIN + GROUP BY |
Una passada en comptes de N; una sola definició del FROM |
El cas d'una sola mètrica, enfrontat:
-- Versió A: escalar al SELECT
SELECT c.id, c.nom,
(SELECT COUNT(*) FROM comandes AS co WHERE co.client_id = c.id) AS comandes
FROM clients AS c
ORDER BY c.id;
-- Versió B: LEFT JOIN + GROUP BY
SELECT c.id, c.nom, COUNT(co.id) AS comandes
FROM clients AS c
LEFT JOIN comandes AS co ON co.client_id = c.id
GROUP BY c.id, c.nom
ORDER BY c.id;| id | nom | comandes |
|---|---|---|
| 1 | Lucía | 3 |
| 2 | Carlos | 2 |
| 3 | Marta | 1 |
| 13 | Núria | 0 |
(4 files de les 15; idèntiques en les dues versions, inclosos els zeros dels clients 13, 14 i 15.) Amb una mètrica la versió A guanya en claredat: no toca el FROM, no necessita GROUP BY i no pot inflar res. I dos matisos que solen decidir l'elecció quan n'hi ha més:
- La subconsulta és immune a la multiplicació de files. El
LEFT JOINamblinies_comandaobliga aCOUNT(DISTINCT co.id)per no comptar 9 comandes on n'hi ha 3. - El
GROUP BYés immune a la repetició. Afegir una mètrica nova és una línia; amb subconsultes és copiar un bloc de quatre línies i canviar l'agregat.
Hi ha un tercer cas on la subconsulta guanya clarament: quan la mètrica no es pot expressar amb un agregat sobre el JOIN. "L'import de l'última comanda de cada client" no és cap SUM ni MAX de les columnes unides: exigeix ordenar i tallar. Allà toca subconsulta correlacionada, LATERAL (07-04) o funció de finestra (10-03).
- Equivalència 4: correlacionada enfront de taula derivada
La pregunta: productes per sobre de la mitjana de la seva categoria (les 8 files de 07-02).
-- Versió A: correlacionada
SELECT p.id, p.nom, p.preu
FROM productes AS p
WHERE p.preu > (SELECT AVG(p2.preu) FROM productes AS p2
WHERE p2.categoria_id = p.categoria_id);
-- Versió B: taula derivada agregada
SELECT p.id, p.nom, p.preu, ROUND(m.preu_mitja, 2) AS mitjana_categoria
FROM productes AS p
JOIN (SELECT categoria_id, AVG(preu) AS preu_mitja
FROM productes GROUP BY categoria_id) AS m ON m.categoria_id = p.categoria_id
WHERE p.preu > m.preu_mitja;Les mateixes 8 files, i la versió B a més mostra el llindar:
| id | nom | preu | mitjana_categoria |
|---|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 12.50 | 6.18 |
| 3 | Mel de tarongina crua 500 g | 9.75 | 6.18 |
| 6 | Crema facial d'àloe vera 50 ml | 18.90 | 11.54 |
| 8 | Oli corporal d'ametlles 200 ml | 14.25 | 11.54 |
(4 primeres de 8 files; tanquen els productes 10, 13, 15 i 19.) Les diferències:
| Correlacionada | Taula derivada | |
|---|---|---|
| Vegades que es calcula la mitjana | 20 (conceptualment) | 6, una per categoria |
| Pots mostrar la mitjana? | Sí, repetint la subconsulta | Sí, de franc: és una columna |
| Expressió duplicada | Sí, SELECT i WHERE |
No: es nomena una vegada |
| Es llegeix com la pregunta | Sí, gairebé literalment | Menys directe |
| Escala a taules grans | Pitjor | Millor |
Amb poques files i una sola comparació, la correlacionada és més llegible. Tan bon punt vulguis mostrar el valor de referència, reutilitzar-lo o aplicar-lo sobre milions de files, la taula derivada és millor — i amb tres passos, la CTE de 10-02 és millor que totes dues.
- Què fa l'optimitzador per sota
Abans de decidir per rendiment, convé saber que el motor no executa el que escrius, sinó un pla equivalent que ell tria. PostgreSQL aplica diverses transformacions automàtiques:
| El que escrius | En què ho sol convertir | Canvia el rendiment? |
|---|---|---|
IN (SELECT ...) |
Semi-join (hash o merge) | No: acaba essent com un JOIN desduplicat |
EXISTS (...) |
Semi-join | No |
NOT EXISTS (...) |
Anti-join | No |
NOT IN (SELECT ...) |
No pot: ha de conservar la semàntica dels nuls | Sí, a pitjor |
| Taula derivada simple | L'aplana (subquery pull-up) dins de la consulta externa | No |
Escalar correlacionada al SELECT |
Gairebé mai la transforma | Sí, a pitjor amb moltes files |
Escalar correlacionada al WHERE |
De vegades sí, de vegades no | Depèn |
D'aquí surten les tres conclusions pràctiques del mòdul:
INiEXISTSenfront d'unJOINsón, en rendiment, pràcticament el mateix a PostgreSQL modern. Tria per llegibilitat, no per velocitat.NOT INsí que és més lent, a més de perillós, perquè l'optimitzador té les mans lligades per la semàntica dels nuls.- Les correlacionades al
SELECTsón la construcció amb més risc real: són les que més sovint s'executen literalment, una vegada per fila.
No optimitzis a cegues. "Les subconsultes són lentes" és un mite que circula des de MySQL 5.5, on efectivament ho eren. Amb PostgreSQL 16 gairebé mai no és cert, i amb altres motors depèn de la versió. L'única manera honesta de saber-ho és mesurar:
EXPLAIN ANALYZEt'ensenya el pla real i els temps, i és la lliçó 08-05. Fins llavors, escriu la versió més llegible.
- Llegibilitat i manteniment, criteri de primera classe
Quan dues escriptures tenen el mateix rendiment —que és l'habitual—, la llegibilitat no és un criteri secundari: és el criteri. Una consulta s'escriu una vegada i es llegeix, es depura i es modifica desenes de vegades.
Compara. La versió imbricada de "clients el tiquet mitjà dels quals supera la mitjana global", amb tres nivells:
SELECT c.nom, t.tiquet_mitja
FROM clients AS c
JOIN (SELECT co.client_id, AVG(p.total) AS tiquet_mitja
FROM (SELECT co2.id, co2.client_id,
SUM(lc.quantitat * lc.preu_unitari * (1 - lc.descompte)) AS total
FROM comandes AS co2 JOIN linies_comanda AS lc ON lc.comanda_id = co2.id
GROUP BY co2.id, co2.client_id) AS p
JOIN comandes AS co ON co.id = p.id
GROUP BY co.client_id) AS t ON t.client_id = c.id
WHERE t.tiquet_mitja > (SELECT AVG(x.total) FROM (SELECT SUM(...) AS total ...) AS x);Es llegeix de dins cap enfora, el sagnat consumeix mitja pantalla i el càlcul del total de comanda està escrit dues vegades. La mateixa lògica amb CTE:
WITH totals_comanda AS (...), -- total de cada comanda, una sola vegada
mitjana_global AS (...), -- 36,40 €
tiquet_client AS (...) -- mitjana per client
SELECT ... FROM tiquet_client WHERE tiquet_mitja > (SELECT * FROM mitjana_global);Cada pas té nom, es llegeix de dalt a baix com un procediment, i totals_comanda es defineix una vegada i es fa servir dues. Això és 10-02, i és la raó per la qual aquest mòdul insisteix a remetre-hi: les subconsultes resolen el problema, les CTE el deixen llegible.
Tres senyals que una consulta demana reescriptura:
- Més de dos nivells d'imbricació. Compta parèntesis d'obertura seguits.
- La mateixa expressió escrita dues o més vegades. Un
SUM(...)repetit alSELECTi alHAVINGés deute tècnic. - Tres o més subconsultes gairebé idèntiques al
SELECT. És unGROUP BYesperant a néixer.
- Taula-guia: vull X → escriu Y
| Vull… | Escriu |
|---|---|
| Columnes de dues taules relacionades | INNER JOIN |
| Columnes d'A, i les de B si existeixen | LEFT JOIN |
| Files d'A que tenen relació a B, sense duplicar | EXISTS (o IN) |
| Files d'A que no tenen relació a B | NOT EXISTS |
| Comparar cada fila amb un valor global | Subconsulta escalar al WHERE |
| Comparar cada fila amb un valor del seu grup | Correlacionada, o derivada + JOIN |
| Filtrar grups per un valor global | Subconsulta escalar al HAVING |
| Una mètrica calculada per fila | Escalar al SELECT |
| Tres o més mètriques per fila | LEFT JOIN + GROUP BY |
| Agregar sobre un agregat | Taula derivada al FROM |
| Mètriques de granularitat diferent | Dues taules derivades unides |
| El "top N" de cada grup | LATERAL, o funció de finestra (10-03) |
| Reutilitzar un càlcul o encadenar 3+ passos | CTE amb WITH (10-02) |
| Un rànquing, una posició, un acumulat | Funció de finestra (10-03) |
Errors habituals i consells
- Fer servir
JOINper a una pregunta d'existència. Multiplica files i t'obliga a unDISTINCTque amaga el problema: 20 files on en volies 12. - Creure que "les subconsultes són lentes". A PostgreSQL 16,
INiEXISTSes converteixen en semi-joins. El mite ve de motors i versions antics. - Reescriure per rendiment sense mesurar. Canviar una consulta llegible per una de críptica basant-te en una intuïció és la pitjor operació possible: perds llegibilitat i potser no hi guanyes res (08-05).
- Mantenir
NOT INperquè "avui funciona". La seva correcció depèn d'una propietat de l'esquema que pot canviar sense avisar. - Imbricar tres nivells quan existeix una CTE. Funciona, però ningú —tu inclòs— no la podrà modificar d'aquí a sis mesos.
- Repetir la mateixa expressió al
SELECT, alWHEREi alHAVING. Cada còpia és una oportunitat que una s'actualitzi i les altres no. - Consell: escriu primer la versió que s'assembli a la pregunta. Si la pregunta diu "clients que no han comprat", escriu
NOT EXISTS. La consulta que es llegeix com l'enunciat és la que menys errors amaga. - Consell: compta les files de cada versió abans de donar-ne una per bona. Si dues escriptures "equivalents" retornen 12 i 20 files, no eren equivalents.
- Consell: desa les dues versions quan dubtis. Deixa l'alternativa comentada amb una nota de per què vas triar l'altra. És la documentació més barata que existeix.
Exercicis
Exercici 1
Per a cadascuna d'aquestes cinc preguntes, decideix JOIN o subconsulta aplicant la regla base, i justifica-ho en una frase. No cal escriure el SQL complet.
- Productes amb la seva categoria i el seu proveïdor.
- Productes que han rebut alguna ressenya.
- Comandes l'import de les quals supera el tiquet mitjà global.
- Clients amb el nombre de comandes que han fet.
- Empleats que no han gestionat mai una comanda.
Exercici 2
Un company ha escrit això per a "els productes que s'han venut alguna vegada":
-- ⚠️ Sospitosa
SELECT DISTINCT p.id, p.nom, p.preu
FROM productes AS p
JOIN linies_comanda AS lc ON lc.producte_id = p.id
ORDER BY p.id;- El resultat és correcte? Quantes files retorna abans i després del
DISTINCT? - Reescriu-la amb
EXISTSi explica què s'hi guanya. - En quin cas el
JOINseria l'escriptura correcta per a una pregunta semblant?
Exercici 3
Pren aquesta consulta, que respon a "clients amb més d'una comanda i la seva facturació total":
SELECT c.id, c.nom,
(SELECT COUNT(*) FROM comandes co WHERE co.client_id = c.id) AS comandes,
(SELECT ROUND(SUM(lc.quantitat * lc.preu_unitari * (1 - lc.descompte)), 2)
FROM comandes co JOIN linies_comanda lc ON lc.comanda_id = co.id
WHERE co.client_id = c.id) AS facturacio
FROM clients c
WHERE (SELECT COUNT(*) FROM comandes co WHERE co.client_id = c.id) > 1
ORDER BY facturacio DESC;- Quantes execucions de subconsulta implica?
- Reescriu-la amb
JOINiGROUP BY, tenint cura del recompte. - Dona'n el resultat i digues quina versió defensaries en una revisió de codi.
Solucions
Solució 1
| # | Pregunta | Elecció | Per què |
|---|---|---|---|
| 1 | Productes amb categoria i proveïdor | JOIN (dos) |
El resultat mostra columnes de les tres taules |
| 2 | Productes amb alguna ressenya | Subconsulta (EXISTS) |
Només es mostren columnes de productes; el JOIN duplicaria l'oli, l'arròs i la crema |
| 3 | Comandes per sobre del tiquet mitjà | Subconsulta escalar + taula derivada | El llindar és un valor calculat, no una taula per unir |
| 4 | Clients amb el seu nombre de comandes | Les dues | Una mètrica: escalar al SELECT o LEFT JOIN + GROUP BY. Si calguessin tres mètriques, GROUP BY sense dubtar-ho |
| 5 | Empleats sense cap comanda | Subconsulta (NOT EXISTS) |
Pregunta d'absència, i empleat_id admet nuls: NOT IN donaria 0 files |
Solució 2
1. El resultat és correcte, però per accident del DISTINCT. Sense ell, el JOIN retorna 47 files —una per línia de comanda—, amb l'oli d'oliva repetit 5 vegades i l'arròs 4. Amb DISTINCT en queden 17 files, els 17 productes venuts. És a dir: el motor genera 47 files, les ordena o les fica en una taula hash, i en descarta 30. Feina feta per desfer-la.
2. Amb EXISTS:
-- ✅ CORRECTA
SELECT p.id, p.nom, p.preu
FROM productes AS p
WHERE EXISTS (SELECT 1 FROM linies_comanda AS lc WHERE lc.producte_id = p.id)
ORDER BY p.id;17 files directament, sense generar les 47 ni desduplicar. S'hi guanya: el resultat no es pot duplicar per construcció, la intenció queda explícita ("productes que tenen alguna venda"), l'EXISTS fa curtcircuit a la primera línia trobada, i el DISTINCT desapareix —amb ell, el risc que algú afegeixi demà una columna de linies_comanda al SELECT i el DISTINCT deixi de desduplicar sense que ningú ho noti.
3. El JOIN seria correcte tan bon punt la pregunta demanés alguna cosa de linies_comanda: "productes venuts amb les unitats de cada venda" (47 files, i són el resultat), o "productes venuts amb el total d'unitats" (17 files, amb GROUP BY). Tan bon punt necessitis dades de l'altra taula, la regla base mana.
Solució 3
1. Les execucions. Tres subconsultes per client —dues al SELECT i una de repetida al WHERE— × 15 clients = 45, de les quals 15 són un COUNT calculat dues vegades per client. Aquell COUNT duplicat és el símptoma clàssic: el WHERE no veu els àlies del SELECT (ordre lògic del mòdul 2), així que cal repetir l'expressió sencera.
2. La reescriptura:
-- ✅ Una sola passada
SELECT c.id,
c.nom,
COUNT(DISTINCT co.id) AS comandes,
ROUND(SUM(lc.quantitat * lc.preu_unitari * (1 - lc.descompte)), 2) AS facturacio
FROM clients AS c
JOIN comandes AS co ON co.client_id = c.id
JOIN linies_comanda AS lc ON lc.comanda_id = co.id
GROUP BY c.id, c.nom
HAVING COUNT(DISTINCT co.id) > 1
ORDER BY facturacio DESC;| id | nom | comandes | facturacio |
|---|---|---|---|
| 7 | Sofia | 2 | 111.88 |
| 1 | Lucía | 3 | 107.60 |
| 9 | Camille | 2 | 70.87 |
| 4 | Javier | 2 | 62.93 |
| 2 | Carlos | 2 | 59.46 |
| 6 | Pau | 2 | 57.33 |
| 5 | Ana | 2 | 54.85 |
7 clients, els mateixos que sortien a 04-06 amb HAVING COUNT(*) > 1. Dos detalls obligatoris: COUNT(DISTINCT co.id), perquè el segon JOIN multiplica cada comanda per les seves línies (la Lucía tindria 9 comandes en comptes de 3); i INNER JOIN en lloc de LEFT, que aquí és correcte perquè la condició > 1 ja exclou qui en té 0.
3. Quina defensar. La versió amb GROUP BY, sense dubtar-ho: una passada en lloc de 45, l'expressió de l'import escrita una sola vegada i el criteri de tall visible al HAVING al costat de la columna que mostra. La versió amb subconsultes té un únic avantatge real —no necessita COUNT(DISTINCT), perquè no multiplica files— i aquest avantatge no compensa repetir el COUNT en dos llocs. Si la consulta creixés fins a cinc mètriques, la diferència deixaria de ser discutible.
(I la versió ideal, amb el total de comanda calculat una sola vegada i anomenat, arriba a 10-02.)
Conclusió del mòdul
Tanques el mòdul 7 amb un criteri, no només amb sintaxi:
- La regla base: si necessites columnes de l'altra taula,
JOIN; si només filtres o calcules un valor, subconsulta. I si escriusJOIN ... DISTINCT, voliesEXISTS. INenfront d'INNER JOINno és una qüestió d'estil: són 12 files enfront de 20.INprova pertinença i respon una vegada per fila; elJOINprodueix una fila per parella.- Les quatre formes de respondre "què no casa" queden closes:
NOT EXISTSper omissió, anti-join si ja unies aquelles taules,EXCEPTper comparar conjunts, iNOT INmai per costum — perquè la seva correcció depèn d'una propietat de l'esquema que pot canviar sense avisar. - Una mètrica per fila cap bé en una escalar del
SELECT; tres o més demanenLEFT JOIN+GROUP BY, tenint cura delCOUNT(DISTINCT). - Correlacionada o taula derivada: la primera es llegeix com la pregunta, la segona calcula sis mitjanes en comptes de vint i et regala la columna de referència.
- L'optimitzador reescriu
IN,EXISTSiNOT EXISTScom a semi-joins i anti-joins, així que el seu rendiment sol ser equivalent al delJOIN;NOT INi les correlacionades alSELECTsón les que de debò es paguen. I res d'això no se suposa: es mesura ambEXPLAIN ANALYZE(08-05). - La llegibilitat és un criteri de primera classe: tres nivells d'imbricació o una expressió repetida són senyals que toca una CTE (10-02).
I amb això es tanca el mòdul 7. Repassa el que has guanyat en cinc lliçons: distingeixes una subconsulta no correlacionada d'una de correlacionada i saps que la segona s'avalua una vegada per fila; reconeixes les escalars, les de fila i les de taula, i els operadors que admet cadascuna; has resolt per fi la pregunta que 04-06 va deixar pendent —en Julien, la Sofia i en Tiago superen el tiquet mitjà de 36,40 €—; domines EXISTS i NOT EXISTS, inclosa la doble negació de la divisió relacional; col·loques subconsultes al SELECT, al FROM, al WHERE, al HAVING i a les instruccions del mòdul 5; i amb les taules derivades has liquidat per fi el problema dels ports que arrossegaves des del mòdul 3: 727,95 € + 118,25 € = 846,20 €, quadrat al cèntim.
Amb els set mòduls que portes, pots escriure gairebé qualsevol consulta que BotigaVerda necessiti. I justament per això, la pregunta important canvia. Fins ara ha estat sempre la mateixa: això retorna el que vull?. A partir d'aquí és una altra: quant triga?. Amb 20 comandes, 47 línies i 15 clients, tot el que has escrit respon en mil·lisegons, i tant se val que una subconsulta s'executi vint vegades o una de sola. Amb 20 milions de comandes, aquella mateixa consulta pot trigar minuts, bloquejar una pantalla d'administració o tombar un informe nocturn. Al mòdul 8, Índexs i rendiment, aprendràs què és un índex i com converteix un recorregut complet de la taula en una cerca dirigida, com crear-los i gestionar-los, quins tipus existeixen i —tan important com l'anterior— quan no indexar, les tècniques d'optimització de consultes, i EXPLAIN, l'eina que et dirà per fi, amb dades i no amb intuïcions, què està fent realment el motor amb el SQL que escrius.
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
