代码之家  ›  专栏  ›  技术社区  ›  Amr

MySQL-显示每个玩家的最高记录[重复]

  •  0
  • Amr  · 技术社区  · 5 年前

    有一张桌子 messages 其中包含的数据如下所示:

    Id   Name   Other_Columns
    -------------------------
    1    A       A_data_1
    2    A       A_data_2
    3    A       A_data_3
    4    B       B_data_1
    5    B       B_data_2
    6    C       C_data_1
    

    如果我运行查询 select * from messages group by name ,我将得到以下结果:

    1    A       A_data_1
    4    B       B_data_1
    6    C       C_data_1
    

    什么查询将返回以下结果?

    3    A       A_data_3
    5    B       B_data_2
    6    C       C_data_1
    

    也就是说,应该返回每组中的最后一条记录。

    目前,我使用的查询是:

    SELECT
      *
    FROM (SELECT
      *
    FROM messages
    ORDER BY id DESC) AS x
    GROUP BY name
    

    但这看起来效率很低。还有其他方法可以达到同样的效果吗?

    0 回复  |  直到 4 年前
        1
  •  1
  •   id'7238    4 年前

    MySQL 8.0现在支持 windowing functions ,就像几乎所有流行的SQL实现一样。使用这种标准语法,我们可以编写最大的n个分组查询:

    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;
    

    这种方法和其他寻找 groupwise maximal rows 如MySQL手册所示。

    以下是我在2009年为这个问题写的原始答案:


    我这样写解决方案:

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

    关于性能,根据数据的性质,一种或另一种解决方案可能更好。因此,您应该测试这两个查询,并在给定数据库的情况下使用性能更好的查询。

    例如,我有一份 StackOverflow August data dump 。我将使用它进行基准测试。这一行有1114357行 Posts 桌子这是在运行 MySQL 5.0.75在我的Macbook Pro 2.40GHz上。

    我将编写一个查询来查找给定用户ID(我的)的最新帖子。

    首先使用技术 shown 作者@Eric和 GROUP BY 在子查询中:

    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)
    

    甚至 EXPLAIN analysis 耗时超过16秒:

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

    现在使用 my technique 具有 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)
    

    这个 解释 分析表明,这两个表都可以使用它们的索引:

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

    这是我的DDL 帖子 表:

    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;
    

    评论者注意:如果您想要另一个具有不同版本MySQL、不同数据集或不同表设计的基准测试,可以自己做。我已经展示了上面的技巧。Stack Overflow在这里向您展示如何进行软件开发工作,而不是为您完成所有工作。