Wie wähle ich eine ID mit maximaler Datumsgruppe nach Kategorie in PostgreSQL aus?

85

Als Beispiel möchte ich ID mit maximaler Datumsgruppe nach Kategorie auswählen. Das Ergebnis ist: 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

Darf ich wissen, wie das in PostgreSQL geht?

user2412043
quelle
4
Es ist immer ratsam, Ihre Version von PostgreSQL einzuschließen.
Erwin Brandstetter

Antworten:

134

Dies ist ein perfekter Anwendungsfall für DISTINCT ON(Postgres-spezifische Erweiterung des Standards DISTINCT):

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

Vorsicht bei absteigender Sortierreihenfolge. Wenn die Spalte NULL sein kann, möchten Sie möglicherweise Folgendes hinzufügen NULLS LAST:

DISTINCT ONist am einfachsten und schnellsten. Detaillierte Erklärung in dieser verwandten Antwort:

Betrachten Sie für große Tische diesen alternativen Ansatz:

Leistungsoptimierung für viele Zeilen pro category:

Erwin Brandstetter
quelle
Scheint großartig, aber sind Sie absolut sicher, dass dies garantiert jedes Mal funktioniert?
Atherion
@ Pixel: Auf jeden Fall. Folgen Sie den Links für weitere Details.
Erwin Brandstetter
20

Probier diese:

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

Siehe diese SQLFiddle

hims056
quelle
1
Es gibt eine andere Option, die die Fensterfunktion rank () verwendet.
Denis de Bernardy
@ user1735921: Sie erhalten alle Spalten aus Tabelle1. Sie können wählen, was Sie wollen.
hims056
15

Ein anderer Ansatz ist die Verwendung der first_valueFensterfunktion: http://sqlfiddle.com/#!12/7a145/14

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

... obwohl ich vermute, dass der Vorschlag von hims056 normalerweise besser abschneidet, wenn geeignete Indizes vorhanden sind.

Eine dritte Lösung ist:

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
quelle
-5

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

Unbarmherzig
quelle
2
Dies ist eine illegale Syntax und beantwortet die Frage nicht.
Erwin Brandstetter
4
Dies funktioniert nicht unter PostgreSQL, sondern mit Sqlite
vladaman