LAG() și LEAD() în PostgreSQL

CTE și Window Functions PostgreSQL 8.4+ (standard în PostgreSQL 15–17)

Accesează valoarea unei coloane de pe rândul anterior (LAG) sau următor (LEAD) fără a face self-join.

Sintaxă SQL
LAG(expresie [, offset [, valoare_implicita]]) OVER (PARTITION BY ... ORDER BY ...)

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 BY câ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 prin LAG() ultimul rând al clientului 1.
  • Neutilizarea lui NULLIF(..., 0) la împărțirea cu valoarea returnată de LAG() pentru a preveni eroarea division by zero.

Comenzi și funcții înrudite