sql >> Database teknologi >  >> RDS >> Mysql

Henter den sidste post i hver gruppe - MySQL

MySQL 8.0 understøtter nu vinduesfunktioner, som næsten alle populære SQL-implementeringer. Med denne standardsyntaks kan vi skrive største-n-pr-gruppe-forespørgsler:

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;
 

Nedenfor er det originale svar, jeg skrev til dette spørgsmål i 2009:

Jeg skriver løsningen på denne måde:

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

Med hensyn til ydeevne kan den ene eller den anden løsning være bedre, afhængigt af arten af ​​dine data. Så du bør teste begge forespørgsler og bruge den, der er bedre til ydeevne givet din database.

For eksempel har jeg en kopi af StackOverflow August-datadumpet . Jeg vil bruge det til benchmarking. Der er 1.114.357 rækker i Posts bord. Dette kører på MySQL 5.0.75 på min Macbook Pro 2.40GHz.

Jeg vil skrive en forespørgsel for at finde det seneste indlæg for et givet bruger-id (mit).

Brug først teknikken vist af @Eric med GROUP BY i en underforespørgsel:

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)
 

Selv EXPLAIN analyse tager over 16 sekunder:

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

Fremstil nu det samme forespørgselsresultat ved hjælp af min teknik med 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 analyse viser, at begge tabeller er i stand til at bruge deres indekser:

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

Her er DDL for mine Posts 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;
 

Bemærkning til kommentatorer:Hvis du ønsker et andet benchmark med en anden version af MySQL, et andet datasæt eller et andet tabeldesign, er du velkommen til at gøre det selv. Jeg har vist teknikken ovenfor. Stack Overflow er her for at vise dig, hvordan du laver softwareudviklingsarbejde, ikke for at gøre alt arbejdet for dig.



  1. Jeg kan ikke bruge mysql_* funktioner efter opgradering af PHP

  2. Udeladelse af det dobbelte citat for at foretage forespørgsler på PostgreSQL

  3. Hvordan kan jeg opdatere/genåbne en SQLite-database efter gendannelse af den, når jeg bruger en singleton til databasehjælperen

  4. PostgreSQL forkert sortering