如何在不排序表的情况下获取最后N行

时间:2013-04-05 01:27:58

标签: sql postgresql

我有几百万行的表,需要获取特定ID的最后一行

例如最后一行有device_id = 123而最后一行有device_id = 1234

因为表格太大而且排序需要花费很多时间,所以可以在不排序表的情况下选择最后的200,然后只需订购那些200并获取我需要的行。

我该怎么做?

提前感谢您的帮助!

更新

我的PostgreSQL版本是9.2.1

示例数据:

time                      device_id         data       data ....
"2013-03-23 03:58:00-04" | "001EC60018E36" | 66819.59 | 4.203
"2013-03-23 03:59:00-04" | "001EC60018E37" | 64277.22 | 4.234
"2013-03-23 03:59:00-04" | "001EC60018E23" | 46841.75 | 2.141
"2013-03-23 04:00:00-04" | "001EC60018E21" | 69697.38 | 4.906
"2013-03-23 04:00:00-04" | "001EC600192524"| 69452.69 | 2.844
"2013-03-23 04:01:00-04" | "001EC60018E21" | 69697.47 | 5.156
....

请参阅SQLFiddle of this data

所以如果device_id = 001EC60018E21 我想要与device_id最新的行。 这个device_id的最后一行是我想要的行,但它可能是也可能不是表的最后一行。

2 个答案:

答案 0 :(得分:2)

就个人而言,我会在device_id上创建一个综合索引并降序time

CREATE INDEX table1_deviceid_time ON table1("device_id","time" DESC);

然后我会使用子查询为每个time找到最高device_id,并将子查询结果与device_idtime上的主表结合,以找到相关数据,例如:

SELECT t1."device_id", t1."time", t1."data", t1."data1"
FROM Table1 t1
INNER JOIN (
  SELECT t1b."device_id", max(t1b."time") FROM Table1 t1b GROUP BY t1b."device_id"
) last_ids("device_id","time") 
ON (t1."device_id" = last_ids."device_id" 
    AND t1."time" = last_ids."time");

请参阅this SQLFiddle

维护每个设备ID的最高时间戳的基于触发器的物化视图可能会有所帮助。但是,如果多个连接可以插入给定设备ID的数据,则由于连接争用更新锁,这将导致并发问题。如果你不知道什么时候会出现新的设备ID,这也是一种痛苦,因为你必须做一个upsert - 这是非常低效和笨拙的。此外,汇总表创建的额外写入负载和autovacuum工作可能不值得;最好只支付更昂贵的查询的价格。

BTW,time是列的一个可怕名称,因为它是内置的数据类型名称。如果可以,请使用更合适的东西。

答案 1 :(得分:1)

获取每个device_id的“最后”行的一般方法如下所示。

select *
from Table1 
inner join (select device_id, max(time) max_time
            from Table1
            group by device_id) T2
   on Table1.device_id = T2.device_id
  and Table1.time = T2.max_time;

在不使用ORDER BY的情况下获取“最后”200个device_id数字并不实用,但目前尚不清楚为什么您可能想要首先执行此操作。如果200是任意数字,那么您可以通过获取基于任意时间的表的子集来获得更好的性能。

select *
from Table1 
inner join (select device_id, max(time) max_time
            from Table1
            where time > '2013-03-23 12:03'
            group by device_id) T2
on Table1.device_id = T2.device_id
and Table1.time = T2.max_time;