Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Pobieranie ostatniego rekordu w każdej grupie - MySQL

MySQL 8.0 obsługuje teraz funkcje okienkowe, podobnie jak prawie wszystkie popularne implementacje SQL. Dzięki tej standardowej składni możemy pisać zapytania o największej liczbie na grupę:

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;

Poniżej znajduje się oryginalna odpowiedź, którą napisałem na to pytanie w 2009 roku:

Rozwiązanie piszę w ten sposób:

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

Jeśli chodzi o wydajność, jedno lub drugie rozwiązanie może być lepsze, w zależności od charakteru danych. Dlatego powinieneś przetestować oba zapytania i użyć tego, który jest lepszy pod względem wydajności, biorąc pod uwagę Twoją bazę danych.

Na przykład mam kopię StackOverflow sierpniowego zrzutu danych . Użyję tego do analizy porównawczej. W Posts znajduje się 1 114 357 wierszy stół. Działa na MySQL 5.0.75 na moim Macbooku Pro 2,40 GHz.

Napiszę zapytanie, aby znaleźć najnowszy post dla danego identyfikatora użytkownika (mojego).

Najpierw użyj techniki pokazano autorstwa @Eric z GROUP BY w podzapytaniu:

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)

Nawet EXPLAIN analiza trwa ponad 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)

Teraz wygeneruj ten sam wynik zapytania, używając moja technika z 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 analiza pokazuje, że obie tabele mogą korzystać ze swoich indeksów:

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

Oto DDL dla moich Posts tabela:

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;

Uwaga dla komentatorów:jeśli potrzebujesz innego testu porównawczego z inną wersją MySQL, innym zbiorem danych lub innym projektem tabeli, możesz zrobić to sam. Technikę pokazałem powyżej. Stack Overflow jest tutaj, aby pokazać, jak wykonywać prace programistyczne, a nie wykonywać całą pracę za Ciebie.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ustawianie zmiennych konfiguracyjnych MySQL – MySQL 5.7 vs MySQL 8.0

  2. Jak wykonać kopię zapasową zaszyfrowanej bazy danych za pomocą serwera Percona Server dla MySQL 8.0?

  3. Jak usunąć duplikaty w tabeli MySQL?

  4. Utwórz tabelę w MySQL

  5. Jak importować / przywracać tabele MySql za pomocą PHP