1305 Stimmen

Abrufen des letzten Datensatzes in jeder Gruppe - MySQL

Es gibt eine Tabelle messages die Daten wie unten dargestellt enthält:

Id   Name   Other_Columns
-------------------------
1    A       A_data_1
2    A       A_data_2
3    A       A_data_3
4    B       B_data_1
5    B       B_data_2
6    C       C_data_1

Wenn ich eine Abfrage ausführe select * from messages group by name erhalte ich das folgende Ergebnis:

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

Welche Abfrage liefert das folgende Ergebnis?

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

Das heißt, der letzte Datensatz in jeder Gruppe sollte zurückgegeben werden.

Zurzeit verwende ich diese Abfrage:

SELECT
  *
FROM (SELECT
  *
FROM messages
ORDER BY id DESC) AS x
GROUP BY name

Dies erscheint jedoch äußerst ineffizient. Gibt es andere Möglichkeiten, um das gleiche Ergebnis zu erzielen?

4 Stimmen

Siehe akzeptierte Antwort in stackoverflow.com/questions/1379565/ für eine effizientere Lösung

2 Stimmen

12 Stimmen

Warum können Sie nicht einfach DESC hinzufügen, d. h. select * from messages group by name DESC

3voto

michal.jakubeczy Punkte 6138

Wenn Sie sich wirklich Sorgen um die Leistung machen, können Sie eine neue Spalte in die Tabelle einfügen, die IsLastInGroup vom Typ BIT.

Setzen Sie sie für die letzten Spalten auf true und behalten Sie sie bei jedem Einfügen/Aktualisieren/Löschen einer Zeile bei. Schreibvorgänge werden langsamer sein, aber Sie werden bei Lesevorgängen davon profitieren. Es hängt von Ihrem Anwendungsfall ab, und ich empfehle es nur, wenn Sie auf das Lesen fokussiert sind.

Ihre Abfrage wird also wie folgt aussehen:

SELECT * FROM Messages WHERE IsLastInGroup = 1

0 Stimmen

Einige Tabellen in Moodle haben eine Flaggenspalte wie diese.

3voto

Ullas Punkte 11181

Wenn Sie die letzte Zeile für jede Name können Sie jeder Zeilengruppe eine Zeilennummer zuweisen, indem Sie die Name und Bestellung durch Id in absteigender Reihenfolge.

ABFRAGE

SELECT t1.Id, 
       t1.Name, 
       t1.Other_Columns
FROM 
(
     SELECT Id, 
            Name, 
            Other_Columns,
    (
        CASE Name WHEN @curA 
        THEN @curRow := @curRow + 1 
        ELSE @curRow := 1 AND @curA := Name END 
    ) + 1 AS rn 
    FROM messages t, 
    (SELECT @curRow := 0, @curA := '') r 
    ORDER BY Name,Id DESC 
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;

SQL-Gefiedel

3voto

Teja Punkte 12510

Die nachstehende Abfrage funktioniert wie in Ihrer Frage beschrieben.

SELECT M1.* 
FROM MESSAGES M1,
(
 SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
 FROM MESSAGES
 GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;

3voto

Lukasz Szozda Punkte 137580

MariaDB 10.3 und neuere Versionen mit GRUPPE_CONCAT .

Die Idee ist die Verwendung von ORDER BY + LIMIT :

SELECT GROUP_CONCAT(id ORDER BY id DESC LIMIT 1) AS id,
       name,
       GROUP_CONCAT(Other_columns ORDER BY id DESC LIMIT 1) AS Other_columns
FROM t
GROUP BY name;

db<>fiddle demo

2voto

Azathoth Punkte 540

Wie wäre es damit:

SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;

Ich hatte ein ähnliches Problem (auf Postgresql tough) und auf eine 1M Datensätze Tabelle. Diese Lösung dauert 1,7 s gegenüber 44 s bei der Lösung mit LEFT JOIN. In meinem Fall musste ich die Korrespondenz zu Ihrer Tabelle filtern. Name Feld gegen NULL-Werte, was zu einer noch besseren Leistung um 0,2 Sekunden führt

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X