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


958

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

しかし、これは非常に非効率的に見えます。同じ結果を達成する他の方法はありますか?


2
より効率的なソリューションについては、stackoverflow.com
questions / 1379565 /…で


7
なぜDESCを追加できないのですか。つまり、メッセージグループから名前*を選択してDESC
Kim Prince


2
@KimPrinceあなたが提案している答えは期待されていることをしていないようです!私はあなたの方法を試しました、そしてそれは各グループのために最初の列を取り、DESCを注文しました。各グループの最後の行は含まれません
Ayrat

回答:


970

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;

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

たとえば、StackOverflowのAugustデータダンプのコピーがあります。これをベンチマークに使用します。Postsテーブルには1,114,357行あります。これは、Macbook Pro 2.40GHzのMySQL 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;

8
本当に?大量のエントリがある場合はどうなりますか?たとえば、社内のバージョン管理を使用して作業していて、ファイルごとに大量のバージョンがある場合、その結合結果は膨大になります。これでサブクエリメソッドをベンチマークしたことがありますか?どちらが勝つかを知りたいのですが、最初にあなたに尋ねないようにしたいとは思いません。
エリック、

2
いくつかのテストを行いました。小さなテーブル(30万レコード、19万グループなので、大規模なグループなどではありません)では、クエリが結び付けられます(各8秒)。
エリック

1
@BillKarwin:meta.stackexchange.com/questions/123017、特にAdam Rackisの回答の下のコメントを参照してください。新しい質問に対する回答を取り戻したい場合はお知らせください。
ロバートハーベイ

3
@Tim、いいえ、一意でない<=列がある場合は役に立ちません。タイブレーカーとして一意の列を使用する必要があります。
ビルカーウィン2015

2
行数が増えるか、グループが大きくなると、パフォーマンスは指数関数的に低下します。たとえば、5つの日付で構成されるグループは、左結合を介して4 + 3 + 2 + 1 + 1 = 11行を生成し、最後に1行がフィルタリングされます。グループ化された結果と結合するパフォーマンスは、ほぼ線形です。テストに欠陥があります。
Salman A

148

UPD:2017-03-31、MySQL のバージョン5.7.5により、ONLY_FULL_GROUP_BYスイッチがデフォルトで有効になりました(そのため、非決定的なGROUP BYクエリは無効になりました)。さらに、彼らはGROUP BY実装を更新し、無効なスイッチを使用しても、ソリューションが期待どおりに機能しなくなる可能性があります。確認する必要があります。

上記のビルカーウィンのソリューションは、グループ内のアイテム数がかなり少ない場合は問題なく機能しますが、グループがかなり大きい場合は、比較n*n/2 + n/2のみを必要とするため、クエリのパフォーマンスが低下しますIS NULL

グループ186844461182含む行のInnoDBテーブルでテストを行いました。このテーブルには、機能テストのテスト結果が含まれて(test_id, request_id)おり、主キーとしてがあります。したがって、test_idグループであり、私はrequest_idそれぞれの最後を探していましたtest_id

Billのソリューションはすでにデルe4310で数時間実行されており、カバレッジインデックス(したがってusing indexEXPLAIN)で動作しているのに、いつ終了するかわかりません。

同じアイデアに基づく他の解決策がいくつかあります。

  • 基礎となるインデックスがBTREEインデックスである場合(これは通常のケースです)、最大の(group_id, item_value)ペアはeach内の最後の値です。降順でインデックスをウォークスルーする場合group_id、それぞれの最初のペアになりgroup_idます。
  • インデックスでカバーされている値を読み取る場合、値はインデックスの順序で読み取られます。
  • 各インデックスには暗黙的に主キー列が追加されています(つまり、主キーはカバレッジインデックスにあります)。以下のソリューションでは、主キーを直接操作します。この場合、結果に主キー列を追加するだけで済みます。
  • 多くの場合、サブクエリで必要な行IDを必要な順序で収集し、そのIDでサブクエリの結果を結合する方がはるかに安価です。サブクエリの結果の各行について、MySQLは主キーに基づく単一のフェッチが必要であるため、サブクエリは最初に結合に配置され、行はサブクエリのIDの順序で出力されます(明示的なORDER BYを省略した場合)参加のため)

MySQLがインデックスを使用する3つの方法は、詳細を理解するための優れた記事です。

解決策1

これは信じられないほど高速で、18M以上の行で約0.8秒かかります。

SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;

順序をASCに変更する場合は、それをサブクエリに入れ、IDのみを返し、それをサブクエリとして使用して残りの列に結合します。

SELECT test_id, request_id
FROM (
    SELECT test_id, MAX(request_id) AS request_id
    FROM testresults
    GROUP BY test_id DESC) as ids
ORDER BY test_id;

これは私のデータに約1.2秒かかります。

解決策2

これが私のテーブルに約19秒かかる別のソリューションです:

SELECT test_id, request_id
FROM testresults, (SELECT @group:=NULL) as init
WHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC

同様に降順でテストを返します。フルインデックススキャンを実行するため、処理速度は非常に遅くなりますが、ここでは、各グループの最大N行を出力する方法について説明します。

クエリの欠点は、その結果をクエリキャッシュでキャッシュできないことです。


人々がプラットフォームでテストできるように、テーブルのダンプにリンクしてください。
Pacerier 2015

3
ソリューション1あなたは、GROUP BY句であることをせずにREQUEST_ID選択することができない、動作することはできません
GIO

2
@giò、これは答えが5歳です。MySQL 5.7.5までONLY_FULL_GROUP_BYはデフォルトで無効にされ、このソリューションはそのままdev.mysql.com/doc/relnotes/mysql/5.7/en/…で機能しました。GROUP BYの実装が変更されたため、モードを無効にしてもソリューションが引き続き機能するかどうかはわかりません。
newtover 2017年

最初のソリューションでASCが必要な場合、MAXをMINにするとうまくいきますか?
ジン

@JinIzzraeel、各グループの上部にデフォルトでMINがあります(カバリングインデックスの順序です)SELECT test_id, request_id FROM testresults GROUP BY test_id;。各test_idの最小request_idを返します。
newtover、2017年

102

途中なので、サブクエリを使用して正しいグループを返します。

これを試して:

select
    a.*
from
    messages a
    inner join 
        (select name, max(id) as maxid from messages group by name) as b on
        a.id = b.maxid

そうでないid場合は、次の最大値が必要です。

select
    a.*
from
    messages a
    inner join 
        (select name, max(other_col) as other_col 
         from messages group by name) as b on
        a.name = b.name
        and a.other_col = b.other_col

このようにして、相関するサブクエリやサブクエリの順序付けを回避します。これらは非常に遅く/非効率になる傾向があります。


1
解決策の警告に注意してくださいother_col。その列が一意でないname場合、それらが同数の場合、同じで複数のレコードが返される可能性がありますmax(other_col)。私のニーズに対するソリューションを説明するこの投稿を見つけましたname
Eric Simonton、2015

場合によっては、このソリューションしか使用できませんが、受け入れられるソリューションはあります。
tom10271 2015

私の経験では遅く/非効率になる傾向があるのは、いまいましいメッセージテーブル全体をグループ化することです!つまり、ノートサブクエリは、全表スキャンが必要であること、および鉱山ではないことを、あなたのオプティマイザが何かをやっている場合を除き...ブートにその上でグループ化を行います。したがって、このソリューションは、テーブル全体をメモリに保持することに大きく依存しています。
ティモ

それらは恩恵を受けるだろうINDEX(name, id)INDEX(name, other_col)
リック・ジェームス

55

各グループ内の最後の投稿のIDを取得し、最初のクエリの結果をWHERE x IN構成の引数として使用してメッセージテーブルから選択するという、別のソリューションに到達しました。

SELECT id, name, other_columns
FROM messages
WHERE id IN (
    SELECT MAX(id)
    FROM messages
    GROUP BY name
);

他のいくつかのソリューションと比較してこれがどのように機能するのかはわかりませんが、300万行以上のテーブルで見事に機能しました。(1200秒以上の結果を伴う4秒の実行)

これはMySQLとSQL Serverの両方で機能するはずです。


(name、id)にインデックスがあることを確認してください。
SamuelÅslund16年

1
自己参加の方がはるかに良い
anwerj

私はあなたから何か良いことを学びました、そしてこのクエリはより高速です
ハンフリー

33

サブクエリフィドルリンクによる解決策

select * from messages where id in
(select max(id) from messages group by Name)

結合条件フィドルリンクによるソリューション

select m1.* from messages m1 
left outer join messages m2 
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null

この投稿の理由は、フィドルリンクのみを提供することです。同じSQLが他の回答ですでに提供されています。


1
@AlexanderSuraphel mysql5.5は現在フィドルでは使用できません。フィドルリンクはそれを使用して作成されました。さて、今日のフィドルはmysql5.6をサポートしています。データベースをmysql 5.6に変更し、スキーマを構築してSQLを実行することができます。
Vipin

8

かなりの速度でのアプローチは以下の通りです。

SELECT * 
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

結果

Id  Name    Other_Columns
3   A   A_data_3
5   B   B_data_2
6   C   C_data_1

これはid、必要に応じて注文されていることを前提としています。一般的なケースでは、他の列が必要です。
リックジェームズ

6

ここに2つの提案があります。まず、mysqlがROW_NUMBER()をサポートしている場合、それは非常に簡単です。

WITH Ranked AS (
  SELECT Id, Name, OtherColumns,
    ROW_NUMBER() OVER (
      PARTITION BY Name
      ORDER BY Id DESC
    ) AS rk
  FROM messages
)
  SELECT Id, Name, OtherColumns
  FROM messages
  WHERE rk = 1;

「最後」とは、IDの最後の意味だと思います。そうでない場合は、それに応じてROW_NUMBER()ウィンドウのORDER BY句を変更します。ROW_NUMBER()が利用できない場合、これは別の解決策です:

次に、そうでない場合、これは多くの場合、続行するための良い方法です。

SELECT
  Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
  SELECT * FROM messages as M2
  WHERE M2.Name = messages.Name
  AND M2.Id > messages.Id
)

つまり、同じ名前のlater-Idメッセージがないメッセージを選択します。


8
MySQLはROW_NUMBER()またはCTEをサポートしていません。
ビルカーウィン

1
MySQL 8.0(およびMariaDB 10.2)がROW_NUMBER()CTEをサポートするようになりました。
リックジェームズ

6

私はまだ大きなDBでテストしていませんが、これはテーブルを結合するよりも速いと思います:

SELECT *, Max(Id) FROM messages GROUP BY Name

14
これは任意のデータを返します。つまり、返された列はMAX(Id)のレコードからのものではない可能性があります。

WHERE条件のあるレコードのセットから最大IDを選択するのに役立ちます: "SELECT Max(Id)FROM Prod WHERE Pn = '" + Pn + "'"これは、同じPn.In c#を持つレコードのセットから最大Idを返します結果を取得するには、reader.GetString(0)を使用します
ニコラ

5

これは、GROUP_CONCATwith by を使用して最後の関連レコードを取得SUBSTRING_INDEXし、リストからレコードの1つを選択する別の方法です

SELECT 
  `Id`,
  `Name`,
  SUBSTRING_INDEX(
    GROUP_CONCAT(
      `Other_Columns` 
      ORDER BY `Id` DESC 
      SEPARATOR '||'
    ),
    '||',
    1
  ) Other_Columns 
FROM
  messages 
GROUP BY `Name` 

クエリ意志グループの上にすべてOther_Columns同じであることNameグループと使用してORDER BY id DESC参加するすべてOther_Columns私が使用している私の場合は提供区切りで降順に特定のグループに||、使用してSUBSTRING_INDEXこのリスト上の最初のものを選択します

フィドルデモ


group_concat_max_lenは、処理できる行数を制限することに注意してください。
リックジェームズ

5

同じ結果を得るにはさまざまな方法がたくさんあることは明らかですが、あなたの質問は、MySQLの各グループの最後の結果を効率的に取得する方法は何でしょうか。大量のデータを処理していて、MySQLの最新バージョン(5.7.21や8.0.4-rcなど)でもInnoDBを使用していると仮定すると、これを行う効率的な方法がない場合があります。

6,000万行を超えるテーブルでもこれを行う必要がある場合があります。

これらの例では、クエリがデータ内のすべてのグループの結果を見つける必要がある、約150万行のみのデータを使用します。実際のケースでは、約2,000のグループからデータを返す必要があることがよくあります(仮想的には、データの大部分を調べる必要はありません)。

次の表を使用します。

CREATE TABLE temperature(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT, 
  groupID INT UNSIGNED NOT NULL, 
  recordedTimestamp TIMESTAMP NOT NULL, 
  recordedValue INT NOT NULL,
  INDEX groupIndex(groupID, recordedTimestamp), 
  PRIMARY KEY (id)
);

CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id)); 

温度テーブルには、約150万のランダムなレコードと100の異なるグループが入力されています。selected_groupには、これらの100個のグループが取り込まれます(この場合、通常、すべてのグループで20%未満になります)。

このデータはランダムであるため、複数の行に同じrecordedTimestampが含まれる可能性があります。必要なのは、選択されたすべてのグループのリストをgroupIDの順に取得し、各グループの最後に記録されたTimestampを取得することです。同じグループにそのような複数の一致する行がある場合、それらの行の最後に一致するIDです。

仮にMySQLに、特別なORDER BY句の最後の行から値を返すlast()関数があった場合、単純に次のようにできます。

SELECT 
  last(t1.id) AS id, 
  t1.groupID, 
  last(t1.recordedTimestamp) AS recordedTimestamp, 
  last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;

これは、通常のGROUP BY関数を使用しないため、この場合は数100行を調べるだけで済みます。これは0秒で実行されるため、非常に効率的です。通常、MySQLではGROUP BY句の後にORDER BY句が表示されることに注意してください。ただし、このORDER BY句は、last()関数のORDERを決定するために使用されます。GROUP BY句がない場合、最後の値は返されたすべての行で同じになります。

ただし、MySQLにはこれがないため、MySQLの持つさまざまなアイデアを見て、どれも効率的でないことを証明しましょう。

例1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT t2.id
  FROM temperature t2 
  WHERE t2.groupID = g.id
  ORDER BY t2.recordedTimestamp DESC, t2.id DESC
  LIMIT 1
);

これは、3,009,254行を調べ、5.7.21では約0.859秒、8.0.4-rcではわずかに長くなりました。

例2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
INNER JOIN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
) t5 ON t5.id = t1.id;

これは1,505,331行を調べ、5.7.21では1.25秒、8.0.4-rcでは少し長くかかりました。

例3

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
WHERE t1.id IN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
)
ORDER BY t1.groupID;

これは、3,009,685行を調べ、5.7.21では〜1.95秒かかり、8.0.4-rcではわずかに長くなりました。

実施例4

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT max(t2.id)
  FROM temperature t2 
  WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
      SELECT max(t3.recordedTimestamp)
      FROM temperature t3 
      WHERE t3.groupID = g.id
    )
);

これは6,137,810行を調べ、5.7.21では約2.2秒、8.0.4-rcではわずかに長くかかりました。

例5

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
  SELECT 
    t2.id, 
    t2.groupID, 
    t2.recordedTimestamp, 
    t2.recordedValue, 
    row_number() OVER (
      PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
    ) AS rowNumber
  FROM selected_group g 
  INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;

これは6,017,808行を調べ、8.0.4-rcで約4.2秒かかりました

実施例6

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM (
  SELECT 
    last_value(t2.id) OVER w AS id, 
    t2.groupID, 
    last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp, 
    last_value(t2.recordedValue) OVER w AS recordedValue
  FROM selected_group g
  INNER JOIN temperature t2 ON t2.groupID = g.id
  WINDOW w AS (
    PARTITION BY t2.groupID 
    ORDER BY t2.recordedTimestamp, t2.id 
    RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  )
) t1
GROUP BY t1.groupID;

これは6,017,908行を調べ、8.0.4-rcで約17.5秒かかりました

実施例7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2 
  ON t2.groupID = g.id 
  AND (
    t2.recordedTimestamp > t1.recordedTimestamp 
    OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
  )
WHERE t2.id IS NULL
ORDER BY t1.groupID;

これは永遠にかかっていたので、私はそれを殺さなければなりませんでした。


これは別の問題です。そして、解決策は巨大なUNION ALLクエリです。
Paul Spiegel

@PaulSpiegelあなたは巨大なUNION ALLについて冗談を言っていると思います。選択したすべてのグループを事前に知っておく必要があるという事実と、信じられないほど巨大なクエリとなる2,000の選択したグループがあるという事実に加えて、上記の最速の例よりもさらにパフォーマンスが悪いため、そうではありません。解決。
ヨセフ

私は真剣です。私は過去に数百のグループでそれをテストしました。大きなグループでタイを処理する必要がある場合、UNION ALLはMySQLで最適な実行プランを強制する唯一の方法です。SELECT DISTINCT(groupID)高速であり、そのようなクエリを作成するために必要なすべてのデータを提供します。を超えない限り、クエリサイズは問題ありません。MySQL5.7 max_allowed_packetではデフォルトで4MBに設定されています。
Paul Spiegel

5

Group By of recordsの最後のレコードを取得するときにMySQLを使用する方法を見ていきます。たとえば、この投稿の結果セットがあるとします。

id category_id post_title

1 1 Title 1

2 1 Title 2

3 1 Title 3

4 2 Title 4

5 2 Title 5

6 3 Title 6

タイトル3、タイトル5、タイトル6の各カテゴリの最後の投稿を取得できるようにしたいと思います。投稿をカテゴリ別に取得するには、MySQL Group Byキーボードを使用します。

select * from posts group by category_id

しかし、このクエリから返される結果は次のとおりです。

id category_id post_title

1 1 Title 1

4 2 Title 4

6 3 Title 6

group byは常に、結果セットのグループの最初のレコードを返します。

SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );

これにより、各グループで最も高いIDの投稿が返されます。

id category_id post_title

3 1 Title 3

5 2 Title 5

6 3 Title 6

ここをクリック


4
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;

答えを少し詳しく説明していただけますか?なぜあなたのクエリはVijaysオリジナルのクエリよりも好ましいのですか?
janfoeh 2014年

4

これが私の解決策です:

SELECT 
  DISTINCT NAME,
  MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES 
FROM MESSAGE;

これは、名前ごとに最新のメッセージを返しません。そして、それはの非常に複雑なバージョンですSELECT NAME, MAX(MESSAGES) MESSAGES FROM MESSAGE GROUP BY NAME
Paul Spiegel

さらに、この定式化は非常に非効率的です。
リックジェームズ

3

これを試して:

SELECT jos_categories.title AS name,
       joined .catid,
       joined .title,
       joined .introtext
FROM   jos_categories
       INNER JOIN (SELECT *
                   FROM   (SELECT `title`,
                                  catid,
                                  `created`,
                                  introtext
                           FROM   `jos_content`
                           WHERE  `sectionid` = 6
                           ORDER  BY `id` DESC) AS yes
                   GROUP  BY `yes`.`catid` DESC
                   ORDER  BY `yes`.`created` DESC) AS joined
         ON( joined.catid = jos_categories.id )  

3

こんにちは@Vijay Devテーブルメッセージに主キーの自動インクリメントであるIdが含まれている場合、主キーに基づいて最新のレコードを取得するには、クエリは次のように読み取る必要があります。

SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId

これは私が見つけた中で最も速い
CORSAIR '10

3

こちらからも眺めることができます。

http://sqlfiddle.com/#!9/ef42b/9

最初のソリューション

SELECT d1.ID,Name,City FROM Demo_User d1
INNER JOIN
(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);

2番目のソリューション

SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;

3
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )

3

**

こんにちは、このクエリは役立つかもしれません:

**

SELECT 
  *
FROM 
  message 

WHERE 
  `Id` IN (
    SELECT 
      MAX(`Id`) 
    FROM 
      message 
    GROUP BY 
      `Name`
  ) 
ORDER BY 
   `Id` DESC

2

このメソッドを使用してテーブルの重複を削除する方法はありますか?結果セットは基本的に一意のレコードのコレクションなので、結果セットにないすべてのレコードを削除できれば、重複は事実上ありませんか?私はこれを試しましたが、mySQLは1093エラーを出しました。

DELETE FROM messages WHERE id NOT IN
 (SELECT m1.id  
 FROM messages m1 LEFT JOIN messages m2  
 ON (m1.name = m2.name AND m1.id < m2.id)  
 WHERE m2.id IS NULL)

出力を一時変数に保存してからNOT IN(一時変数)から削除する方法はありますか?@Billは非常に便利なソリューションに感謝します。

編集:私が解決策を見つけたと思います:

DROP TABLE IF EXISTS UniqueIDs; 
CREATE Temporary table UniqueIDs (id Int(11)); 

INSERT INTO UniqueIDs 
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON 
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields  
    AND T1.ID < T2.ID) 
    WHERE T2.ID IS NULL); 

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

2

以下のクエリは、質問に従って正常に機能します。

SELECT M1.* 
FROM MESSAGES M1,
(
 SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
 FROM MESSAGES
 GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;

2

あなたはそれぞれの最後の行をしたい場合Name、あなたはで各行グループに行番号を与えることができますNameすることにより、オーダーId降順インチ

クエリ

SELECT t1.Id, 
       t1.Name, 
       t1.Other_Columns
FROM 
(
     SELECT Id, 
            Name, 
            Other_Columns,
    (
        CASE Name WHEN @curA 
        THEN @curRow := @curRow + 1 
        ELSE @curRow := 1 AND @curA := Name END 
    ) + 1 AS rn 
    FROM messages t, 
    (SELECT @curRow := 0, @curA := '') r 
    ORDER BY Name,Id DESC 
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;

SQLフィドル


2

これはどう:

SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;

私は同様の問題(postgresqlは厳しい)と1Mレコードテーブルで発生しました。このソリューションは、1.7秒とLEFT JOINを使用した44秒で生成されます。私の場合、名前フィールドの対応するものをNULL値に対してフィルタリングする必要があったため、0.2秒でさらに優れたパフォーマンスが得られました


1

パフォーマンスが本当に懸念される場合はIsLastInGroup、タイプBIT と呼ばれるテーブルに新しい列を導入できます。

最後の列でtrueに設定し、すべての行の挿入/更新/削除で維持します。書き込みは遅くなりますが、読み取りではメリットがあります。それはあなたのユースケースに依存し、私はあなたが読み取り重視の場合にのみお勧めします。

したがって、クエリは次のようになります。

SELECT * FROM Messages WHERE IsLastInGroup = 1

Moodleのいくつかのテーブルには、このようなフラグ列があります。
ローレンス


0

あなたは数えることによってグループ化することができ、またグループの最後のアイテムを得ることができます:

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user

0

以下のOracleクエリが役立つことを願っています:

WITH Temp_table AS
(
    Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID 
    desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1

0

別のアプローチ:

各プログラムの最大m2_priceでプロパティを見つけます(1つのプログラムにn個のプロパティ):

select * from properties p
join (
    select max(m2_price) as max_price 
    from properties 
    group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.