Ce face JOIN și CROSS JOIN LATERAL
Pe lângă joncțiunile relaționale standard (INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN), PostgreSQL suportă cuvântul cheie LATERAL. O subinterogare sau o funcție de tabel precedată de LATERAL este evaluată pentru fiecare rând din tabelul aflat în stânga ei și poate accesa direct coloanele acelui rând.
CROSS JOIN LATERAL (sau LEFT JOIN LATERAL ... ON true când vrem să păstrăm rândurile fără corespondent) este soluția ideală pentru problema „Top N pe fiecare grup” sau pentru apelarea funcțiilor care returnează seturi de rânduri (precum jsonb_array_elements() sau unnest()).
Clauze, operatori și argumente
| Clauză / Operator | Descriere |
|---|---|
INNER JOIN ... ON |
Returnează doar rândurile care au corespondent în ambele tabele conform condiției ON. |
LEFT JOIN ... ON |
Returnează toate rândurile din tabelul stâng și completează cu NULL coloanele din dreapta dacă nu există potrivire. |
CROSS JOIN LATERAL (...) |
Evaluează subinterogarea din dreapta pentru fiecare rând din stânga; exclude rândurile din stânga pentru care subinterogarea nu returnează nimic. |
LEFT JOIN LATERAL (...) ON true |
La fel ca CROSS JOIN LATERAL, dar păstrează și rândurile din tabelul stâng pentru care subinterogarea returnează 0 rânduri. |
Exemple
Exemplul 1: Ultimele 2 comenzi pentru fiecare client (Top-N per grup cu LATERAL)
Pentru fiecare client din tabelul clienti, subinterogarea LATERAL caută rapid cele mai recente 2 comenzi folosind indexul pe (client_id, plasata_la DESC).
SELECT c.id AS client_id,
c.nume,
ult.id AS comanda_id,
ult.total_lei,
ult.plasata_la
FROM clienti c
LEFT JOIN LATERAL (
SELECT id, total_lei, plasata_la
FROM comenzi cmd
WHERE cmd.client_id = c.id
ORDER BY cmd.plasata_la DESC
LIMIT 2
) ult ON true
ORDER BY c.id, ult.plasata_la DESC;
Rezultat interogare
client_id | nume | comanda_id | total_lei | plasata_la
-----------+---------------+------------+-----------+---------------------
1 | Ana Marinescu | 912 | 350.00 | 2025-03-15 11:20:00
1 | Ana Marinescu | 884 | 120.00 | 2025-03-02 17:05:00
2 | Mihai Dobre | 905 | 890.00 | 2025-03-10 09:30:00
3 | Ioana Pavel | | |
(4 rows)
Exemplul 2: Reutilizarea expresiilor calculate în lanț cu CROSS JOIN LATERAL
Definim formule intermediare într-un bloc LATERAL (VALUES (...)) ca să nu repetăm expresii complexe în SELECT și WHERE.
SELECT p.denumire,
p.pret_baza,
calc.pret_redus,
ROUND(calc.pret_redus * 1.19, 2) AS pret_cu_tva
FROM produse p
CROSS JOIN LATERAL (
VALUES (ROUND(p.pret_baza * (1 - p.discount_proc / 100.0), 2))
) AS calc(pret_redus)
WHERE calc.pret_redus < 500;
Rezultat interogare
denumire | pret_baza | pret_redus | pret_cu_tva
---------------------+-----------+------------+-------------
SSD NVMe 1TB | 450.00 | 405.00 | 481.95
Mouse Wireless Pro | 320.00 | 256.00 | 304.64
(2 rows)
Greșeli frecvente
- Folosirea
CROSS JOIN LATERALatunci când unii părinți nu au rânduri copil: acei părinți vor dispărea complet din rezultat; foloseșteLEFT JOIN LATERAL (...) ON truedacă vrei să îi păstrezi. - Omiterea unui index pe coloana de legătură și ordonare din subinterogarea
LATERAL, ceea ce ar forța câte o scanare secvențială pentru fiecare rând din tabelul stâng.
Comenzi și funcții înrudite
SELECT și DISTINCT ON: Extrage rânduri din tabele și păstrează doar primul rând pentru fiecare grup de valori distincte.ROW_NUMBER(), RANK() și DENSE_RANK(): Numerotează și clasifică rândurile în interiorul unei partiții fără a le colapsa ca GROUP BY.ARRAY, ANY() și unnest(): Stochează și manipulează tablouri tipizate (text[], int[]), verifică apartenența cu ANY() și le extinde în rânduri cu unnest().