DISTINCT ON do PostgreSQL
Escolha uma linha por grupo.
DISTINCT ON do PostgreSQL é uma aula grátis de SQL Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de SQL Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de SQL Academy inclui 4 aulas no total.
O que é DISTINCT ON?
O PostgreSQL oferece uma extensão poderosa à palavra-chave padrão DISTINCT, chamada DISTINCT ON. Enquanto o DISTINCT comum remove linhas totalmente duplicadas, DISTINCT ON permite escolher exatamente uma linha por grupo, com base em uma ou mais colunas escolhidas por você.
Pense assim: “Para cada valor distinto desta coluna, forneça uma linha.” Isso é extremamente útil quando você quer o pedido mais recente por cliente, a maior pontuação por aluno ou o primeiro evento por categoria.
Sintaxe básica de DISTINCT ON
A sintaxe coloca DISTINCT ON (column) logo depois de SELECT. A coluna dentro dos parênteses define o agrupamento — o PostgreSQL retornará uma linha para cada valor distinto dessa coluna.
O exemplo abaixo retorna uma linha por customer_id de uma tabela de pedidos. O PostgreSQL escolhe qual linha retornar com base na cláusula ORDER BY que vem em seguida.
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
order_date,
total_amount
FROM orders
ORDER BY customer_id, order_date DESC;Preparando tabelas de exemplo
Vamos criar uma tabela simples de orders e inserir algumas linhas de exemplo para experimentar DISTINCT ON na prática. Temos três clientes, cada um com vários pedidos em datas diferentes.
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);Pedido mais recente por cliente
Um caso de uso muito comum: encontrar o pedido mais recente de cada cliente. Ao ordenar order_date DESC dentro de cada grupo de customer_id, DISTINCT ON escolhe a linha com a data mais recente.
Observe que a cláusula ORDER BY deve começar com a mesma coluna ou as mesmas colunas listadas em DISTINCT ON. Esse é um requisito do PostgreSQL.
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
order_date,
total_amount
FROM orders
ORDER BY customer_id, order_date DESC;Pedido mais antigo por cliente
Para obter o primeiro pedido (o mais antigo) de cada cliente, basta alterar a direção da ordenação para ASC. A única diferença é a ordem das linhas dentro de cada grupo — DISTINCT ON sempre escolhe a primeira linha após a ordenação.
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
order_date,
total_amount
FROM orders
ORDER BY customer_id, order_date ASC;A regra de ORDER BY
Regra importante: ao usar DISTINCT ON (col), a cláusula ORDER BY deve começar com a mesma coluna ou as mesmas colunas listadas em DISTINCT ON. Se isso não acontecer, o PostgreSQL gerará um erro.
Depois da coluna ou das colunas de agrupamento, você pode adicionar qualquer critério de ordenação para controlar qual linha será selecionada dentro de cada grupo.
-- 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)Maior pontuação por aluno
Aqui está outro exemplo prático usando uma tabela test_scores. Queremos a maior pontuação que cada aluno já obteve. Ao ordenar score DESC dentro de cada grupo de alunos, DISTINCT ON retorna apenas a linha com a maior pontuação de cada aluno.
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 com várias colunas
Você pode agrupar por mais de uma coluna listando várias colunas dentro de DISTINCT ON. Isso retorna uma linha para cada combinação distinta dessas colunas.
O exemplo abaixo escolhe a maior pontuação de cada aluno por disciplina, tratando cada par (aluno, disciplina) como seu próprio grupo.
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;Filtrando com WHERE
DISTINCT ON funciona naturalmente em conjunto com cláusulas WHERE. O filtro é aplicado primeiro; depois, DISTINCT ON escolhe uma linha por grupo entre os resultados filtrados.
Aqui encontramos o pedido mais recente de cada cliente, mas apenas entre os pedidos acima de 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 versus GROUP BY
Tanto DISTINCT ON quanto GROUP BY podem produzir uma linha por grupo, mas têm finalidades diferentes:
- GROUP BY agrupa as linhas e exige funções de agregação (SUM, MAX etc.) para as colunas que não fazem parte do agrupamento.
- DISTINCT ON mantém uma linha existente de fato — todas as colunas dela ficam disponíveis sem agregação.
Use GROUP BY quando precisar de valores agregados. Use DISTINCT ON quando precisar de todos os dados de uma linha específica dentro de cada grupo.
-- 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;Usando DISTINCT ON em uma subconsulta
Às vezes, você precisa aplicar filtros ou ordenações adicionais sobre o resultado de DISTINCT ON. Como o ORDER BY externo está vinculado à coluna de agrupamento, você pode envolver a consulta em uma subconsulta (ou CTE) para aplicar uma ordenação diferente ao resultado final.
Este exemplo primeiro escolhe o pedido mais recente de cada cliente e depois ordena o resultado final por total_amount em ordem 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ção rápida
Vamos verificar sua compreensão sobre DISTINCT ON. Leia a consulta abaixo com atenção e escolha a resposta que melhor descreve o que ela retorna.
SELECT DISTINCT ON (department_id) department_id, employee_name, salary FROM employees ORDER BY department_id, salary DESC;
Resumo da lição
Excelente trabalho! Veja um resumo do que você aprendeu sobre DISTINCT ON do PostgreSQL:
DISTINCT ON (col)retorna exatamente uma linha por valor distinto da coluna ou das colunas especificadas.- A cláusula
ORDER BYdeve começar com a mesma coluna ou as mesmas colunas listadas emDISTINCT ON— ela controla qual linha será selecionada de cada grupo. - Você pode usar várias colunas:
DISTINCT ON (col1, col2)agrupa pela combinação das duas. - Diferentemente de
GROUP BY,DISTINCT ONretorna uma linha real com todas as suas colunas originais — não é necessária nenhuma agregação. - Envolva a consulta em uma subconsulta quando precisar ordenar o resultado final por uma coluna diferente.
DISTINCT ON é um recurso específico do PostgreSQL e uma das formas mais elegantes de resolver problemas do tipo “escolher uma linha por grupo”.
Perguntas Frequentes
A aula “DISTINCT ON do PostgreSQL” é grátis?
Sim — o texto completo de “DISTINCT ON do PostgreSQL” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de SQL Academy, atualize para CoddyKit PRO. O curso de SQL Academy inclui 4 aulas no total.
O que vou aprender em “DISTINCT ON do PostgreSQL”?
Escolha uma linha por grupo. Você pratica SQL Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar SQL Academy?
Nenhuma experiência prévia é necessária. SQL Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “DISTINCT ON do PostgreSQL”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de SQL Academy?
Sim. Cada aula de SQL Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Noções básicas de SELECT DISTINCT
- DISTINCT em várias colunas
- DISTINCT ON do PostgreSQL
- Contando valores exclusivos