0Pricing
SQL Academy · Lezione

DISTINCT ON di PostgreSQL

Scelga una riga per ogni gruppo

DISTINCT ON di PostgreSQL è una lezione SQL Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento SQL Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso SQL Academy include 4 lezioni in totale.

Che cos'è DISTINCT ON?

PostgreSQL offre una potente estensione della parola chiave standard DISTINCT, chiamata DISTINCT ON. Mentre il normale DISTINCT rimuove le righe completamente duplicate, DISTINCT ON consente di scegliere esattamente una riga per gruppo in base a una o più colonne scelte.

Lo si può vedere così: 'Per ogni valore univoco in questa colonna, restituisca una riga.' È estremamente utile quando si desidera ottenere l'ordine più recente per cliente, il punteggio più alto per studente o il primo evento per categoria.

Sintassi di base di DISTINCT ON

La sintassi colloca DISTINCT ON (column) subito dopo SELECT. La colonna tra parentesi definisce il raggruppamento: PostgreSQL restituirà una riga per ogni valore univoco di quella colonna.

L'esempio seguente restituisce una riga per ogni customer_id dalla tabella degli ordini. PostgreSQL sceglie quale riga restituire in base alla clausola ORDER BY successiva.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Creazione delle tabelle di esempio

Crei una semplice tabella orders e inserisca alcune righe di esempio per provare DISTINCT ON in pratica. Sono presenti tre clienti, ciascuno con più ordini effettuati in date diverse.

CREATE TABLE orders (
  order_id      SERIAL PRIMARY KEY,
  customer_id   INT,
  order_date    DATE,
  total_amount  NUMERIC(10, 2)
);

INSERT INTO orders (customer_id, order_date, total_amount) VALUES
  (1, '2024-01-05', 120.00),
  (1, '2024-03-12', 85.50),
  (1, '2024-06-20', 200.00),
  (2, '2024-02-14', 45.00),
  (2, '2024-05-30', 310.00),
  (3, '2024-04-01', 75.00);

Ordine più recente per cliente

Un caso d'uso molto comune consiste nel trovare l'ordine più recente per ogni cliente. Ordinando order_date DESC all'interno di ogni gruppo customer_id, DISTINCT ON seleziona la riga con la data più recente.

Noti che la clausola ORDER BY deve iniziare con la stessa colonna o le stesse colonne elencate in DISTINCT ON. Questo è un requisito di PostgreSQL.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Ordine più vecchio per cliente

Per ottenere invece il primo ordine (il più vecchio) per cliente, basta cambiare la direzione dell'ordinamento in ASC. L'unica differenza è l'ordine delle righe all'interno di ogni gruppo: DISTINCT ON seleziona sempre la prima riga dopo l'ordinamento.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date ASC;

La regola di ORDER BY

Regola importante: quando si usa DISTINCT ON (col), la clausola ORDER BY deve iniziare con la stessa colonna o le stesse colonne elencate in DISTINCT ON. In caso contrario, PostgreSQL genererà un errore.

Dopo le colonne di raggruppamento, è possibile aggiungere qualsiasi altro criterio di ordinamento per controllare quale riga viene selezionata all'interno di ogni gruppo.

-- Correct: ORDER BY starts with the DISTINCT ON column
SELECT DISTINCT ON (customer_id)
  customer_id, order_date, total_amount
FROM orders
ORDER BY customer_id, total_amount DESC;

-- This would cause an error:
-- ORDER BY order_date DESC  (missing customer_id at the start)

Punteggio più alto per studente

Ecco un altro esempio pratico che utilizza una tabella test_scores. Vogliamo trovare il punteggio più alto mai ottenuto da ogni studente. Ordinando score DESC all'interno di ogni gruppo di studenti, DISTINCT ON restituisce solo la riga con il punteggio più alto per ogni studente.

CREATE TABLE test_scores (
  id         SERIAL PRIMARY KEY,
  student_id INT,
  subject    VARCHAR(50),
  score      INT,
  taken_on   DATE
);

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Math',    92, '2024-02-10'),
  (101, 'Math',    78, '2024-04-15'),
  (102, 'Math',    85, '2024-02-10'),
  (102, 'Math',    91, '2024-04-15'),
  (103, 'Math',    67, '2024-02-10');

SELECT DISTINCT ON (student_id)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, score DESC;

DISTINCT ON con più colonne

È possibile raggruppare in base a più di una colonna elencando diverse colonne all'interno di DISTINCT ON. In questo modo viene restituita una riga per ogni combinazione univoca di tali colonne.

L'esempio seguente seleziona il punteggio più alto per studente e per materia, trattando ogni coppia (studente, materia) come un gruppo distinto.

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Science', 88, '2024-03-01'),
  (101, 'Science', 95, '2024-05-20'),
  (102, 'Science', 72, '2024-03-01');

SELECT DISTINCT ON (student_id, subject)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, subject, score DESC;

Filtrare con WHERE

DISTINCT ON funziona naturalmente insieme alle clausole WHERE. Il filtro viene applicato prima, quindi DISTINCT ON seleziona una riga per ogni gruppo tra i risultati filtrati.

Qui troviamo l'ordine più recente per ogni cliente, ma solo tra gli ordini di importo superiore a 100.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
WHERE total_amount > 100
ORDER BY customer_id, order_date DESC;

DISTINCT ON e GROUP BY a confronto

Sia DISTINCT ON sia GROUP BY possono produrre una riga per gruppo, ma hanno scopi diversi:

  • GROUP BY accorpa le righe e richiede funzioni di aggregazione (SUM, MAX, ecc.) per le colonne non raggruppate.
  • DISTINCT ON conserva una riga realmente esistente: tutte le sue colonne sono disponibili senza aggregazione.

Usi GROUP BY quando servono valori aggregati. Usi DISTINCT ON quando servono tutti i dati di una riga specifica all'interno di ogni gruppo.

-- GROUP BY: only aggregated columns allowed
SELECT customer_id, MAX(order_date) AS latest_date
FROM orders
GROUP BY customer_id;

-- DISTINCT ON: returns the whole row for that latest date
SELECT DISTINCT ON (customer_id)
  customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Usare DISTINCT ON in una sottoquery

A volte è necessario applicare ulteriori filtri o un ordinamento al risultato di DISTINCT ON. Poiché l'ORDER BY esterno è vincolato alla colonna di raggruppamento, è possibile racchiudere la query in una sottoquery (o in una CTE) per applicare un ordinamento diverso all'output finale.

Questo esempio seleziona prima l'ordine più recente per cliente, quindi ordina il risultato finale per total_amount in ordine decrescente.

SELECT *
FROM (
  SELECT DISTINCT ON (customer_id)
    customer_id,
    order_id,
    order_date,
    total_amount
  FROM orders
  ORDER BY customer_id, order_date DESC
) AS latest_orders
ORDER BY total_amount DESC;

Verifica rapida

Verifichi la Sua comprensione di DISTINCT ON. Legga attentamente la query seguente e scelga la risposta che descrive meglio ciò che restituisce.

SELECT DISTINCT ON (department_id) department_id, employee_name, salary FROM employees ORDER BY department_id, salary DESC;

Riepilogo della lezione

Ottimo lavoro! Ecco un riepilogo di ciò che ha imparato su PostgreSQL DISTINCT ON:

  • DISTINCT ON (col) restituisce esattamente una riga per ogni valore univoco delle colonne specificate.
  • La clausola ORDER BY deve iniziare con la stessa colonna o le stesse colonne elencate in DISTINCT ON: determina quale riga viene selezionata da ogni gruppo.
  • È possibile usare più colonne: DISTINCT ON (col1, col2) raggruppa in base alla combinazione di entrambe.
  • A differenza di GROUP BY, DISTINCT ON restituisce una riga reale con tutte le sue colonne originali, senza bisogno di aggregazioni.
  • Racchiuda la query in una sottoquery quando deve ordinare l'output finale in base a una colonna diversa.

DISTINCT ON è una funzionalità specifica di PostgreSQL e uno dei modi più eleganti per risolvere i problemi in cui occorre 'scegliere una riga per gruppo'.

Domande Frequenti

La lezione «DISTINCT ON di PostgreSQL» è gratuita?

Sì — il testo completo di «DISTINCT ON di PostgreSQL» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso SQL Academy, passa a CoddyKit PRO. Il corso SQL Academy include 4 lezioni in totale.

Cosa imparerò in «DISTINCT ON di PostgreSQL»?

Scelga una riga per ogni gruppo Eserciti SQL Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare SQL Academy?

Non è richiesta alcuna esperienza precedente. SQL Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «DISTINCT ON di PostgreSQL»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione SQL Academy?

Sì. Ogni lezione SQL Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Nozioni di base su SELECT DISTINCT
  2. DISTINCT su più colonne
  3. DISTINCT ON di PostgreSQL
  4. Contare i valori univoci
← Torna a SQL Academy