sql >> データベース >  >> RDS >> Mysql

各グループの最後のレコードを取得する-MySQL

    MySQL 8.0は、ほとんどすべての一般的なSQL実装と同様に、ウィンドウ関数をサポートするようになりました。この標準構文を使用すると、グループごとに最大のクエリを記述できます。

    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;
    

    以下は、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;
    

    パフォーマンスに関しては、データの性質に応じて、どちらかのソリューションの方が優れている場合があります。したがって、両方のクエリをテストし、データベースでパフォーマンスが優れているクエリを使用する必要があります。

    たとえば、StackOverflowAugustデータダンプ のコピーがあります 。これをベンチマークに使用します。 Postsには1,114,357行あります テーブル。これはMySQL で実行されています MacbookPro2.40GHzで5.0.75。

    特定のユーザーID(私のもの)の最新の投稿を見つけるためのクエリを作成します。

    最初にテクニックを使用する表示 @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 分析 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)
    

    ここで、私のテクニック 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 分析によると、両方のテーブルがそれぞれのインデックスを使用できることが示されています。

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

    これが私のPostsの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は、すべての作業を行うのではなく、ソフトウェア開発作業を行う方法を示すためにここにあります。



    1. MySQL-1つのクエリで異なる値を持つ複数の行を更新します

    2. シェルスクリプトからMySQLコマンドを実行する方法は?

    3. 同じ列に複数のWHERE条件がある場合の選択

    4. 2つのテーブルの結合でクエリを更新する