User Tools

Site Tools


sql:tutorials:selectrecordwithmaxdatepergroup

[SQL] Aktuelle Einträge pro Gruppe auslesen

Einige Beispiele wie man pro Gruppe die Zeile mit dem neusten Datum ermitteln kann. Komplexere DBMS besitzen Analytische Funktionen, die so was abnehmen. Nicht aber MySQL und MS Access. Ergo muss hier auf diese Funktionen verzichtet werden.

Ausgangslage

Als Beispiel nehme ich eine einfache Währungskurs-Tabelle. Sie hat pro Währungs-kombination mehrere Einträge. nun möchte ich von jeder Kombination den aktuellsten Eintrag auslesen

[kurs]
w1  | w2  | kurs        | datum
CHF | EUR | 0.779438988 | 2011-03-21
CHF | USD | 1.10045     | 2011-03-20
CHF | USD | 1.10314     | 2011-03-21
CHF | EUR | 0.872367653 | 2011-03-20
CHF | USD | 1.10112     | 2011-03-19

Lösungsansatz

Dazu müssen wir erst einmal für jede Währungskombination das aktuellste Datum auslesen

SELECT w1, w2, MAX(datum) AS max_datum
FROM kurs
GROUP BY w1, w2;

Das ergibt dann die folgende Ausgabe

w1  | w2  | max_datum
CHF | EUR | 2011-03-21
CHF | USD | 2011-03-21

nun interessiert uns aber der Umrechnungskurs zu diesen max_datum. Also müssen wir dieses Resultat wieder mit der Tabelle kurs verknüpfen. Dazu gibt es ganz unterschiedliche Varianten, die eigentlich genau das bewerkstelligen.

Lösungen

Die vorgestellten Lösungen ergeben immer dasselbe Resultat. Welches am performantesten ist, kann ich nicht pauschal beantworten. Bei kleineren Datenmengen ist es irrelevant und bei Grösseren kommt es stark auf Indexe, Datenzusammensetzung etc an.

Die Ausgabe wird immer so aussehen

w1  | w2  | kurs        | datum
CHF | EUR | 0.779438988 | 2011-03-21
CHF | USD | 1.10314     | 2011-03-21

Einfaches Subquery mit WHERE-Verknüpfung

MySQL, MS Access, Oracle

Das Subquery wird mittels WHERE-Beziehungen an die Daten geknüpft

SELECT k.*
FROM
    kurs AS k,
    (
        SELECT w1, w2, MAX(datum) AS max_datum
        FROM kurs
        GROUP BY w1, w2
    ) AS le
WHERE
    k.w1 = le.w1
    AND k.w2 = le.w2
    AND k.datum = le.max_datum;

Einfaches Subquery mit INNER JOIN

MySQL, MS Access, Oracle

Dasselbe wie zuvor, einfach mit der INNER-JOIN-Schreibweise

SELECT k.*
FROM
    kurs AS k
    INNER JOIN (
        SELECT w1, w2, MAX(datum) AS max_datum
        FROM kurs
        GROUP BY w1, w2
    ) AS le
        ON
            k.w1 = le.w1
            AND k.w2 = le.w2
            AND k.datum = le.max_datum;

Einfaches Subquery mit IN()

MySQL, Oracle

Natürlich kann man das auch mit IN() im WHERE-Bereich erledigen

SELECT k.*
FROM kurs AS k
WHERE
    (k.w1, k.w2, k.datum) IN (
        SELECT w1, w2, MAX(datum) AS max_datum
        FROM kurs
        GROUP BY w1, w2
    );

Korrelierte Unterabfrage

MySQL, MS Access, Oracle

oder als Korrelierte Unterabfrage

SELECT k.*
FROM kurs AS k
WHERE
    k.datum IN (
        SELECT MAX(datum) AS max_datum
        FROM kurs
        WHERE kurs.w1 = k.w1 AND kurs.w2 = k.w2
        GROUP BY w1, w2
    );

Mittels DMAX() in MS Access

MS Access

In MS Access kann man auch mit DMAX() arbeiten. Mit DMAX() das aktuelleste Datum auslesen und im WHERE verknüpfen

SELECT k.*
FROM kurs AS k
WHERE k.datum = DMAX("datum", "kurs", "[w1]='" & k.w1 & "' AND [w2]='" & k.w2 & "'")
;

Erweiterung: Historischer Wert

Nehmen wir an, wir wollen nun wissen, was der Kurs am 19ten war

Wir können einfach im inneren Query (Abfragedes aktuellen Datums) noch die Bedinung mit dem Datum hinzufügen. Also Datum der Tabelle ist kleiner oder gleich dem Selektionierten Datum.

Der Datumstring ist je nach DBMS. Oracle: TO_DATE('19.03.2011', 'DD.MM.YYYY'), Access: #03/19/2011#
SELECT w1, w2, MAX(datum) AS max_datum
FROM kurs
WHERE datum <= 2011-03-19
GROUP BY w1, w2
;

Oder beim DMAX-Beispiel im Access:

WHERE k.datum = DMAX("datum", "kurs", "[w1]='" & k.w1 & "' AND [w2]='" & k.w2 & "' AND [datum] <= #03/19/2011#")
sql/tutorials/selectrecordwithmaxdatepergroup.txt · Last modified: 17.10.2019 14:10:04 by yaslaw