Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Mengambil catatan terakhir di setiap grup - MySQL

MySQL 8.0 sekarang mendukung fungsi windowing, seperti hampir semua implementasi SQL populer. Dengan sintaks standar ini, kita dapat menulis kueri n-per-grup terbesar:

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;

Di bawah ini adalah jawaban asli yang saya tulis untuk pertanyaan ini pada tahun 2009:

Saya menulis solusinya seperti ini:

SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
 ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;

Mengenai kinerja, satu solusi atau yang lain bisa lebih baik, tergantung pada sifat data Anda. Jadi, Anda harus menguji kedua kueri dan menggunakan kueri yang kinerjanya lebih baik dari basis data Anda.

Misalnya, saya memiliki salinan dump data StackOverflow Agustus . Saya akan menggunakannya untuk benchmarking. Ada 1.114.357 baris di Posts meja. Ini berjalan di MySQL 5.0.75 di Macbook Pro 2.40GHz saya.

Saya akan menulis kueri untuk menemukan postingan terbaru untuk ID pengguna tertentu (milik saya).

Pertama menggunakan teknik ditampilkan oleh @Eric dengan GROUP BY dalam subkueri:

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)

Bahkan EXPLAIN analisis membutuhkan waktu lebih dari 16 detik:

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 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)

Sekarang hasilkan hasil kueri yang sama menggunakan teknik saya dengan 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 analisis menunjukkan bahwa kedua tabel dapat menggunakan indeksnya:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 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)

Ini DDL untuk Posts saya tabel:

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;

Catatan untuk komentator:Jika Anda menginginkan tolok ukur lain dengan versi MySQL yang berbeda, kumpulan data yang berbeda, atau desain tabel yang berbeda, jangan ragu untuk melakukannya sendiri. Saya telah menunjukkan teknik di atas. Stack Overflow hadir untuk menunjukkan kepada Anda cara melakukan pekerjaan pengembangan perangkat lunak, bukan untuk melakukan semua pekerjaan untuk Anda.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Melakukan Full Outer Join di MySQL

  2. MySQL 8 Ekspresi Tabel Umum CTE

  3. Sisipan Bersyarat MySQL

  4. GANDA vs DECIMAL di MySQL

  5. Panduan Merancang Database Untuk Task Manager Di MySQL