User Tools

Site Tools


sql:tutorials:selectrecordwithmaxdatepergroup

This is an old revision of the document!


[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. 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    --Last_entries
        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
    )
;

Erweiterung: Historischer Wert

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

Wir können einfach im inneren Query 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-11-2015#
SELECT      w1,
            w2,
            MAX(datum) AS max_datum
FROM        kurs
WHERE       datum <= 2011-03-19
GROUP BY    w1,
            w2
;
sql/tutorials/selectrecordwithmaxdatepergroup.1429513393.txt.gz · Last modified: 20.04.2015 09:03:13 by yaslaw