Ramos da InformáticaBanco de DadosComo Retornar o Último Registro de Cada Grupo no...

Como Retornar o Último Registro de Cada Grupo no SQL

Aprenda como recuperar o último registro de cada grupo em SQL com técnicas avançadas para MySQL, PostgreSQL, SQL Server e Oracle. Otimize consultas e melhore o desempenho do banco de dados agora!

-

Recuperar o último registro de cada grupo em um banco de dados é uma necessidade comum, mas que pode ser desafiadora dependendo do sistema de gerenciamento de banco de dados (SGDB) utilizado. Este artigo explora várias formas de resolver esse problema em diferentes SGDBs, como MySQL, PostgreSQL, SQL Server e Oracle, utilizando abordagens modernas e eficientes.


1. Entendendo o Problema

O objetivo é obter o último registro (ou o registro mais recente) de cada grupo baseado em uma ou mais colunas. Por exemplo, em uma tabela de vendas, você pode querer encontrar a venda mais recente de cada cliente.

Exemplo de Tabela:

id cliente_id data_venda valor
1 101 2024-01-10 200
2 102 2024-01-15 150
3 101 2024-01-20 300
4 102 2024-01-22 250

Resultado esperado: Queremos o último registro de cada cliente com base na coluna data_venda:

Dica de Leitura: Se você está buscando melhorar a performance das suas consultas SQL, especialmente aquelas que envolvem operações complexas como a recuperação do último registro de cada grupo, entender como otimizar o desempenho com tecnologias complementares pode ser crucial. Confira nosso artigo sobre Performance com Redis em Bancos Relacionais para descobrir como você pode acelerar suas aplicações.

id cliente_id data_venda valor
3 101 2024-01-20 300
4 102 2024-01-22 250

2. Soluções Gerais

2.1 Usando ROW_NUMBER()

O uso da função ROW_NUMBER() é uma abordagem moderna e eficiente suportada por muitos SGDBs.

Exemplo (SQL Server, PostgreSQL, Oracle):

sqlCopiar códigoWITH cte AS (
    SELECT 
        id, 
        cliente_id, 
        data_venda, 
        valor,
        ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY data_venda DESC) AS row_num
    FROM vendas
)
SELECT id, cliente_id, data_venda, valor
FROM cte
WHERE row_num = 1;

Como Funciona:

  1. A função ROW_NUMBER() atribui um número a cada linha dentro de cada grupo (PARTITION BY cliente_id), ordenando por data_venda em ordem decrescente.
  2. No passo final, filtramos apenas os registros onde row_num = 1.

2.2 Usando DISTINCT ON

PostgreSQL oferece a funcionalidade DISTINCT ON, que é extremamente eficiente para este tipo de problema.

Exemplo (PostgreSQL):

sqlCopiar códigoSELECT DISTINCT ON (cliente_id) id, cliente_id, data_venda, valor
FROM vendas
ORDER BY cliente_id, data_venda DESC;

Como Funciona:

  1. DISTINCT ON retorna apenas o primeiro registro encontrado para cada grupo definido pela cláusula (cliente_id).
  2. A cláusula ORDER BY determina qual registro será considerado o “primeiro” para cada grupo.

2.3 Usando Subconsultas

Subconsultas correlacionadas funcionam em qualquer SGDB, incluindo MySQL.

Exemplo (MySQL, SQL Server, Oracle):

sqlCopiar códigoSELECT id, cliente_id, data_venda, valor
FROM vendas v1
WHERE data_venda = (
    SELECT MAX(data_venda)
    FROM vendas v2
    WHERE v1.cliente_id = v2.cliente_id
);

Como Funciona:

  1. Para cada registro da tabela vendas, a subconsulta encontra a data máxima (MAX(data_venda)) para o mesmo cliente_id.
  2. Apenas os registros com a maior data_venda são retornados.

Desvantagens:

  • Pode ser menos performático em tabelas grandes, já que a subconsulta é executada para cada linha.

2.4 Usando Funções de Agregação

Combinar GROUP BY com funções de agregação pode ser uma abordagem simples.

Exemplo (MySQL, PostgreSQL, SQL Server):

sqlCopiar códigoSELECT v1.id, v1.cliente_id, v1.data_venda, v1.valor
FROM vendas v1
JOIN (
    SELECT cliente_id, MAX(data_venda) AS max_data
    FROM vendas
    GROUP BY cliente_id
) v2
ON v1.cliente_id = v2.cliente_id AND v1.data_venda = v2.max_data;

Como Funciona:

  1. O subquery encontra a data máxima (MAX(data_venda) para cada cliente.
  2. O JOIN combina a tabela original com os resultados agregados para trazer os registros completos.

VAI GOSTAR: O que é normalização de banco de dados? – Descubra tudo sobre a normalização de banco de dados, incluindo as explicações detalhadas da 5NF e 2NF com exemplos práticos. Aprenda como melhorar a organização e a eficiência dos seus dados e otimize seus sistemas de forma profissional.


3. Abordagens Específicas por SGDB

3.1 MySQL (Antes do 8.0)

MySQL antes da versão 8.0 não suportava funções analíticas como ROW_NUMBER(). Nesse caso, o uso de subconsultas ou agregações é necessário.

Exemplo:

sqlCopiar códigoSELECT v1.*
FROM vendas v1
INNER JOIN (
    SELECT cliente_id, MAX(data_venda) AS max_data
    FROM vendas
    GROUP BY cliente_id
) v2
ON v1.cliente_id = v2.cliente_id AND v1.data_venda = v2.max_data;

3.2 MySQL 8.0 ou Superior

Com suporte a ROW_NUMBER():

sqlCopiar códigoWITH cte AS (
    SELECT 
        id, cliente_id, data_venda, valor,
        ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY data_venda DESC) AS row_num
    FROM vendas
)
SELECT id, cliente_id, data_venda, valor
FROM cte
WHERE row_num = 1;

3.3 PostgreSQL

Usar DISTINCT ON é geralmente a abordagem mais eficiente:

sqlCopiar códigoSELECT DISTINCT ON (cliente_id) id, cliente_id, data_venda, valor
FROM vendas
ORDER BY cliente_id, data_venda DESC;

3.4 SQL Server

Usando ROW_NUMBER():

sqlCopiar códigoWITH cte AS (
    SELECT 
        id, cliente_id, data_venda, valor,
        ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY data_venda DESC) AS row_num
    FROM vendas
)
SELECT id, cliente_id, data_venda, valor
FROM cte
WHERE row_num = 1;

3.5 Oracle

Com funções analíticas:

SELECT id, cliente_id, data_venda, valor
FROM (
    SELECT 
        id, cliente_id, data_venda, valor,
        ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY data_venda DESC) AS row_num
    FROM vendas
)
WHERE row_num = 1;

4. Desempenho e Otimizações

4.1 Índices

  • Adicionar um índice nas colunas usadas para particionamento e ordenação (cliente_id, data_venda) pode melhorar significativamente o desempenho.

4.2 Tamanho da Tabela

  • Em tabelas muito grandes, preferir abordagens que minimizam leituras redundantes, como DISTINCT ON (PostgreSQL) ou ROW_NUMBER().

4.3 Benchmarking

  • Execute benchmarks em seu ambiente para comparar o desempenho de diferentes abordagens, especialmente se a tabela tiver milhões de registros.

5. Conclusão

Recuperar o último registro de cada grupo pode ser feito de várias formas, dependendo do SGDB e das necessidades específicas do sistema. Funções analíticas, como ROW_NUMBER(), são geralmente as mais eficientes e flexíveis, mas alternativas como DISTINCT ON e subconsultas também são úteis em cenários específicos. Entender as capacidades do SGDB escolhido e ajustar a abordagem para o contexto é essencial para garantir consultas rápidas e eficientes.

Referências Sobre o Tema

Aqui estão algumas referências confiáveis e úteis para aprofundar seu conhecimento sobre como recuperar o último registro em cada grupo em SQL:


Documentação Oficial

  1. MySQL:
    • Documentação sobre Funções Analíticas:
      MySQL ROW_NUMBER()
    • Documentação sobre Funções Agregadas:
      MySQL Aggregate Functions
  2. PostgreSQL:
  3. SQL Server:
  4. Oracle:

Livros

Para quem gosta de se aprofundar com leitura, livros oferecem uma visão mais técnica e estruturada.

Ambientes Práticos

A prática é essencial para dominar SQL. Aqui estão algumas ferramentas para experimentar e executar consultas.

VAI GOSTAR:

Perguntas Frequentes (FAQ): Último Registro por Grupo no SQL

Qual é a forma mais eficiente de pegar o último registro no PostgreSQL?

No PostgreSQL, a sintaxe nativa SELECT DISTINCT ON (coluna_grupo) aliada a um ORDER BY coluna_grupo, coluna_data DESC é, na esmagadora maioria dos casos, a forma mais limpa e performática de retornar o registro mais recente de cada grupo, superando o desempenho de CTEs e Subconsultas.

Como retornar a linha mais recente no MySQL sem Window Functions?

Se você utiliza uma versão do MySQL inferior a 8.0 (que não suporta ROW_NUMBER()), a melhor abordagem é usar um INNER JOIN com uma subconsulta. Você agrupa os dados na subconsulta usando GROUP BY e MAX(data), e depois faz um JOIN dessa tabela temporária com a tabela original, cruzando o ID e a data máxima.

Por que usar MAX() diretamente não retorna os outros dados da linha corretamente?

O erro clássico de agrupamento ocorre porque o SQL padrão exige que qualquer coluna no SELECT que não faça parte de uma função de agregação (como MAX() ou SUM()) deva estar explicitamente no GROUP BY. Se você usar MAX(data) e tentar selecionar a coluna “valor” sem agrupá-la, o banco de dados retornará um valor aleatório daquele grupo, e não necessariamente o valor correspondente à data máxima.

O que faz a cláusula PARTITION BY no ROW_NUMBER()?

O PARTITION BY funciona de maneira similar ao GROUP BY, mas sem “achatar” as linhas em um único resultado. Ele divide o conjunto de resultados em “partições” (ou grupos) com base em uma coluna (ex: ID do Cliente). O ROW_NUMBER() então recomeça a contagem a partir de 1 para cada nova partição, ordenando os dados internamente conforme a instrução ORDER BY.

Ramos da Informática
Ramos da Informáticahttps://ramosdainformatica.com.br
Ramos da Informática é um hub de comunidade dedicado a linguagens de programação, banco de dados, DevOps, Internet das Coisas (IoT), tecnologias da Indústria 4.0, cibersegurança e startups. Com curadoria de conteúdos de qualidade, o projeto é mantido por Ramos de Souza Janones.

Mais recentes

Como aprender a programar, um guia definitivo

Última atualização em 23/04/2026. Guia completo sobre: Como aprender a programar. Espero que este “guia” ou “manifesto”, como prefiro chamar, seja...

Stream Deck para Desenvolvedores: o Console de Comando do Futuro

Esqueça os streamers. Descubra como o Stream Deck se tornou o hardware essencial para Engenheiros de IA e Full...

Como Usar o Skills in Chrome no Brasil: Tutorial Completo de IA

A inteligência artificial já faz parte do nosso fluxo de trabalho, mas ter que reescrever os mesmos prompts repetidamente...

Context Engineering: Como Arquitetar Dados para LLMs e RAG

Na edição desta newsletter intitulada “Engenharia de Prompt: Não é só mais uma buzzword“: https://www.linkedin.com/pulse/engenharia-de-prompt-n%C3%A3o-%C3%A9-s%C3%B3-mais-uma-buzzword-de-souza-janones-tpkxf tratei sobre o tema...
E-Zine Dev

Evolua para Sênior

Estratégias de Node.js, arquitetura Limpa e IA que nunca publicamos no blog. Junte-se a +10.000 devs.

Assinar Gratuitamente Zero spam. Cancele quando quiser.

Aprender Idiomas com Google Tradutor: Na Prática

O Google está lançando um novo recurso experimental com tecnologia de IA no Google Tradutor, projetado para ajudar as...

Comunidades Internacionais de Desenvolvedores

Descubra as melhores comunidades internacionais de devs para 2026: GitHub, Stack Overflow, Discord e mais. Comparativo de salários Brasil vs. exterior e guia de carreira remota.

Mais Lidos

Programa Divulga Startup Brasil​

A Ramos da Informática lança uma iniciativa chamada “Programa...

Window Functions SQL: Guia Definitivo Para Análises

O uso de Window Functions vem crescendo progressivamente no...

O Problema N+1 Queries no SQL: O Que É e Como Resolver

Seu banco de dados está lento por causa do...

Pagamentos Seguros em IA: Guia à Prova de Balas

O Google lançou o Agent Payments Protocol (AP2), um...
E-Zine Dev

Evolua para Sênior

Estratégias de Node.js, arquitetura Limpa e IA que nunca publicamos no blog. Junte-se a +10.000 devs.

Assinar Gratuitamente Zero spam. Cancele quando quiser.

Você vai gostarrelacionados
Continue aprendendo

E-Zine Dev Ramos

Quer dominar arquitetura e IA?

Junte-se a +10.000 profissionais. Receba semanalmente estratégias de Node.js, React e IA que nunca publicamos no blog.

Assinar Gratuitamente Zero spam. Cancele quando quiser.