Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Abrufen des letzten Datensatzes in jeder Gruppe - MySQL

MySQL 8.0 unterstützt jetzt Windowing-Funktionen, wie fast alle gängigen SQL-Implementierungen. Mit dieser Standard-Syntax können wir Abfragen mit den größten n pro Gruppe 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;

Unten ist die ursprüngliche Antwort, die ich 2009 auf diese Frage geschrieben habe:

Ich schreibe die Lösung so:

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 je nach Art Ihrer Daten die eine oder andere Lösung besser sein. Daher sollten Sie beide Abfragen testen und diejenige verwenden, die für Ihre Datenbank die bessere Leistung bietet.

Zum Beispiel habe ich eine Kopie des StackOverflow August Data Dump . Ich werde das zum Benchmarking verwenden. Es gibt 1.114.357 Zeilen in den Posts Tisch. Dies läuft auf MySQL 5.0.75 auf meinem Macbook Pro 2,40 GHz.

Ich werde eine Abfrage schreiben, um den neuesten Beitrag für eine bestimmte Benutzer-ID (meine) zu finden.

Erste Verwendung der Technik angezeigt 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)

Sogar der 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)

Erzeuge jetzt dasselbe 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)

Das EXPLAIN Die Analyse zeigt, dass beide Tabellen ihre Indizes verwenden können:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 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 Tabelle:

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 für Kommentatoren:Wenn Sie einen anderen Benchmark mit einer anderen Version von MySQL, einem anderen Datensatz oder einem anderen Tabellendesign wünschen, können Sie dies gerne selbst tun. Ich habe die Technik oben gezeigt. Stack Overflow ist hier, um Ihnen zu zeigen, wie man Softwareentwicklungsarbeit erledigt, und nicht, um die ganze Arbeit für Sie zu erledigen.