[解決済み] 各グループの最後のレコードを取得する - MySQL
質問
テーブルがあります
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
しかし、これは非常に非効率的に見えます。同じ結果を得るための他の方法はありますか?
どのように解決するのですか?
MySQL 8.0 は、ほとんどすべての一般的な 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;
以下は、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 8月のデータダンプ
. それをベンチマークに使ってみます。 には1,114,357行があります。
Posts
テーブルを表示します。 で実行しています。
MySQL
5.0.75、Macbook Pro 2.40GHzです。
指定されたユーザー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
テーブルを作成します。
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 は、ソフトウェア開発作業の方法を紹介するためのものであり、すべての作業を代行するものではありません。
関連
-
[解決済み] [GROUP BY句に含まれるか、集約関数で使用される必要があります。
-
[解決済み] MySQLクエリ GROUP BY 日/月/年
-
[解決済み] MySQLでdatetimeとtimestampのどちらのデータ型を使用すべきですか?
-
[解決済み] MySQLでコマンドラインを使用してSQLファイルをインポートするにはどうすればよいですか?
-
[解決済み] 各GROUP BYグループの最初の行を選択しますか?
-
[解決済み] MySQLで'insert if not exists'を行うにはどうしたらいいですか?
-
[解決済み] 最初の行への結合方法
-
[解決済み] SQL SELECT WHERE フィールドに単語が含まれる場合
-
[解決済み] 各グループの上位1行を取得
-
[解決済み】countとgroup byを同じselect文で使用する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
MHAの高可用性構成とフェイルオーバー
-
[解決済み] 1つのPostgreSQLクエリで複数のWITHステートメントを使用するには?
-
[解決済み] SQLでカラムに最大値を持つ行のみを選択する [重複]。
-
[解決済み] SQL Serverでシングルクォートをエスケープするにはどうすればよいですか?
-
[解決済み] T-SQLでnot equalには!=と<>のどちらを使うべきですか?
-
[解決済み] SQL JOIN - WHERE句とON句の比較
-
[解決済み] SQL ServerにおけるINSERT OR UPDATEに関する解決策
-
[解決済み] mysqldumpで特定のテーブルをスキップする
-
[解決済み] "ON UPDATE CASCADE "を使用する場合について
-
[解決済み] SQL/mysql - Select distinct/UNIQUE but return all column?