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!

-

Eleve seu desenvolvimento com o Cursor

Eu utilizo o Cursor diariamente para acelerar meu fluxo. Use meu link e garanta 50% de desconto no seu primeiro mês dos planos Pro, Pro+ ou Ultra.

Resgatar meu desconto * Apenas para novos usuários

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():

english-interview-debugger.sh
$ grep -r "senior_dev_communication" ./career
[CRITICAL_ERROR] Código sênior detectado, mas fluência falhou no runtime.
Motivo: Travou na hora de explicar a arquitetura (System Design) em inglês para o gringo.

O mercado internacional não quer um robô de gramática. Quer um dev que saiba defender uma tomada de decisão técnica sob pressão. Destrave sua conversão na Preply com aulas particulares focadas em TI.

$ ./fix-english.sh --target=remote-job
Achar Professor Particular ➔
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
JS TS IA

Pesquisa: Como você está usando IA na sua rotina Dev?

Responda em menos de 2 minutos e ajude a direcionar nossos próximos artigos técnicos, guias e conteúdos.

Responder Pesquisa →
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

Sincronizando PostgreSQL e Turbopuffer com Puffgres

Guia Definitivo: Sincronizando PostgreSQL e Turbopuffer com Puffgres para Buscas Vetoriais Avançadas A integração de inteligência artificial em aplicações modernas...

3 Técnicas para Reduzir Consumo de Tokens no Claude Code e Codex

Aprenda 3 técnicas eficientes para reduzir o consumo de tokens no Claude Code e Codex. Otimize custos, melhore a...

Entenda Componentes de Cache no Next.js

O modelo de cache do Next.js 16 por trás das Navegações Instantâneas. Aprenda a usar use cache, cacheLife e...

Grok 4.5: mais rápido e integrado ao Cursor

O laboratório de IA de Elon Musk, agora com nova identidade visual, acaba de dar mais um passo à...
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.

Cloudflare Monetization Gateway Está Redefinindo a Economia da IA

Cloudflare lança o Cloudflare Monetization Gateway que permite cobrar por APIs, dados e sites via micropagamentos em stablecoins. O...

Configuração do GLM-5.2 no Cursor via OpenRouter

Descubra o GLM-5.2, o novo modelo de pesos abertos que rivaliza com GPT-5.5 em tarefas agênticas. Aprenda a configurar...

Mais Lidos

Concatenar Linhas em String SQL: Guia Definitivo (Prática)

A manipulação de strings em bancos de dados é...

Setup para Programadores: A Estação de Trabalho Eficiente

A configuração de uma estação de trabalho eficiente é...

Blockchain e Impacto Social: Como Criptomoedas Mudam Vidas

Iniciativa impactMarket leva Renda Básica Universal e mudanças extremamente...

Salário em Dólar: Plano de Ação para Ganhar em Dólar

Ganhar em dólar é um sonho de carreira para...
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.

Recursos da Comunidade

Carreira Internacional

JOB NA GRINGA

Meta de Salário Remoto
U$ 5.000/mês

O mapa completo para programadores do Brasil conquistarem contratos internacionais e mudarem de vida financeira.

  • Vagas exclusivas semanais: Membros acessam vagas com 7 dias de antecedência.
  • Workshops e lives gravadas: Buscar vagas não é óbvio. Nós te mostraremos como.
  • 498 Portais de vagas: Que contratam Brasileiros direto na sua dashboard.
  • Mentorias com Recrutadores: Encontros semanais ao vivo com Erika Linares.
  • Inglês diário com foco em conversação: Treine para entrevistas num ambiente sem julgamentos.
  • Suporte pós-contratação: Contabilidade e recebimento legal com a menor taxa.
Garantir Minha Vaga

Inscrição segura via Hotmart

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.