获取最后一组不同的记录

Posted

技术标签:

【中文标题】获取最后一组不同的记录【英文标题】:Get last distinct set of records 【发布时间】:2011-07-30 01:43:10 【问题描述】:

我有一个包含以下列的数据库表:

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 的记录,即最后一组具有不同代码的记录(基于日期时间值)。我强调的只是我想要实现的一个示例,因为该表最终将包含数百万条记录和数百个单独的代码值。

我可以使用什么 SQL 语句来实现这一点?我似乎无法用一条 SQL 语句完成它。我的数据库是 mysql 5。

【问题讨论】:

【参考方案1】:

这应该适合你。

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

如果 id 为 AUTO_INCREMENT,则无需担心计算成本要高得多的日期时间,因为最近的日期时间也将具有最高的 id。

更新:从性能的角度来看,确保在处理大量记录时对 idcode 列进行索引。如果id 是主键,这是内置的,但您可能需要添加一个非聚集索引,覆盖codeid

【讨论】:

+1 用于避免自动增量时的日期时间测试...我冒昧地重新格式化答案。 像魅力一样工作!非常感谢。 @smdrager,非常好...节省了我的时间。 我必须在 Eloquent ORM 中实现这个...第一次尝试,谢谢【参考方案2】:

试试这个:

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

【讨论】:

既然表有主键,就不用做这么复杂的where子句了。看我的回答。 @Krtek:同意,但这需要假设 id 是自动递增的。 完全没有,smdrager 的答案确实如此,由于having 子句,我的子查询返回了要使用的好 ID【参考方案3】:

这是旧帖子,但是用大表测试@smdrager 答案非常慢。我对此的解决方法是使用“inner join”而不是“where in”。

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

这真的很快。

【讨论】:

谢谢。下次我需要使用它时,我会尝试一下。【参考方案4】:

我会尝试这样的:

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

(免责声明:未经测试)

如果datetime较大的行也有较大的id,smdrager提出的方案比较快。

【讨论】:

【参考方案5】:

看起来所有现有答案都建议在整个桌子上做GROUP BY code。当它在逻辑上正确时,实际上这个查询将遍历整个(!)表(使用EXPLAIN 来确保)。就我而言,我的表中的行数少于 500k,执行 ...GROUP BY code需要 0.3 秒,这绝对是不可接受的。

但是我可以在这里使用我的数据知识(读作“显示帖子的最后一个 cmets”):

我只需要选择前 20 条记录 最后 X 条记录中具有相同代码的记录数量相对较少(~帖子中 cmets 的均匀分布,没有获得所有最近 cmets 的“病毒”帖子) 记录总数>>可用code的数量>>您想要获得的“***”记录数量

通过试验数字,我发现如果我只选择最后 50 条记录,我总能找到 20 条不同的 code。在这种情况下,以下查询有效(记住@smdrager 评论关于使用id 而不是datetime 的可能性很高)

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

只选择最后 50 个条目非常快,因为它不需要检查整个表格。剩下的就是从这 50 个条目中选择具有不同 code 的前 20 个条目。

显然,对 50 (100, 500) 个元素的集合的查询比对具有数十万个条目的整个表的查询要快得多。

原始 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 的列表,如果您想执行其他联接,请将此查询作为另一个嵌套查询并对其执行所有联接。

后端“后处理”

然后,您需要用您的编程语言处理数据,以便仅将具有不同 code 的记录包含到最终集合中。

某种 Python 伪代码:

records = select_simple_top_records(50)
added_codes = set()
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.add(record['code'])
    # If we found all top-20 required, finish
    if len(top_records) >= 20:
        break

【讨论】:

您好教父,我其实也有同样的问题。我有一个订单表。有些商品不受欢迎且不经常订购。如何确保包含这些项目(即 ItemId)?我喜欢你的方法并希望使用它。

以上是关于获取最后一组不同的记录的主要内容,如果未能解决你的问题,请参考以下文章

thinkphp 怎么获取多天最后一条记录

RESTFUL设计风格

如何从内存列表中获取一组不同的属性值?

django - 从不同的模型字段名称中获取一组对象

从 Oracle 中选择最新的两条不同记录

如何从具有最后时间戳的数据框中选择不同的记录