Ce face LAG() și LEAD()
LAG() și LEAD() sunt funcții fereastră de deplasare (offset window functions) care îți permit să citești date dintr-un rând anterior (LAG) sau dintr-un rând următor (LEAD) față de rândul curent, în cadrul aceleiași partiții.
Sunt indispensabile în analizele financiare și de serii temporale: calcularea creșterii procentuale de la o lună la alta (MoM / YoY), detectarea modificărilor de preț sau măsurarea timpului scurs între două evenimente succesive ale aceluiași utilizator.
Clauze, operatori și argumente
| Clauză / Operator | Descriere |
|---|---|
LAG(expr, offset, default) |
Returnează valoarea expresiei de pe rândul aflat cu offset poziții în urmă (implicit offset = 1, default = NULL). |
LEAD(expr, offset, default) |
Returnează valoarea expresiei de pe rândul aflat cu offset poziții înainte (după rândul curent). |
FIRST_VALUE(expr) / LAST_VALUE(expr) |
Returnează prima sau ultima valoare din cadrul ferestrei curente. |
OVER (PARTITION BY ... ORDER BY ...) |
Izolează seria pe fiecare entitate (ex: per produs sau per client) și stabilește ordinea cronologică. |
Exemple
Exemplul 1: Calcularea variației lunare a încasărilor (Month-over-Month) cu LAG()
Aducem încasările lunii precedente pe același rând cu luna curentă și calculăm diferența absolută și procentuală.
SELECT luna,
incasari,
LAG(incasari, 1) OVER (ORDER BY luna) AS luna_anterioara,
incasari - LAG(incasari, 1) OVER (ORDER BY luna) AS diferenta,
ROUND(
100.0 * (incasari - LAG(incasari, 1) OVER (ORDER BY luna))
/ NULLIF(LAG(incasari, 1) OVER (ORDER BY luna), 0),
2
) AS crestere_proc
FROM venituri_lunare
ORDER BY luna;
Rezultat interogare
luna | incasari | luna_anterioara | diferenta | crestere_proc
---------+-----------+-----------------+-----------+---------------
2025-01 | 120000.00 | | |
2025-02 | 135000.00 | 120000.00 | 15000.00 | 12.50
2025-03 | 128250.00 | 135000.00 | -6750.00 | -5.00
(3 rows)
Exemplul 2: Durata dintre două comenzi consecutive ale aceluiași client cu LEAD()
Folosim LEAD() partiționat pe client_id pentru a vedea data următoarei comenzi a fiecărui client.
SELECT client_id,
id AS comanda_id,
plasata_la::date AS data_curenta,
LEAD(plasata_la::date, 1) OVER (
PARTITION BY client_id
ORDER BY plasata_la
) AS data_urmatoarei_comenzi
FROM comenzi
WHERE client_id = 101
ORDER BY plasata_la;
Rezultat interogare
client_id | comanda_id | data_curenta | data_urmatoarei_comenzi
-----------+------------+--------------+-------------------------
101 | 4910 | 2025-01-15 | 2025-02-04
101 | 4988 | 2025-02-04 | 2025-03-14
101 | 5042 | 2025-03-14 |
(3 rows)
Greșeli frecvente
- Uitarea clauzei
PARTITION BYcând tabelul conține mai multe entități (ex: mai mulți clienți sau produse): fărăPARTITION BY, primul rând al clientului 2 va prelua prinLAG()ultimul rând al clientului 1. - Neutilizarea lui
NULLIF(..., 0)la împărțirea cu valoarea returnată deLAG()pentru a preveni eroareadivision by zero.
Comenzi și funcții înrudite
ROW_NUMBER(), RANK() și DENSE_RANK(): Numerotează și clasifică rândurile în interiorul unei partiții fără a le colapsa ca GROUP BY.SUM() / AVG() OVER (PARTITION BY): Calculează totaluri cumulate (running totals), medii mobile și procente din totalul grupului folosind ferestre.COALESCE, NULLIF și CASE WHEN: Gestionează valorile NULL, previne împărțirea la zero și evaluează logica condițională direct în expresiile SQL.