Como selecionar id com grupo de datas máximas por categoria no PostgreSQL?

88

Por exemplo, eu gostaria de selecionar id com grupo de datas máximas por categoria, o resultado é: 7, 2, 6

id  category  date
1   a         2013-01-01
2   b         2013-01-03
3   c         2013-01-02
4   a         2013-01-02
5   b         2013-01-02
6   c         2013-01-03
7   a         2013-01-03
8   b         2013-01-01
9   c         2013-01-01

Posso saber como fazer isso no PostgreSQL?

user2412043
fonte
4
É sempre recomendável incluir sua versão do PostgreSQL.
Erwin Brandstetter

Respostas:

141

Este é um caso de uso perfeito para DISTINCT ON(extensão específica do Postgres do padrão DISTINCT):

SELECT DISTINCT ON (category)
       id  -- , category, date -- add any other column (expression) from the same row
FROM   tbl
ORDER  BY category, "date" DESC;

Cuidado com a ordem de classificação decrescente. Se a coluna puder ser NULL, você pode adicionar NULLS LAST:

DISTINCT ONé o mais simples e rápido. Explicação detalhada nesta resposta relacionada:

Para tabelas grandes, considere esta abordagem alternativa:

Otimização de desempenho para muitas linhas por category:

Erwin Brandstetter
fonte
Parece ótimo, mas você tem certeza de que funcionará sempre?
Atherion de
@Tixel: Com certeza. Siga os links para mais detalhes.
Erwin Brandstetter
21

Tente este:

SELECT t1.* FROM Table1 t1
JOIN 
(
   SELECT category, MAX(date) AS MAXDATE
   FROM Table1
   GROUP BY category
) t2
ON T1.category = t2.category
AND t1.date = t2.MAXDATE

Veja este SQLFiddle

Himanshu Jansari
fonte
1
Existe outra opção usando a função de janela rank ().
Denis de Bernardy
@ user1735921: Você obterá todas as colunas da Tabela1. Você pode escolher o que quiser.
Himanshu Jansari
15

Outra abordagem é usar a first_valuefunção de janela: http://sqlfiddle.com/#!12/7a145/14

SELECT DISTINCT
  first_value("id") OVER (PARTITION BY "category" ORDER BY "date" DESC) 
FROM Table1
ORDER BY 1;

... embora eu suspeite que a sugestão do hims056 normalmente terá um desempenho melhor quando os índices apropriados estiverem presentes.

Uma terceira solução é:

SELECT
  id
FROM (
  SELECT
    id,
    row_number() OVER (PARTITION BY "category" ORDER BY "date" DESC) AS rownum
  FROM Table1
) x
WHERE rownum = 1;
Craig Ringer
fonte
-5

SELECIONE id FROM tbl GROUP BY cat HAVING MAX (data)

Impiedoso
fonte
2
Esta é uma sintaxe ilegal e não responde à pergunta.
Erwin Brandstetter
4
Isso não funciona no PostgreSQL, mas funciona com o
Sqlite