Postgres - 返回具有匹配校验和的每个组中具有最新时间戳的行

时间:2015-01-15 10:11:06

标签: sql postgresql greatest-n-per-group

我有一个表(Postgres 9.3)定义如下

 CREATE TABLE tsrs (
     id SERIAL PRIMARY KEY,
     customer_id INTEGER NOT NULL REFERENCES customers,
     timestamp TIMESTAMP WITHOUT TIME ZONE,
     licensekeys_checksum VARCHAR(32));

此处的相关详细信息是customer_id,时间戳和licensekeys_checksum。此表中可以有多个具有相同customer_id的条目,其中一些可能具有匹配的licensekey_checksum条目,有些可能不同。

我有兴趣构建一个查询,该查询将为每组行返回一个包含1行的表,其中包含licensekeys_checksum条目。为每个组返回的行应该是具有最新/最新时间戳的条目。

我很抱歉,如果这很明显 - 我对SQL很新,而且我对这个查询有点不了解。任何帮助/指针将不胜感激!

示例输入

1, 2, 2014-08-21 16:03:35, 3FF2561A
2, 2, 2014-08-22 10:00:41, 3FF2561A
2, 2, 2014-06-10 10:00:41, 081AB3CA
3, 5, 2014-02-01 12:03:23, 299AFF90
4, 5, 2013-12-13 08:14:26, 299AFF90
5, 6, 2013-09-09 18:21:53, 49FFA891

期望的输出

2, 2, 2014-08-22 10:00:41, 3FF2561A
2, 2, 2014-06-10 10:00:41, 081AB3CA
3, 5, 2014-02-01 12:03:23, 299AFF90
5, 6, 2013-09-09 18:21:53, 49FFA891

编辑: 我已经设法根据以下评论拼凑查询,并在互联网上搜索数小时:)

select * from tsrs
inner join (
select licensekeys_checksum, max(timestamp) as mts
from tsrs
group by licensekeys_checksum )
x on x.licensekeys_checksum = tsrs.licensekeys_checksum and x.mts = tsrs.timestamp;

它似乎有效,但很难验证(如果我说我完全理解它是如何工作的话,我会说谎:))。如果我走在正确的轨道上,有人可以告诉我吗?

3 个答案:

答案 0 :(得分:3)

您在问题中的查询应该比当前接受的答案中的查询执行得更好。使用EXPLAIN ANALYZE进行测试。

如果您正在寻找更简单(也更快)的内容,请使用DISTINCT ON

SELECT DISTINCT ON (licensekeys_checksum) *
FROM   tsrs
ORDER  BY licensekeys_checksum, timestamp DESC;

SQL Fiddle - 建立在@WingedPanther(kudos)提供的基础之上 详细解释:

答案 1 :(得分:2)

试试这个

select * 
from tsrs
where (timestamp,licensekeys_checksum) in (
                                          select max(timestamp)
                                                ,licensekeys_checksum
                                          from tsrs 
                                          group by licensekeys_checksum) 

>SqlFiddle Demo

with cte as (
            select id
                   ,customer_id
                   ,timestamp
                   ,licensekeys_checksum
                   ,row_number () over (partition by  licensekeys_checksum  ORDER BY timestamp DESC) as rk
            from  tsrs)
select  id
       ,customer_id
       ,timestamp
       ,licensekeys_checksum  
from cte where rk=1 order by id

>SqlFiddle Demo


参考:Window Functionsrow_number()CTE

答案 2 :(得分:2)

使用NOT EXISTS(...)

替代重复数据删除
SELECT *
FROM tsrs t
WHERE NOT EXISTS (
    SELECT *
    FROM tsrs x
    WHERE x.customer_id = t.customer_id                  -- same customer
    AND x.licensekeys_checksum = t.licensekeys_checksum  -- same checksum
    AND x.ztimestamp > t.ztimestamp                      -- but more recent
    );