sql >> Databáze >  >> RDS >> Mysql

Načtení posledního záznamu v každé skupině - MySQL

MySQL 8.0 nyní podporuje funkce oken, jako téměř všechny populární implementace SQL. S touto standardní syntaxí můžeme psát největší-n-per-group dotazů:

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;

Níže je původní odpověď, kterou jsem na tuto otázku napsal v roce 2009:

Řešení píšu takto:

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

Pokud jde o výkon, jedno nebo druhé řešení může být lepší, v závislosti na povaze vašich dat. Měli byste tedy otestovat oba dotazy a použít ten, který je výkonově lepší vzhledem k vaší databázi.

Mám například kopii výpis dat StackOverflow ze srpna . Použiji to pro benchmarking. Posts obsahují 1 114 357 řádků stůl. Toto běží na MySQL 5.0.75 na mém Macbooku Pro 2,40 GHz.

Napíšu dotaz, abych našel nejnovější příspěvek pro dané ID uživatele (moje).

Nejprve pomocí techniky zobrazeno od @Eric pomocí GROUP BY v dílčím dotazu:

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)

Dokonce i EXPLAIN analýza trvá déle než 16 sekund:

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

Nyní vytvořte stejný výsledek dotazu pomocí moje technika pomocí 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 analýza ukazuje, že obě tabulky jsou schopny používat své indexy:

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

Zde je DDL pro mé Posts tabulka:

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;

Poznámka pro komentátory:Pokud chcete další benchmark s jinou verzí MySQL, jinou datovou sadou nebo jiným návrhem tabulek, můžete to udělat sami. Techniku ​​jsem ukázal výše. Stack Overflow je tu proto, aby vám ukázal, jak dělat práci na vývoji softwaru, a ne dělat veškerou práci za vás.



  1. Připojení k databázi Oracle pomocí Sql Server Integration Services

  2. SQLite Connection uniklo, i když vše bylo uzavřeno

  3. Jak získat PostgreSQL na VPS / Dedikovaný server

  4. MySQL DROP VIEW