Comment sélectionner un identifiant avec un groupe de dates maximum par catégorie dans PostgreSQL?

88

Pour un exemple, je voudrais sélectionner l'id avec le groupe de date maximum par catégorie, le résultat est: 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

Puis-je savoir comment faire cela dans PostgreSQL?

user2412043
la source
4
Il est toujours judicieux d'inclure votre version de PostgreSQL.
Erwin Brandstetter le

Réponses:

141

C'est un cas d'utilisation parfait pour DISTINCT ON(extension spécifique de Postgres de la norme DISTINCT):

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

Attention à l'ordre de tri décroissant. Si la colonne peut être NULL, vous pouvez ajouter NULLS LAST:

DISTINCT ONest le plus simple et le plus rapide. Explication détaillée dans cette réponse connexe:

Pour les grandes tables, envisagez cette approche alternative:

Optimisation des performances pour de nombreuses lignes par category:

Erwin Brandstetter
la source
Cela semble génial, mais êtes-vous absolument sûr que cela fonctionnera à chaque fois?
Atherion
@Tixel: Absolument. Suivez les liens pour plus de détails.
Erwin Brandstetter
21

Essaye celui-là:

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

Voir ce SQLFiddle

Himanshu Jansari
la source
1
Il existe une autre option utilisant la fonction de fenêtre rank ().
Denis de Bernardy
@ user1735921: Vous obtiendrez toutes les colonnes de Table1. Vous pouvez choisir ce que vous voulez.
Himanshu Jansari
15

Une autre approche consiste à utiliser la first_valuefonction window: http://sqlfiddle.com/#!12/7a145/14

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

... bien que je soupçonne que la suggestion de hims056 fonctionnera généralement mieux là où des index appropriés sont présents.

Une troisième solution est:

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
la source
-5

SELECT id FROM tbl GROUP BY cat HAVING MAX (date)

Impitoyable
la source
2
C'est une syntaxe illégale et ne répond pas à la question.
Erwin Brandstetter
4
Cela ne fonctionne pas sur PostgreSQL mais fonctionne avec Sqlite
vladaman