Hi ha una família de preguntes que no necessita cap valor: només un o un no. Ha comprat aquest client alguna vegada? Té aquest producte alguna ressenya? Conté aquesta comanda algun article de cosmètica? No importa quants, ni quins, ni quant sumen: importa si existeix almenys un.

EXISTS és l'operador que respon exactament això, i és probablement la subconsulta que més escriuràs en la teva vida professional. La seva negació, NOT EXISTS, resol l'altra meitat —clients que no han comprat, productes que ningú no ha venut— i ho fa amb una propietat que cap de les alternatives no té: és immune als valors nuls. En aquesta lliçó veuràs totes dues, les compararàs amb NOT IN, amb l'anti-join de 03-03 i amb EXCEPT de 03-07, i tancaràs amb el problema clàssic de la divisió relacional.

Contingut

  1. EXISTS: un predicat, no un valor
  2. Per què tant se val el que posi al SELECT intern
  3. Quatre casos de BotigaVerda amb EXISTS
  4. NOT EXISTS: els quatre buits del conjunt de dades
  5. NOT EXISTS enfront de NOT IN: la diferència que importa
  6. Les quatre formes de respondre "què no casa"
  7. Curtcircuit: EXISTS enfront de COUNT(*) > 0
  8. Divisió relacional: la doble negació
  9. Errors habituals i consells
  10. Exercicis
  11. Conclusió

  1. EXISTS: un predicat, no un valor

EXISTS (subconsulta) és un predicat: retorna TRUE o FALSE, mai NULL. La seva regla és d'una simplicitat radical:

EXISTS és TRUE tan bon punt la subconsulta produeix la seva primera fila. Si no en produeix cap, és FALSE.

SELECT c.id, c.nom, c.cognoms
FROM clients AS c
WHERE EXISTS (SELECT 1 FROM comandes AS co WHERE co.client_id = c.id)
ORDER BY c.id
LIMIT 4;
id nom cognoms
1 Lucía Martínez Soler
2 Carlos Ferrer Ibáñez
3 Marta Sanchis Gil
4 Javier Ortega Ruiz

(4 primeres de 12 files: els 12 clients que han comprat alguna vegada.)

Tres propietats el distingeixen de tot el que has vist: retorna TRUE/FALSE i mai NULL, així que és immune a la lògica de tres valors de 04-03; no mira el contingut de la subconsulta, tant se val quines columnes retorni; i s'atura a la primera fila, no compta ni suma ni ordena.

I una quarta, decisiva: EXISTS és correlacionat a la pràctica sempre. Un EXISTS sense correlació —WHERE EXISTS (SELECT 1 FROM comandes)— és TRUE o FALSE per a totes les files per igual, així que retorna tota la taula o cap fila. No és cap error, però tampoc no serveix de res. La condició co.client_id = c.id és la que fa la feina: converteix "hi ha comandes?" en "hi ha comandes d'aquest client?".

  1. Per què tant se val el que posi al SELECT intern

Aquestes quatre escriptures són exactament equivalents i produeixen el mateix pla d'execució:

WHERE EXISTS (SELECT 1        FROM comandes AS co WHERE co.client_id = c.id)
WHERE EXISTS (SELECT *        FROM comandes AS co WHERE co.client_id = c.id)
WHERE EXISTS (SELECT co.id    FROM comandes AS co WHERE co.client_id = c.id)
WHERE EXISTS (SELECT 1/0      FROM comandes AS co WHERE co.client_id = c.id)

L'última n'és la demostració: 1/0 és una divisió per zero i no dona error, perquè PostgreSQL no avalua mai la llista de columnes d'un EXISTS. Només comprova si la consulta produeix files. La projecció es descarta abans de calcular-se.

D'aquí la fórmula SELECT 1, la que veuràs al 90 % del codi professional: és la manera més curta de dir "no m'interessa el contingut". SELECT * és igual de correcte i algunes guies el prefereixen perquè subratlla el mateix. Tria'n una i sigues coherent; en aquest curs fem servir SELECT 1.

El que sí que importa dins de l'EXISTS és el WHERE: la correlació. Un EXISTS el WHERE del qual no esmenta la fila externa està mal plantejat gairebé segur.

  1. Quatre casos de BotigaVerda amb EXISTS

Productes amb almenys una ressenya. L'EXISTS no diu quantes ni de quina puntuació: només que n'hi ha alguna.

SELECT p.id, p.nom AS producte, p.preu
FROM productes AS p
WHERE EXISTS (SELECT 1 FROM ressenyes AS r WHERE r.producte_id = p.id)
ORDER BY p.id;
id producte preu
1 Oli d'oliva verge extra 500 ml 12.50
2 Arròs integral ecològic 1 kg 3.90
5 Tomàquet triturat ecològic 400 g 1.95
6 Crema facial d'àloe vera 50 ml 18.90
10 Detergent ecològic concentrat 1 L 11.20

(5 primeres de 9 files; les altres quatre són els productes 12, 15, 16 i 18.) 9 dels 20 productes. Compara-ho amb un INNER JOIN sobre ressenyes: aquell hauria retornat 12 files, una per ressenya, amb l'oli, l'arròs i la crema repetits. EXISTS no multiplica mai files, i aquest és el seu avantatge més pràctic enfront del JOIN.

Comandes que contenen algun producte de cosmètica natural. Aquí la subconsulta porta el seu propi JOIN:

SELECT co.id AS comanda_id, co.data_comanda, c.nom || ' ' || c.cognoms AS client, co.estat
FROM comandes AS co
JOIN clients  AS c ON co.client_id = c.id
WHERE EXISTS (SELECT 1
              FROM linies_comanda AS lc
              JOIN productes AS p ON p.id = lc.producte_id
              WHERE lc.comanda_id = co.id
                AND p.categoria_id = 2)
ORDER BY co.id;
comanda_id data_comanda client estat
2 2025-03-12 Carlos Ferrer Ibáñez lliurat
6 2025-05-23 Ana Belmonte Roca cancellat
9 2025-07-15 Tiago Almeida Nunes lliurat
10 2025-08-03 Camille Dubois lliurat
15 2025-12-02 Lucía Martínez Soler lliurat
18 2026-01-27 Ana Belmonte Roca pagat

6 comandes de 20. La comanda 9 conté dos articles de cosmètica (xampú i bàlsam) i apareix una sola vegada: amb JOIN n'haurien sortit 7 files i hauria calgut un DISTINCT. Empleats que han gestionat alguna comanda, mateix patró:

SELECT e.id, e.nom || ' ' || e.cognoms AS empleat, e.carrec
FROM empleats AS e
WHERE EXISTS (SELECT 1 FROM comandes AS co WHERE co.empleat_id = e.id)
ORDER BY e.id;
id empleat carrec
4 Óscar Peris Blasco Comercial
5 Laia Puig Sanchis Comercial
6 Marc Estévez Roig Atenció al client

3 dels 8 empleats, exactament els que anunciava 01-06.

Nota de dialecte: a PostgreSQL EXISTS retorna un boolean de debò, així que el pots fer servir com a columna: SELECT c.id, EXISTS (SELECT 1 FROM comandes co WHERE co.client_id = c.id) AS ha_comprat FROM clients c retorna true/false per als 15 clients. SQL Server i Oracle no ho permeten i obliguen a embolcallar-ho en un CASE WHEN EXISTS (...) THEN 1 ELSE 0 END. MySQL 8 i SQLite sí que ho accepten, retornant 1 o 0.

  1. NOT EXISTS: els quatre buits del conjunt de dades

NOT EXISTS és la negació literal: TRUE quan la subconsulta no produeix cap fila. És l'eina natural per a totes les preguntes amb "sense", "mai" o "cap", i respon els quatre buits deliberats de BotigaVerda amb la mateixa plantilla.

-- Clients que no han comprat mai
SELECT c.id, c.nom, c.cognoms, c.ciutat, c.data_registre
FROM clients AS c
WHERE NOT EXISTS (SELECT 1 FROM comandes AS co WHERE co.client_id = c.id)
ORDER BY c.id;
id nom cognoms ciutat data_registre
13 Núria Bosch Ferrer Barcelona 2025-06-20
14 Hugo Iglesias Pardo Saragossa 2025-09-12
15 Inés Carrasco Vega València 2026-01-08

I la mateixa plantilla, canviant només la taula de dins, dona els productes mai venuts:

SELECT p.id, p.nom, p.preu, p.stock, p.actiu
FROM productes AS p
WHERE NOT EXISTS (SELECT 1 FROM linies_comanda AS lc WHERE lc.producte_id = p.id)
ORDER BY p.id;
id nom preu stock actiu
13 Espelmes de cera de soja (pack 2) 13.75 0 true
19 Desodorant natural en barra 50 g 7.80 75 true
20 Càpsules d'espirulina 120 u 16.40 55 false

Amb NOT EXISTS (SELECT 1 FROM ressenyes AS r WHERE r.producte_id = p.id) sobre productes obtens els 11 productes sense ressenya, i amb NOT EXISTS (SELECT 1 FROM comandes AS co WHERE co.empleat_id = e.id) sobre empleats, els 5 empleats sense comandes (Rosa, Andrés, Beatriz, Irene i Daniel).

Quatre preguntes de negoci, una sola plantilla. Aquesta uniformitat és la raó principal per preferir NOT EXISTS: no cal decidir quina columna comprovar amb IS NULL, ni amoïnar-se pels nuls, ni recordar quina taula va a l'esquerra.

  1. NOT EXISTS enfront de NOT IN: la diferència que importa

Ara la demostració central del mòdul. La mateixa pregunta, dues escriptures, dos resultats diferents.

-- ⚠️ INCORRECTA: 0 files
SELECT e.id, e.nom, e.cognoms
FROM empleats AS e
WHERE e.id NOT IN (SELECT empleat_id FROM comandes);

-- ✅ CORRECTA: 5 files
SELECT e.id, e.nom, e.cognoms
FROM empleats AS e
WHERE NOT EXISTS (SELECT 1 FROM comandes AS co WHERE co.empleat_id = e.id);
id nom cognoms
1 Rosa Alcázar Vives
2 Andrés Company Talens
3 Beatriz Nadal Ripoll
7 Irene Salvador Mira
8 Daniel Vercher Lluch

La primera retorna zero files i la segona cinc. La causa, que ja vas diagnosticar a 07-01, són les 10 comandes del canal web amb empleat_id a NULL: la llista és {4, 5, 6, NULL} i 1 <> NULL és UNKNOWN, així que NOT IN no pot ser TRUE per a cap fila. NOT EXISTS no pateix el mateix perquè no compara el valor extern amb una llista: pregunta si la subconsulta produeix files. Per a l'empleada 1, SELECT 1 FROM comandes WHERE empleat_id = 1 no en produeix cap —les files amb empleat_id nul tampoc no satisfan aquella igualtat i es descarten com qualsevol altra que no casi— i NOT EXISTS és TRUE. La lògica de tres valors actua dins de la subconsulta, on només decideix quines files es retornen, i mai no s'escapa al predicat.

flowchart LR
    A["e.id = 1"] --> B{"NOT IN (4,5,6,NULL)"}
    B --> C["UNKNOWN → ❌ descartada"]
    A --> D{"NOT EXISTS<br/>(comandes amb empleat_id = 1)"}
    D --> E["0 files → TRUE → ✅ conservada"]

La regla, en una frase: NOT EXISTS és segur amb nuls i NOT IN no. Si tries NOT EXISTS per costum, mai no t'hauràs de preguntar si la columna de la subconsulta admet nuls.

  1. Les quatre formes de respondre "què no casa"

A 03-07 es va anunciar que les quatre escriptures es compararien al mòdul 7. Aquí les tens, resolent la mateixa pregunta —clients que no han comprat mai— i retornant totes les mateixes 3 files (13, 14 i 15):

-- 1. NOT EXISTS
SELECT c.id, c.nom FROM clients AS c
WHERE NOT EXISTS (SELECT 1 FROM comandes AS co WHERE co.client_id = c.id);
-- 2. NOT IN
SELECT c.id, c.nom FROM clients AS c
WHERE c.id NOT IN (SELECT client_id FROM comandes);
-- 3. Anti-join (03-03)
SELECT c.id, c.nom FROM clients AS c
LEFT JOIN comandes AS co ON co.client_id = c.id WHERE co.id IS NULL;
-- 4. EXCEPT (03-07)
SELECT id FROM clients EXCEPT SELECT client_id FROM comandes;
NOT EXISTS NOT IN Anti-join EXCEPT
Comportament amb NULL Segur Perillós: 0 files Segur Segur (tracta NULL com un valor més)
Columnes que pot retornar Totes les de fora Totes les de fora Totes les de fora Només les comparades
Elimina duplicats? No en crea No en crea No en crea Sí, sempre
Llegibilitat Alta: es llegeix com la pregunta Molt alta… fins que falla Mitjana: cal saber la fórmula Alta, però limitada
Rendiment típic Anti-join al pla Pitjor si hi ha nuls possibles Anti-join al pla Requereix ordenar/desduplicar
Portabilitat Universal Universal Universal EXCEPT no existeix a MySQL 5.7 ni a Oracle (allà és MINUS)

La recomanació del curs: fes servir NOT EXISTS. És segur, universal, deixa disponibles totes les columnes de la taula externa i es llegeix igual que la pregunta de negoci. L'anti-join és igual d'idiomàtic i de vegades més natural si ja estaves unint aquelles taules. EXCEPT, només quan comparis conjunts de la mateixa forma i no necessitis més columnes. I NOT IN, únicament si garanteixes que la columna de la subconsulta és NOT NULL — i encara aleshores, no hi guanyes res. Aquesta taula es tanca i s'amplia amb el criteri de rendiment a 07-05.

  1. Curtcircuit: EXISTS enfront de COUNT(*) > 0

És temptador escriure "n'hi ha algun?" com a "el recompte és més gran que zero?". Funciona i dona el mateix resultat —12 files totes dues—, però no la mateixa feina:

-- ⚠️ Correcta però pitjor
SELECT c.id, c.nom FROM clients AS c
WHERE (SELECT COUNT(*) FROM comandes AS co WHERE co.client_id = c.id) > 0;
-- ✅ Preferible
SELECT c.id, c.nom FROM clients AS c
WHERE EXISTS (SELECT 1 FROM comandes AS co WHERE co.client_id = c.id);
COUNT(*) > 0 EXISTS
Files que llegeix la subconsulta Totes les del client Una: s'atura a la primera
Amb un client de 50.000 comandes En compta 50.000 En llegeix 1
Què comunica al lector "quantes n'hi ha, i compara-ho" "n'hi ha alguna?"

EXISTS fa curtcircuit: tan bon punt troba una fila, deixa de buscar; COUNT no pot, perquè per saber quantes n'hi ha les ha de veure totes. Amb 20 comandes la diferència és inmesurable; amb taules reals separa una consulta instantània d'un recorregut complet. I hi ha un tercer argument, el més important en el dia a dia: EXISTS diu el que vols dir. El mateix amb la negació: COUNT(*) = 0 és NOT EXISTS escrit d'una manera més cara.

  1. Divisió relacional: la doble negació

Arribem al problema clàssic. La pregunta sembla innocent:

Quins clients han comprat productes de totes les categories?

I no es pot respondre amb EXISTS a seques, perquè EXISTS parla d'"algun", no de "tots". El truc és reformular la frase fins a convertir el "tots" en dos "caps" encadenats:

"ha comprat de TOTES les categories"
≡ "NO hi ha CAP categoria de la qual NO hagi comprat"

Aquesta reformulació —que en lògica és ∀x P(x) ≡ ¬∃x ¬P(x)— es tradueix literalment a SQL:

SELECT c.id, c.nom || ' ' || c.cognoms AS client
FROM clients AS c
WHERE NOT EXISTS (                              -- no hi ha cap categoria…
        SELECT 1
        FROM categories AS cat
        WHERE NOT EXISTS (                      -- …de la qual aquest client no hagi comprat
                SELECT 1
                FROM comandes AS co
                JOIN linies_comanda AS lc ON lc.comanda_id = co.id
                JOIN productes      AS p  ON p.id = lc.producte_id
                WHERE co.client_id   = c.id
                  AND p.categoria_id = cat.id))
ORDER BY c.id;
(0 files)

Cap client, i el resultat no és decebedor sinó diagnòstic: Complements no ha venut mai (0,00 € a les xifres del curs), així que ningú no pot haver comprat de les sis categories. La consulta és correcta; el que falla és la pregunta. Reformulada sobre les quatre categories principals —Alimentació, Cosmètica natural, Llar sostenible i Begudes— n'hi ha prou d'acotar la subconsulta intermèdia:

        SELECT 1
        FROM categories AS cat
        WHERE cat.id IN (1, 2, 3, 4)
          AND NOT EXISTS ( ... )
id client
1 Lucía Martínez Soler

Una sola clienta. La Lucía és l'única que ha comprat de les quatre categories principals, i ho ha aconseguit amb tres comandes molt diferents entre si: la 1 (oli, arròs i infusió), la 5 (detergent, fregall i bosses) i la 15 (mel, xampú i fregall). És exactament el perfil que un equip de màrqueting voldria identificar: el client que ha explorat el catàleg sencer.

Com es llegeix aquesta consulta sense marejar-se

Llegeix-la de dins cap enfora, i en tres frases:

Nivell Què pregunta Per a qui
Intern (NOT EXISTS 2) "aquest client no ha comprat res d'aquesta categoria?" Cada parella client-categoria
Intermedi (FROM categories) "hi ha alguna categoria que compleixi l'anterior?" Cada client
Extern (NOT EXISTS 1) "cap no la compleix?" → aleshores les va comprar totes Cada client

I un advertiment: hi ha dues correlacions en joc. La interna fa servir c.id (dos nivells cap enfora) i cat.id (un). Si n'oblides una, la consulta continua essent vàlida i retorna una barbaritat. Prova la subconsulta interna amb valors fixos (c.id = 1, cat.id = 3) abans d'acoblar les tres capes.

Nota: hi ha una alternativa més llegible amb GROUP BY i HAVING COUNT(DISTINCT p.categoria_id) = 4, més curta i sovint més ràpida. La doble negació mereix entendre's perquè és l'única que funciona quan el conjunt de referència no és un simple recompte —"totes les categories actives", "tots els productes d'un catàleg que canvia"— i perquè és la fórmula que reconeixeràs en llegir codi d'altri.

Errors habituals i consells

  • Escriure un EXISTS sense correlació. WHERE EXISTS (SELECT 1 FROM comandes) és TRUE per a totes les files i retorna la taula sencera: la condició que enllaça amb la fila externa és l'essencial.
  • Creure que SELECT * dins d'un EXISTS és més lent. No ho és: la llista de columnes no s'avalua mai, i SELECT 1 i SELECT * produeixen el mateix pla.
  • Fer servir NOT IN sobre una columna que admet nuls. Zero files, sense cap avís; NOT EXISTS no té aquest problema mai. I escriure COUNT(*) > 0 compta totes les files per respondre una cosa que es contesta amb la primera.
  • Posar un ORDER BY o un LIMIT dins d'un EXISTS. No canvien el resultat i afegeixen feina. I confondre NOT EXISTS amb EXISTS (... WHERE NOT ...). "No té cap comanda lliurada" és NOT EXISTS (... estat = 'lliurat'); "té alguna comanda no lliurada" és EXISTS (... estat <> 'lliurat'). Són preguntes diferents.
  • Consell: tradueix la pregunta paraula per paraula. "Clients que han comprat" → EXISTS. "Clients que no han comprat mai" → NOT EXISTS. "Clients que han comprat de totes" → NOT EXISTS ( ... NOT EXISTS ( ... )). I aquí, compte a no oblidar una de les dues correlacions: no dona error i el resultat és brossa.
  • Consell: fes servir EXISTS quan el JOIN t'obligaria a un DISTINCT. Si només vols saber si hi ha relació i no necessites dades de l'altra taula, evita la multiplicació de files d'arrel. I prova sempre les subconsultes amb valors fixos —substitueix c.id per 1— abans de muntar la correlació.

Exercicis

Exercici 1

Atenció al client vol una campanya d'opinions dirigida a clients que han comprat alguna vegada però no han escrit mai una ressenya. Escriu la consulta amb EXISTS i NOT EXISTS a la mateixa clàusula WHERE, mostrant id, nom complet, país i nombre de comandes.

Després respon: quants clients queden fora per cadascuna de les dues condicions?

Exercici 2

Un company necessita les comandes que no contenen cap producte d'alimentació i ha escrit això:

-- ⚠️ Sospitosa
SELECT DISTINCT co.id
FROM comandes AS co
JOIN linies_comanda AS lc ON lc.comanda_id = co.id
JOIN productes      AS p  ON p.id = lc.producte_id
WHERE p.categoria_id <> 1;
  1. Explica per què està malament i quina pregunta respon realment.
  2. Escriu-la correctament amb NOT EXISTS i dona el nombre de files.
  3. La podries resoldre amb un anti-join? I amb NOT IN? Justifica si serien segures.

Exercici 3

Compres vol saber quins proveïdors no tenen cap producte sense vendre: aquells dels quals totes les seves referències s'han venut almenys una vegada. Escriu-ho amb doble NOT EXISTS. (Pista: mateixa estructura que la divisió relacional, però el conjunt de referència són els productes del mateix proveïdor.)

Solucions

Solució 1

SELECT c.id,
       c.nom || ' ' || c.cognoms AS client,
       c.pais,
       (SELECT COUNT(*) FROM comandes AS co WHERE co.client_id = c.id) AS comandes
FROM clients AS c
WHERE EXISTS     (SELECT 1 FROM comandes  AS co WHERE co.client_id = c.id)
  AND NOT EXISTS (SELECT 1 FROM ressenyes AS r  WHERE r.client_id  = c.id)
ORDER BY c.id;
id client pais comandes
5 Ana Belmonte Roca Espanya 2
10 Julien Moreau França 1
12 Diego Ramos Herrera Espanya 1

3 clients. El repartiment dels 15:

Condició Descarta Qui
EXISTS (comandes) 3 Núria, Hugo i Inés: no han comprat mai, no tenen res per ressenyar
NOT EXISTS (ressenyes) 9 Els 9 clients que ja han escrit alguna ressenya
Sobreviuen 3 Ana, Julien i Diego

Els tres són objectius perfectes: han comprat, estan satisfets o no ho sabem, i no els hem demanat mai la seva opinió. Fixa't que les dues condicions són inseparables: només amb NOT EXISTS (ressenyes) en sortirien 6, incloent-hi tres que no han comprat res.

Solució 2

1. Què està malament. La consulta respon a "comandes que contenen algun producte que no és d'alimentació", que és gairebé el contrari. Una comanda amb oli (categoria 1) i kombutxa (categoria 4) té una línia que compleix categoria_id <> 1, així que hi apareix — encara que sí que porta alimentació. L'error és de quantificador: el WHERE d'un JOIN filtra línies, i la pregunta parla de comandes. El DISTINCT dissimula el símptoma (les files repetides) sense tocar-ne la causa.

2. La versió correcta:

-- ✅ CORRECTA
SELECT co.id AS comanda_id, co.data_comanda, co.estat
FROM comandes AS co
WHERE NOT EXISTS (SELECT 1
                  FROM linies_comanda AS lc
                  JOIN productes AS p ON p.id = lc.producte_id
                  WHERE lc.comanda_id = co.id
                    AND p.categoria_id = 1)
ORDER BY co.id;
comanda_id data_comanda estat
2 2025-03-12 lliurat
5 2025-05-07 lliurat
7 2025-06-11 lliurat
9 2025-07-15 lliurat
10 2025-08-03 lliurat

(5 primeres de 10 files; les altres són les comandes 12, 13, 16, 18 i 19.) 10 comandes de 20 no porten ni un sol producte d'alimentació. La consulta original en retornava 18 —totes les que tenen alguna línia d'una altra categoria—, i vuit d'elles sí que compren alimentació. Observa el canvi de lògica: la condició p.categoria_id = 1 s'escriu en positiu dins de l'EXISTS, i la negació l'aporta el NOT. Aquest és el patró de tota pregunta amb "cap".

3. Amb anti-join, sí: FROM comandes co LEFT JOIN (linies_comanda lc JOIN productes p ON p.id = lc.producte_id AND p.categoria_id = 1) ON lc.comanda_id = co.id WHERE lc.id IS NULL funciona, però cal portar la condició de categoria a l'ON (si va al WHERE degrada el LEFT a INNER, 03-03), cosa que la fa força més fràgil d'escriure. Amb NOT IN també funcionariaco.id NOT IN (SELECT lc.comanda_id FROM linies_comanda lc JOIN productes p ON ... WHERE p.categoria_id = 1)— i seria segura perquè linies_comanda.comanda_id és NOT NULL. Però continuaries depenent d'una garantia de l'esquema que demà pot canviar: NOT EXISTS no depèn de res.

Solució 3

SELECT pr.id, pr.nom AS proveidor, pr.pais
FROM proveidors AS pr
WHERE NOT EXISTS (
        SELECT 1
        FROM productes AS p
        WHERE p.proveidor_id = pr.id
          AND NOT EXISTS (SELECT 1 FROM linies_comanda AS lc
                          WHERE lc.producte_id = p.id))
ORDER BY pr.id;
id proveidor pais
1 Huerta del Turia Espanya
2 BioSierra Ibérica Espanya
3 Verde Atlántico Portugal

3 proveïdors de 5. Quadra amb els tres productes mai venuts: el 19 (Desodorant) és de Maison Nature, i el 13 (Espelmes) i el 20 (Espirulina) són d'EcoNordic Supplies. Aquests dos en queden fora; els altres tres han col·locat el seu catàleg sencer.

L'estructura és idèntica a la de la secció 8 amb una única diferència: el conjunt de referència està correlacionat (p.proveidor_id = pr.id) en lloc de ser la taula sencera de categories. És la variant més útil del patró a la pràctica, i la seva lectura literal és "no hi ha cap producte seu del qual no existeixi cap venda".

Conclusió

EXISTS és la subconsulta que més escriuràs:

  • EXISTS és un predicat: retorna TRUE tan bon punt la subconsulta produeix una fila, i mai NULL. No mira les columnes del SELECT intern —d'aquí la fórmula SELECT 1, i fins i tot SELECT 1/0 funciona— i fa curtcircuit.
  • A la pràctica sempre és correlacionat: la condició que enllaça amb la fila externa és el que li dona sentit.
  • No multiplica files. Els 9 productes amb ressenya surten 9 vegades, no 12; les 6 comandes amb cosmètica surten 6, no 7. On un JOIN demanaria DISTINCT, EXISTS no el necessita.
  • NOT EXISTS resol els quatre buits de BotigaVerda amb una sola plantilla: 3 clients sense comprar, 3 productes sense vendre, 11 productes sense ressenya i 5 empleats sense comandes. I sobretot: és segur amb nuls i NOT IN no — la mateixa pregunta dona 0 files amb NOT IN i 5 amb NOT EXISTS, per les 10 comandes web amb empleat_id nul.
  • Coneixes les quatre formes de respondre "què no casa" —NOT EXISTS, NOT IN, anti-join i EXCEPT— amb les seves diferències en nuls, columnes disponibles, duplicats i portabilitat; la recomanació és NOT EXISTS. I saps traduir un "tots" amb la doble negació: cap client no ha comprat de les sis categories (Complements no va vendre mai) i només la Lucía ho ha fet de les quatre principals.

A la lliçó següent, subconsultes a SELECT, FROM i WHERE, deixaràs de mirar el què per mirar el on: què canvia segons la clàusula on col·loques la subconsulta, per què una taula derivada al FROM necessita àlies obligatòriament, com s'agrega dues vegades seguides per arribar per fi al tiquet mitjà de 36,40 € des del seu origen, i com es creuen dos agregats de granularitat diferent perquè els 727,95 € de producte i els 118,25 € de ports quadrin sense inflar-se.

Curs de SQL

Mòdul 1: Introducció a SQL

Mòdul 2: Consultes bàsiques de SQL

Mòdul 3: Treballar amb múltiples taules

Mòdul 4: Filtratge avançat de dades

Mòdul 5: Manipulació de dades

Mòdul 6: Funcions avançades de SQL

Mòdul 7: Subconsultes i consultes imbricades

Mòdul 8: Índexs i optimització del rendiment

Mòdul 9: Transaccions i concurrència

Mòdul 10: Temes avançats

Mòdul 11: SQL a la pràctica

Mòdul 12: Projecte final

© Copyright 2026. Tots els drets reservats