Sql 每行查询最后N个相关行

Sql 每行查询最后N个相关行,sql,performance,postgresql,indexing,query-optimization,Sql,Performance,Postgresql,Indexing,Query Optimization,我有以下查询,它获取每个台站最近N次观测的id: 我在id、station\u id上创建了索引 这是我提出的唯一一个解决方案,它可以在每个站点获取多条记录。然而,对于一个包含81000条记录的表来说,速度相当慢,为154.0毫秒 如何加快查询速度 只有当您不需要查询最新的实时数据时,这才是一个好的选择 准备需要postgresql 9.3 drop materialized view test; create materialized view test as select * from (

我有以下查询,它获取每个台站最近N次观测的id:

我在id、station\u id上创建了索引

这是我提出的唯一一个解决方案,它可以在每个站点获取多条记录。然而,对于一个包含81000条记录的表来说,速度相当慢,为154.0毫秒


如何加快查询速度

只有当您不需要查询最新的实时数据时,这才是一个好的选择

准备需要postgresql 9.3

drop materialized view test;
create materialized view test as select * from (
  SELECT station_id, id, created_at,
      row_number() OVER(
          PARTITION BY station_id
          ORDER BY created_at DESC
      ) as rn
  FROM (
      SELECT
          station_id,
          id,
          created_at
      FROM observations
  ) s
 ) q WHERE q.rn <= 100 -- use a value that will be your max limit number for further queries
ORDER BY station_id, rn DESC ;


create index idx_test on test(station_id,rn,created_at);

我有另一个解决方案,它不需要物化视图,可以处理实时的最新数据。但考虑到您不需要最新数据,这种物化视图的效率要高得多。

假设至少有Postgres 9.3

指数 首先,多列索引将有助于:

CREATE INDEX observations_special_idx
ON observations(station_id, created_at DESC, id)
在DESC处创建的_稍微更适合,但是索引仍然会以几乎相同的速度向后扫描,而没有DESC

假定CeRealItAt定义为NULL,否则在索引和查询中考虑DESCL NULL:

最后一个列id只有在您从中得到一个值时才有用,如果您不断添加大量新行,这可能不起作用。在这种情况下,请从索引中删除id

更简单的查询仍然很慢 简化查询,内部子选择没有帮助:

SELECT id
FROM  (
  SELECT station_id, id, created_at
       , row_number() OVER (PARTITION BY station_id
                            ORDER BY created_at DESC) AS rn
  FROM   observations
  ) s
WHERE  rn <= #{n}  -- your limit here
ORDER  BY station_id, created_at DESC;
如果没有站点表,下一个最好的方法是创建并维护一个站点表。可能会添加外键引用以强制关系完整性

如果这不是一个选项,您可以动态提取这样一个表。简单的选择是:

SELECT DISTINCT station_id FROM observations;
SELECT station_id FROM observations GROUP BY 1;
将其用作上述简单查询中stations表的插入式替换:

WITH RECURSIVE stations AS (
   (
   SELECT station_id
   FROM   observations
   ORDER  BY station_id
   LIMIT  1
   )
   UNION ALL
   SELECT (SELECT o.station_id
           FROM   observations o
           WHERE  o.station_id > s.station_id
           ORDER  BY o.station_id
           LIMIT  1)
   FROM   stations s
   WHERE  s.station_id IS NOT NULL
   )
SELECT o.id
FROM   stations s
CROSS  JOIN LATERAL (
   SELECT o.id, o.created_at
   FROM   observations o
   WHERE  o.station_id = s.station_id
   ORDER  BY o.created_at DESC
   LIMIT  #{n}  -- your limit here
   ) o
WHERE  s.station_id IS NOT NULL
ORDER  BY s.station_id, o.created_at DESC;
这应该仍然比你拥有的要快几个数量级

SQLFiddle9.6
dbfiddle

分区在这种情况下没有帮助。您的观察表低于8MB。它将放入服务器的内存中。您的查询计划包含对观察表的顺序扫描。问题:从数据库查询最新的实时数据有多重要?如果您只能查询不超过2小时的数据,这会是一个问题吗?你能告诉我们观察表中有多少行吗?只是您可能希望使用散列在单独的列上创建索引的大小。使用哈希列在表上创建索引名;你有81000张唱片。关键问题:1。有多少个不同的电台?2.你们有列出所有电台的表格吗?如果没有,创建和维护一个有问题吗?3.一如既往:你对博士后的看法?4.表定义psql中的CREATE语句或\d观察值?一个更快的查询应该是可能的,这取决于站点的数量……更多细节:它是一个开源的Rails应用程序,从廉价站点收集风数据。目前,只有大约3个站点每天5分钟~288次观测,在3G网络不稳定的情况下更少。实时站点:。大表的最新记录几乎不可能用物化视图覆盖,物化视图更适合于只读数据。这就是为什么我问这样一个问题:会有多少数据。他的观察表是8MB。它离大还差得很远。如果他将更新/删除行,或者只是向该表添加新行,这也很有趣。我有一个轻量级的解决方案,如果只添加行,而不是更新或删除行,它就可以工作。另一种是使用索引,但会减慢新观测值的插入。总会有一个折衷的办法。你的折衷办法是对的。艺术是在这些行业中得到一笔好交易。物化视图并不是最新行的最佳工具,因为它根据定义只包含过去的快照-除非您使用每个新条目自动刷新,这将是一个高昂的代价。观测数据几乎是只读的,该表的行数仍然相对较小,只有8万行,但如果我们获得一些赞助资金来建造/放置更多的气象站,则可能会呈指数级增长。这意味着您永远不会更新或删除此表中的行。在这种情况下,最好在插入行时从触发器更新行号。然后你可以在行号上创建一个索引,整个查询就变成了一个简单的索引扫描…感谢你的详细回答,今晚我们将尝试它。运行速度大约为35毫秒,这是一个巨大的改进。谢谢@帕皮蒂格:哪一个?n=?有或没有电视台?您是否创建了索引并运行了分析检查?在解释分析输出中是否看到仅索引扫描?改进很好,但是我看到了更好的结果。我有一个stations表o。stations_id是外键,所以我尝试了第一个JOIN横向查询。添加特别观察结果似乎没有任何重大影响。我只在我的本地机器上试过,因为我需要等待我的合作者在Heroku上更新postgres。@papirtiger:除非表格没有足够的真空或大量的书写,否则你应该 ee索引仅使用观察值进行扫描,如在FIDLE check视图执行计划中的观察值。你在解释输出中看到了吗?注意,我用DESC对索引做了一些改进,但这不会有太大的区别。该指数在任何情况下都应使用,并具有重大影响。
SELECT id
FROM  (
  SELECT station_id, id, created_at
       , row_number() OVER (PARTITION BY station_id
                            ORDER BY created_at DESC) AS rn
  FROM   observations
  ) s
WHERE  rn <= #{n}  -- your limit here
ORDER  BY station_id, created_at DESC;
SELECT o.id
FROM   stations s
CROSS  JOIN LATERAL (
   SELECT o.id
   FROM   observations o
   WHERE  o.station_id = s.station_id  -- lateral reference
   ORDER  BY o.created_at DESC
   LIMIT  #{n}  -- your limit here
   ) o
ORDER  BY s.station_id, o.created_at DESC;
SELECT DISTINCT station_id FROM observations;
SELECT station_id FROM observations GROUP BY 1;
WITH RECURSIVE stations AS (
   (                  -- extra pair of parentheses ...
   SELECT station_id
   FROM   observations
   ORDER  BY station_id
   LIMIT  1
   )                  -- ... is required!
   UNION ALL
   SELECT (SELECT o.station_id
           FROM   observations o
           WHERE  o.station_id > s.station_id
           ORDER  BY o.station_id
           LIMIT  1)
   FROM   stations s
   WHERE  s.station_id IS NOT NULL  -- serves as break condition
   )
SELECT station_id
FROM   stations
WHERE  station_id IS NOT NULL;      -- remove dangling row with NULL
WITH RECURSIVE stations AS (
   (
   SELECT station_id
   FROM   observations
   ORDER  BY station_id
   LIMIT  1
   )
   UNION ALL
   SELECT (SELECT o.station_id
           FROM   observations o
           WHERE  o.station_id > s.station_id
           ORDER  BY o.station_id
           LIMIT  1)
   FROM   stations s
   WHERE  s.station_id IS NOT NULL
   )
SELECT o.id
FROM   stations s
CROSS  JOIN LATERAL (
   SELECT o.id, o.created_at
   FROM   observations o
   WHERE  o.station_id = s.station_id
   ORDER  BY o.created_at DESC
   LIMIT  #{n}  -- your limit here
   ) o
WHERE  s.station_id IS NOT NULL
ORDER  BY s.station_id, o.created_at DESC;