MySQL 8.0 unterstützt jetzt Windowing-Funktionen wie fast alle gängigen SQL-Implementierungen. Mit dieser Standardsyntax können wir greatest-n-per-group-Abfragen schreiben:
WITH ranked_messages AS (
SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;
Diese und andere Ansätze zum Auffinden von gruppenweise maximale Zeilen werden im MySQL-Handbuch erläutert.
Im Folgenden finden Sie die ursprüngliche Antwort, die ich 2009 auf diese Frage geschrieben habe:
Ich schreibe die Lösung auf diese Weise:
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
In Bezug auf die Leistung kann die eine oder andere Lösung besser sein, je nach der Art Ihrer Daten. Sie sollten also beide Abfragen testen und diejenige verwenden, die in Bezug auf Ihre Datenbank die bessere Leistung bietet.
Ich habe zum Beispiel eine Kopie der StackOverflow August-Datenabzug . Ich verwende das zum Benchmarking. Es gibt 1.114.357 Zeilen in der Posts
Tabelle. Dies läuft unter MySQL 5.0.75 auf meinem Macbook Pro 2.40GHz.
Ich schreibe eine Abfrage, um den neuesten Beitrag für eine bestimmte Benutzer-ID (meine) zu finden.
Erste Anwendung der Technik siehe von @Eric mit dem GROUP BY
in einer Unterabfrage:
SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
FROM Posts pi GROUP BY pi.owneruserid) p2
ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;
1 row in set (1 min 17.89 sec)
Auch die EXPLAIN
Analyse dauert über 16 Sekunden:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 76756 | |
| 1 | PRIMARY | p1 | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY | 8 | p2.maxpostid | 1 | Using where |
| 2 | DERIVED | pi | index | NULL | OwnerUserId | 8 | NULL | 1151268 | Using index |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)
Erzeugen Sie nun das gleiche Abfrageergebnis mit meine Technik mit LEFT JOIN
:
SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;
1 row in set (0.28 sec)
El EXPLAIN
Analyse zeigt, dass beide Tabellen in der Lage sind, ihre Indizes zu verwenden:
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 1 | SIMPLE | p1 | ref | OwnerUserId | OwnerUserId | 8 | const | 1384 | Using index |
| 1 | SIMPLE | p2 | ref | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8 | const | 1384 | Using where; Using index; Not exists |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)
Hier ist die DDL für meine Posts
Tisch:
CREATE TABLE `posts` (
`PostId` bigint(20) unsigned NOT NULL auto_increment,
`PostTypeId` bigint(20) unsigned NOT NULL,
`AcceptedAnswerId` bigint(20) unsigned default NULL,
`ParentId` bigint(20) unsigned default NULL,
`CreationDate` datetime NOT NULL,
`Score` int(11) NOT NULL default '0',
`ViewCount` int(11) NOT NULL default '0',
`Body` text NOT NULL,
`OwnerUserId` bigint(20) unsigned NOT NULL,
`OwnerDisplayName` varchar(40) default NULL,
`LastEditorUserId` bigint(20) unsigned default NULL,
`LastEditDate` datetime default NULL,
`LastActivityDate` datetime default NULL,
`Title` varchar(250) NOT NULL default '',
`Tags` varchar(150) NOT NULL default '',
`AnswerCount` int(11) NOT NULL default '0',
`CommentCount` int(11) NOT NULL default '0',
`FavoriteCount` int(11) NOT NULL default '0',
`ClosedDate` datetime default NULL,
PRIMARY KEY (`PostId`),
UNIQUE KEY `PostId` (`PostId`),
KEY `PostTypeId` (`PostTypeId`),
KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
KEY `OwnerUserId` (`OwnerUserId`),
KEY `LastEditorUserId` (`LastEditorUserId`),
KEY `ParentId` (`ParentId`),
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;
Hinweis an die Kommentatoren: Wenn Sie einen weiteren Benchmark mit einer anderen Version von MySQL, einem anderen Datensatz oder einem anderen Tabellendesign durchführen möchten, können Sie das gerne selbst tun. Ich habe die Technik oben gezeigt. Stack Overflow ist hier, um Ihnen zu zeigen, wie Sie Software entwickeln können, nicht um die ganze Arbeit für Sie zu erledigen.
4 Stimmen
Siehe akzeptierte Antwort in stackoverflow.com/questions/1379565/ für eine effizientere Lösung
2 Stimmen
Duplikat von stackoverflow.com/q/121387/684229
12 Stimmen
Warum können Sie nicht einfach DESC hinzufügen, d. h. select * from messages group by name DESC
0 Stimmen
Mögliches Duplikat von Wie kann ich SELECT Zeilen mit MAX(Spaltenwert), DISTINCT durch eine andere Spalte in SQL?
5 Stimmen
@KimPrince Es scheint, dass die von Ihnen vorgeschlagene Antwort nicht das tut, was erwartet wird! Ich habe gerade Ihre Methode ausprobiert, und sie hat die ERSTE Zeile für jede Gruppe genommen und DESC geordnet. Es wird NICHT die letzte Zeile jeder Gruppe genommen.
0 Stimmen
Für mehr Effizienz, siehe mysql.rjweb.org/doc.php/groupwise_max
0 Stimmen
Siehe dies: paulund.de/get-last-record-in-each-mysql-group
0 Stimmen
@DatsunBing Ihre Lösung ist die einfachste und perfekteste.
0 Stimmen
Ich habe eine interessante Lösung für dieses Problem gefunden: stackoverflow.com/a/68894680/13457138
0 Stimmen
"letzter Datensatz" ist nicht zuverlässig, wenn Sie nicht ausdrücklich einen
ORDER BY
.0 Stimmen
@DatsunBing - Die innere
ORDER BY
wird vom Optimierer ignoriert. Die äußereGROUP BY
verstößt gegenONLY_FULL_GROUP_BY
.0 Stimmen
Kann mir bitte jemand sagen, warum nicht alle Datenbanken das einzige (meiner Meinung nach) vernünftige Ergebnis verwenden, wenn sie eine Aggregatfunktion verwenden, nämlich die anderen Felder auszuwählen aus derselben Reihe ? Dies nicht zu tun, macht nicht nur für mich keinen Sinn, sondern macht auch all diese anderen Methoden notwendig, die viel komplizierter .