【问题标题】:How can I fetch the last N rows, WITHOUT ordering the table如何在不排序表的情况下获取最后 N 行
【发布时间】:2013-04-05 01:27:58
【问题描述】:

我有数百万行的表,需要获取特定 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 的最后一行是我想要的行是受让人,但它可能是也可能不是表的最后一行。

【问题讨论】:

  • 最后 200 行保证有最后一行 id = 123 和 id = 1234
  • 如果您知道 ID,为什么还要费心获取整个表?战略性放置的 RDBMS 索引可以很好地解决问题。
  • 最好显示您的 PostgreSQL 版本、示例数据和预期结果,这样我们就不必为您创建测试用例了。
  • 在我的示例中的 id 不是行 id,它们是特定数据分组的 id。很抱歉造成混乱
  • 您是否尝试过在 DATE 字段上创建逆序索引?这应该加快你的'ORDER BY date DESC'子句。然后你可以使用 SELECT 和 LIMIT。

标签: sql postgresql


【解决方案1】:

我个人会在device_id 和降序time 上创建一个复合索引:

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

然后我会使用子查询为每个device_id 查找最高的time,并将子查询结果与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,这也是一种痛苦——这是非常低效和笨拙的事情。此外,汇总表创建的额外写入负载和自动清理工作可能不值得;为更昂贵的查询付出代价可能会更好。

顺便说一句,time 是一个糟糕的列名称,因为它是一个内置的数据类型名称。如果可以,请使用更合适的东西。

【讨论】:

  • 可能到了我需要这样做的地步。并且“时间”作为一个名称不能更改,已经有太多的依赖脚本,但我会记住这一点。感谢您的帮助!
【解决方案2】:

获取每个 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;

【讨论】:

  • 有趣的计时,一分钟内相同的查询,同时编辑
  • @CraigRinger:嘿,如果我写的 SQL 和你一样,我会把它写在我的简历上。
  • 谢谢 Mike Sherrill 'Catcall' 和 Craig Ringer!我需要一点时间来消化这些绝妙的想法,是的,200 是一个任意数字,你说得对,任意数据更好。
  • 谢谢你们!该查询运行良好,它仍然很慢,但已经比以前快了很多!
  • 我刚刚找到了答案:current_timestamp - interval '5' minute
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 2022-08-06
  • 2018-11-01
  • 2021-10-27
  • 2016-01-23
相关资源
最近更新 更多