【问题标题】:Postgres query for difference between latest and first record of the dayPostgres查询当天最新记录和第一条记录之间的差异
【发布时间】:2021-11-28 17:59:01
【问题描述】:

类似这样的 Postgres 数据:

|   id  |         read_at        | value_1 |
| ------|------------------------|---------|
| 16239 | 2021-11-28 16:13:00+00 |   1509  |
| 16238 | 2021-11-28 16:12:00+00 |   1506  |
| 16237 | 2021-11-28 16:11:00+00 |   1505  |
| 16236 | 2021-11-28 16:10:00+00 |   1501  |
| 16235 | 2021-11-28 16:09:00+00 |   1501  |
| ..... | .......................|   ....  |
| 15266 | 2021-11-28 00:00:00+00 |   1288  |

每分钟添加一个值,并随着时间的推移而增加。

我想获取当天的当前总数并将其保存在 Grafana 统计面板中。上面是:221(1509-1288)。最新记录减去今天的第一条记录。

SELECT id,read_at,value_1
FROM xyz
ORDER BY id DESC
LIMIT 1;

由此给出最新记录 (A)。

SELECT id,read_at,value_1
FROM xyz
WHERE read_at = CURRENT_DATE
ORDER BY id DESC
LIMIT 1;

由此给出当天的第一条记录 (B)。

Grafana 无法对此 (A-B) 进行数学运算。最好是单个查询。

遗憾的是,我的数据库知识很少,构建查询的尝试没有成功,现在已经花了整个下午。

解决这个问题的理论思路:

  • 在今天的时间范围内从最大值中减去最小值。
  • 使用滞后,将其滞后于今天记录的记录数。从最新值中减去滞后值。
  • 窗口函数。

最好的方法(性能方面)是什么?如何编写这样的查询?

【问题讨论】:

    标签: sql postgresql grafana


    【解决方案1】:

    使用窗口函数(这是t 子查询)计算当天每条记录的累积总数last_value - first_value,然后选择最新的。

    select current_total, read_at::date as read_at_date 
    from
    (
      select last_value(value_1) over w - first_value(value_1) over w as current_total,
             read_at 
      from the_table
      where read_at >= current_date and read_at < current_date + 1
      window w as (partition by read_at::date order by read_at)
    ) as t
    order by read_at desc limit 1;
    

    但是,如果确定 value_1 只会“随时间增加”,那么简单的分组就可以了,这是迄今为止最好的性能方式:

    select max(value_1) - min(value_1) as current_total, 
           read_at::date as read_at_date 
    from the_table
    where read_at >= current_date and read_at < current_date + 1
    group by read_at::date;
    

    【讨论】:

    • 第二个执行速度非常快!谢谢。
    【解决方案2】:

    请检查它是否有效。

    由于您打算在 Grafana 中发布它,因此查询不会强加句点过滤器。

    https://www.db-fiddle.com/f/4jyoMCicNSZpjMt4jFYoz5/3080

    create table g (id int, read_at timestamp, value_1 int);
    
    insert into g
    values
    (16239, '2021-11-28 16:13:00+00', 1509),
    (16238, '2021-11-28 16:12:00+00', 1506),
    (16237, '2021-11-28 16:11:00+00', 1505),
    (16236, '2021-11-28 16:10:00+00', 1501),
    (16235, '2021-11-28 16:09:00+00', 1501),
    (15266, '2021-11-28 00:00:00+00', 1288);
    
    select date(read_at), max(value_1) - min(value_1)
    from g
    group by date(read_at);
    

    【讨论】:

    • 在这种情况下,句点过滤器在逻辑上可能是多余的,但它会大大提高性能,特别是如果表很大并且read_at 上有索引。
    • 是的,@Stefanov.sm。 the eternity 的仪表板没有意义。对于索引,我看不出这将如何与该表本身一起使用。但是,他可能会添加另一个计算列,然后在其上创建一个索引。
    • 时间序列或传感器数据表确实会随着时间的推移而增长很多。在这样的表中,时间列上的索引确实有意义,因为仪表板可能只是其用例之一。
    • @Stefanov.sm,我部分同意。 TIME 列上的索引在这里没有多大用处。我认为最好ALTER TABLE &lt;XXX&gt; ADD COLUMN read_at_date DATE AS DATE(read_at)(语法肯定是错误的),然后CREATE INDEX AK_XXX_READ_AT_DATE_VALUE ON the_table(read_at_date, value_1)
    • 添加了order by date desc LIMIT 1 以使其正常工作。谢谢。
    【解决方案3】:

    由于您的数据包含 2 个不同时间(16:09 和 16:10)的多个值,这表明最小值和最大值可能并不总是在时间间隔内增加。放开下跌的可能性。那么您想要最大 - 最小读数还是最小/最大时间读数的差异。下面的 get value difference 是为了得到标题中指示的当天的第一个和最后一个读数之间的差异。

    with parm(dt) as 
         ( values (date '2021-11-28') )
       , first_read (f_read,f_value) as 
         ( select read_at, value_1 
             from test_tbl
            where read_at at time zone 'UTC'= 
                       ( select min(read_at at time zone 'UTC') 
                                from test_tbl
                                join parm  
                                  on ((read_at at time zone 'UTC')::date = dt) 
                            )
         ) 
       , last_read (l_read, l_value) as 
         ( select read_at,value_1 
             from test_tbl
            where read_at at time zone 'UTC'= 
                         ( select max(read_at at time zone 'UTC') 
                                from test_tbl
                                join parm  
                                  on ((read_at at time zone 'UTC')::date = dt) 
                            )
         ) 
    select l_read, f_read, l_value, f_value, l_value - f_value as "Day Difference"
     from last_read 
     join first_read on true;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 1970-01-01
      • 2012-03-11
      • 2012-03-26
      • 1970-01-01
      相关资源
      最近更新 更多