MySQL 8.0 hiện hỗ trợ các chức năng cửa sổ, giống như hầu hết các triển khai SQL phổ biến. Với cú pháp chuẩn này, chúng ta có thể viết các truy vấn lớn nhất-n-mỗi-nhóm:
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;
Dưới đây là câu trả lời ban đầu tôi đã viết cho câu hỏi này vào năm 2009:
Tôi viết giải pháp theo cách này:
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
Về hiệu suất, giải pháp này hay giải pháp khác có thể tốt hơn, tùy thuộc vào bản chất dữ liệu của bạn. Vì vậy, bạn nên kiểm tra cả hai truy vấn và sử dụng truy vấn có hiệu suất tốt hơn dựa trên cơ sở dữ liệu của bạn.
Ví dụ:tôi có một bản sao của kết xuất dữ liệu tháng 8 của StackOverflow
. Tôi sẽ sử dụng nó để đo điểm chuẩn. Có 1.114.357 hàng trong Posts
bàn. Điều này đang chạy trên MySQL
5.0,75 trên Macbook Pro 2,40GHz của tôi.
Tôi sẽ viết một truy vấn để tìm bài đăng gần đây nhất cho một ID người dùng nhất định (của tôi).
Đầu tiên sử dụng kỹ thuật hiển thị
bởi @Eric với GROUP BY
trong một truy vấn con:
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)
Ngay cả EXPLAIN
phân tích
mất hơn 16 giây:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 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)
Bây giờ tạo cùng một kết quả truy vấn bằng cách sử dụng kỹ thuật của tôi
với 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)
EXPLAIN
phân tích cho thấy rằng cả hai bảng đều có thể sử dụng các chỉ mục của chúng:
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 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)
Đây là DDL cho Posts
của tôi bảng:
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;
Lưu ý cho người nhận xét:Nếu bạn muốn một điểm chuẩn khác với phiên bản MySQL khác, một tập dữ liệu khác hoặc thiết kế bảng khác, hãy tự làm. Tôi đã trình bày kỹ thuật ở trên. Stack Overflow ở đây để chỉ cho bạn cách thực hiện công việc phát triển phần mềm, không phải thực hiện tất cả công việc cho bạn.