SELECT și DISTINCT ON în PostgreSQL

Interogări și filtrare specifică Postgres PostgreSQL 7.3+ (standard în PostgreSQL 15–17)

Extrage rânduri din tabele și păstrează doar primul rând pentru fiecare grup de valori distincte.

Sintaxă SQL
SELECT DISTINCT ON (expresie [, ...]) lista_coloane FROM tabel ORDER BY expresie [, ...], criteriu_sortare;

Ce face SELECT și DISTINCT ON

Pe lângă interogarea clasică SELECT și eliminarea duplicatelor complete prin SELECT DISTINCT, PostgreSQL oferă extensia SELECT DISTINCT ON (expresie, ...). Aceasta păstrează doar primul rând din fiecare set de rânduri care au aceleași valori pentru expresiile specificate în paranteze.

Pentru ca rezultatul să fie determinist (de exemplu, cea mai recentă comandă a fiecărui client sau cel mai scump produs din fiecare categorie), expresiile din DISTINCT ON trebuie să corespundă primelor coloane din clauza ORDER BY, urmate de coloana care decide care rând este „primul”.

Clauze, operatori și argumente

Clauză / Operator Descriere
SELECT lista_coloane Specifică expresiile sau coloanele care vor fi returnate în setul de rezultate.
DISTINCT Elimină rândurile complet identice pe toate coloanele selectate.
DISTINCT ON (expr, ...) Păstrează un singur rând (primul conform ORDER BY) pentru fiecare combinație unică a expresiilor indicate.
FROM tabel [AS alias] Specifică tabelul sursă, vederea (view) sau subinterogarea din care se citesc datele.
ORDER BY expr, criteriu Obligatoriu în practică alături de DISTINCT ON: primele coloane din ORDER BY trebuie să coincidă cu cele din DISTINCT ON.

Exemple

Exemplul 1: Ultima comandă plasată de fiecare client cu DISTINCT ON

Selectăm cea mai recentă comandă pentru fiecare client_id, ordonând mai întâi după client și apoi descrescător după data comenzii.

SELECT DISTINCT ON (client_id)
    client_id,
    id AS comanda_id,
    total_lei,
    plasata_la
FROM comenzi
ORDER BY client_id, plasata_la DESC;

Rezultat interogare

 client_id | comanda_id | total_lei |     plasata_la      
-----------+------------+-----------+---------------------
       101 |       5042 |    420.50 | 2025-03-14 16:22:10
       102 |       5039 |    189.00 | 2025-03-12 09:15:04
       103 |       5051 |   1250.00 | 2025-03-18 19:40:55
(3 rows)

Exemplul 2: Produsul cu cel mai mare preț din fiecare categorie

Folosim DISTINCT ON (categorie) combinat cu ORDER BY categorie, pret DESC pentru a obține liderul de preț din fiecare categorie fără subinterogări complexe.

SELECT DISTINCT ON (categorie)
    categorie,
    denumire,
    pret
FROM produse
WHERE activ = true
ORDER BY categorie, pret DESC, id ASC;

Rezultat interogare

 categorie  |        denumire        |  pret   
------------+------------------------+---------
 Laptopuri  | ProBook Ultra 16 M3    | 8499.99
 Monitoare  | Dell UltraSharp 32 4K  | 3799.00
 Periferice | Tastatură Mecanică Pro |  649.50
(3 rows)

Greșeli frecvente

  • Omiterea clauzei ORDER BY când folosești DISTINCT ON: fără o sortare explicită, PostgreSQL va returna un rând imprevizibil din fiecare grup.
  • Plasarea în ORDER BY a unei prime coloane diferite de cea din DISTINCT ON, ceea ce declanșează eroarea: SELECT DISTINCT ON expressions must match initial ORDER BY expressions.

Comenzi și funcții înrudite