繁体   English   中英

SQL 查询从与 N ids Timescaledb 对应的表中查找时间戳的最新或最大值

[英]SQL query for finding latest or max value of timestamp from table corresponding to N ids Timescaledb

我在时间刻度数据库中有一个表 tab1,它有 3 列标记、时间、值。 time 和 tag 构成了表的 pk:(time, tag)。

有超过 500 万 (50 000 000) 行。 我需要为 N 个标签中的每一个找到最新的时间戳或最大(时间)。

我尝试了一些东西,我将与每个人分享我的经验:

  1. 内部查询
SELECT "time", "tag", "value"
FROM tab1 
WHERE ("tag","time") IN 
(SELECT "tag", MAX("time") FROM tab1 WHERE "tag" IN(tag1,tag2) GROUP BY "tag" );

这给出了结果,但执行时间大约为 19 秒,这超出了可接受的限制

  1. 使用时间刻度数据库中的最后一个 function
SELECT tag, last(time, time), last(value,time)
FROM tab1
WHERE "tag" IN (tag1,tag2) GROUP BY "tag" ;

这将在 10 秒内给出 output。

我需要找到另一个类似于第二个可能表现更好的可行解决方案。 我尝试了一些其他的东西,比如 LATERAL JOIN (3), WINDOW FUNCTIONS (ROW_NUMBER, PARTITION) (4) 但解决方案并不像预期的那样。

  1. 使用横向给出多列的交叉,而不是像预期的那样具有最大时间的单个值。 此外,执行需要 15 秒,但这可能是由于查询错误。
SELECT table1."tag", table1."time",table1."value" from tab1 as table1
join lateral (
    SELECT table2 ."tag",table2 ."time" from tab1 as table2   
    where table2."tag" = table1."tag"
    order by table2."time" desc limit 1
) p on true
where table1."tag" in (tag1,tag2)
  1. 尝试分区时,我想像这样设置限制 1 :(按时间描述限制 1 按标记顺序分区)但它给出了语法错误。 没有限制 1 我没有得到最新的时间。
SELECT * from 
( SELECT *, row_number() over (partition by tag order by time desc) as rownum
from  tab1) a
where tag in (tag1,tag2)

任何人都可以建议 3,4 中的查询有什么问题或建议是否有其他选择。

我的表的索引是: 在此处输入图像描述

有几件事情会对此有所帮助,并使此查询更容易、性能更高。 第一个,可能也是最重要的一个是表/超表上的索引——它需要是标签上的多列索引,time desc——时间的顺序并不重要,但顺序索引中的列很重要。 tag必须是这里的第一列,因为我们需要先按标签搜索,然后获取最新时间,如果我们有单独的索引或者如果我们先按时间排序,这将是非常低效的。

您可以通过这样的调用创建此索引:

CREATE INDEX ON tab1 (tag, "time" DESC);

接下来是查询的制定。 为每个标记获取此信息的最简单方法是编写一个DISTINCT ON查询。 在 Timescale 中,我们优化了这种查询 制定它的方式有点奇怪,因此可能有点难以找到。

基本上你会这样写:

SELECT DISTINCT ON (tag) tag, "time" FROM tab1 ORDER BY tag, "time" DESC;

那应该给你你想要的。 这有点奇怪,但它会起作用!

我不会通过其他方法达到 go,但大多数方法都会通过索引得到显着改进,但这仍然可能是性能最高的方法。

如果你愿意,请发表评论,说明它是如何工作的,以及它是否为你加快了速度!

所以想出了其他两种方法:

  1. 使用横向

SELECT distinct t_outer.tag, t_top.time, t_top.value from tab1 t_outer join lateral ( SELECT * from tab1 t_inner where t_inner.tag = t_outer.tag order by t_inner.time desc limit 1) t_top on true where t_outer.tag in (标签 1)

这可行,但处理时间超过 14 秒。

  1. 使用 Window 函数

SELECT * FROM (SELECT tag,time,"value", rank() OVER (PARTITION BY tag order by time desc ) as RN FROM tab1 WHERE tag IN(tag1) ) 作为结果 WHERE results.RN=1;

这也有效并且需要大约 9 秒的时间来处理。

比较结果时,即使对于单个标签,内部查询和横向查询也是性能最差的查询。 所以他们被淘汰了。

到目前为止,Last() 和 Partition() 查询是我们的主要竞争者。 如果获取的列越少,Last() 的性能就越好,否则,如果我们获取所有列,则两者的执行时间相当。

我想补充的一点是,使用 ORDER BY LIMIT 查询比所有选项的执行方式都好(单个标签的执行时间少于 1 秒),但缺点是它不适用于多个标签输入。 因此,如果您需要查询单个标签并具有类似的数据库配置,您可以尝试这样做。

我想在这里为 David 的出色回答添加一些内容,那就是理解为什么列顺序对您的查询很重要。

您的主键实际上是一个多列 b 树索引。 这意味着要使用索引,查询必须遍历时间列,然后才能检查标签列。 就您而言,这对您不是很有帮助。 您希望能够做的是首先遍历标签,然后获取最近的时间。

为此,您必须首先在您的 btree 列表中拥有该标签。 我不知道 asc 或 desc 是否会在这里产生很大的不同,因为 PostgreSQL 可以在任一方向上搜索索引,并且您按时扫描的方向不取决于您扫描标签的方向。 然而,跳过扫描的时间尺度优化可能会发生,因此最好遵循该建议。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM