web-dev-qa-db-ja.com

最後の個別のレコードセットを取得する

次の列を含むデータベーステーブルがあります。

id   code   value   datetime   timestamp

このテーブルでは、一意の値のみがID、つまり主キーに存在します。

日時値に基づいて、このテーブル内の最後の個別のレコードセットを取得したい。たとえば、以下が私のテーブルだとしましょう

id   code   value   datetime               timestamp
1    1023   23.56   2011-04-05 14:54:52    1234223421
2    1024   23.56   2011-04-05 14:55:52    1234223423
3    1025   23.56   2011-04-05 14:56:52    1234223424
4    1023   23.56   2011-04-05 14:57:52    1234223425
5    1025   23.56   2011-04-05 14:58:52    1234223426
6    1025   23.56   2011-04-05 14:59:52    1234223427
7    1024   23.56   2011-04-05 15:00:12    1234223428
8    1026   23.56   2011-04-05 15:01:14    1234223429
9    1025   23.56   2011-04-05 15:02:22    1234223430

ID 4、7、8、および9のレコード、つまり(datetime値に基づく)異なるコードを持つレコードの最後のセットを取得したい。このテーブルには最終的に何百万ものレコードと何百もの個々のコード値が含まれるので、私が強調したのは、私が達成しようとしているものの単なる例です。

これを達成するためにどのSQLステートメントを使用できますか?単一のSQLステートメントでそれを達成することはできません。私のデータベースはMySQL 5です。

43
Obinwanne Hill

これはあなたのために働くはずです。

 SELECT * 
 FROM [tableName] 
 WHERE id IN (SELECT MAX(id) FROM [tableName] GROUP BY code)

IdがAUTO_INCREMENTの場合、最新の日時も最高のidを持つため、計算がはるかに高価な日時を心配する必要はありません。

更新:パフォーマンスの観点から、多数のレコードを処理する場合は、id列とcode列にインデックスが付けられていることを確認してください。 idが主キーの場合、これは組み込まれていますが、codeおよびidをカバーする非クラスター化インデックスを追加する必要がある場合があります。

71
smdrager

これを試して:

SELECT * 
  FROM <YOUR_TABLE>
 WHERE (code, datetime, timestamp) IN
 (
   SELECT code, MAX(datetime), MAX(timestamp)
     FROM <YOUR_TABLE>
    GROUP BY code
 )
8
Chandu

それは古い投稿ですが、大きなテーブルで@smdragerの回答をテストするのは非常に遅かったです。これに対する私の修正は、「where in」の代わりに「inner join」を使用することでした。

SELECT * 
 FROM [tableName] as t1
 INNER JOIN (SELECT MAX(id) as id FROM [tableName] GROUP BY code) as t2
 ON t1.id = t2.id

これは本当に速く働きました。

3
educolo

私はこのようなことを試してみます:

select * from table
where id in (
    select id
    from table
    group by code
    having datetime = max(datetime)
)

(免責事項:これはテストされていません)

Datetimeが大きい行のidも大きい場合、smdragerによって提案されたソリューションはより高速です。

1
krtek

既存のすべての回答が_GROUP BY codeテーブル全体。論理的に正しい場合、実際にはこのクエリはテーブル全体(!)を通過します(確認するにはEXPLAINを使用します)。私の場合、テーブルには50万行未満の行があり、...GROUP BY code0.3秒かかりますが、これは絶対に許容できません。

ただし、ここで自分のデータの知識を使用できます(「投稿の最後のコメントを表示」と読みます)。

  • トップ20のレコードだけを選択する必要があります
  • 最後のXレコード全体で同じコードを持つレコードの量は比較的少ない
  • レコードの合計量>>使用可能なcodeの量>>取得する「トップ」レコードの量

数値を試してみたところ、最後の50レコードだけを選択すると、常に20種類のcodeを見つけることができることがわかりました。この場合、次のクエリが機能します(idの代わりにdatetimeを使用する可能性が高いことについて@smdragerのコメントに留意してください)

SELECT id, code
FROM tablename
ORDER BY id DESC 
LIMIT 50

テーブル全体をチェックする必要がないため、最後の50エントリのみを選択するのは非常に簡単です。そして残りは、それらの50個のエントリから異なるcodeを持つtop-20を選択することです。

明らかに、50(100、500)要素のセットに対するクエリは、数十万のエントリがあるテーブル全体に対するクエリよりもかなり高速です。

Raw SQL "後処理"

SELECT MAX(id) as id, code FROM 
    (SELECT id, code
     FROM tablename
     ORDER BY id DESC 
     LIMIT 50) AS nested 
GROUP BY code
ORDER BY id DESC 
LIMIT 20

これにより、idのリストが非常に速くなり、追加のJOINを実行する場合は、このクエリをさらに別のネストされたクエリとして配置し、すべての結合を実行します。

バックエンド側の「後処理」

その後、プログラミング言語でデータを処理し、codeが別個のレコードのみを最終セットに含める必要があります。

ある種のPython擬似コード:

records = select_simple_top_records(50)
added_codes = []
top_records = []
for record in records:
    # If record for this code was already found before
    # Note: this is not optimal, better to use structure allowing O(1) search and insert
    if record['code'] in added_codes:
        continue
    # Save record
    top_records.append(record)
    added_codes.append(record['code'])
    # If we found all top-20 required, finish
    if len(top_records) >= 20:
        break
0
The Godfather