首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >限制结果行以实现PostgreSQL的最小时间间隔

限制结果行以实现PostgreSQL的最小时间间隔
EN

Stack Overflow用户
提问于 2021-02-21 00:45:40
回答 1查看 120关注 0票数 1

背景:我正在运行TeslaMate/Grafana来监控我的汽车状态,其中一个仪表绘制了从数据库中获取的电池电量。我的服务器位于远程位置,并且在旧NAS的Dock中运行,因此查询性能和网络开销都很重要。

我发现koisk页面经常挂起,经过调查,这可能是由查询引起的--其中两个图从数据库返回了10~100k行的结果。我希望限制SQL查询返回的行数,因为对于绘制如此详细的间隔,绘图肯定没有那么高的精度。

我试着跟上这个答案并使用row_number()仅弹出第100行结果,但出现了更复杂的问题,即行之间的时间间隔不一致。

汽车有4种状态,正在行驶/在线/睡眠/离线。

  1. 如果汽车处于驾驶状态,则时间间隔可能小于200ms,因为每当有新数据时,汽车都会推送该状态。
  2. 如果汽车处于在线状态,时间间隔可能是几分钟,因为系统会主动从汽车获取状态。
  3. 更糟糕的是,如果系统认为汽车将要休眠并需要停止获取状态(以避免阻止汽车休眠),则根据设置,间隔时间最长可能为40分钟。
  4. 如果汽车处于睡眠/离线状态,则根本不会记录任何数据。

这显然使得跳过每第n行不是一个好主意,因为对于上面的情况2-4,许多数据点可能会丢失,因此Grafana无法以令人满意的精度绘制表示电池电量的正确图形。

我想知道是否可以按时间间隔跳过日期时间字段中的行,而不是行_number()没有太多的查询开销?即,从前一行中提取最小1000ms的每一行。

例如,我在表中有以下数据,我希望返回的行是第1行、第4行和第5行。

代码语言:javascript
运行
复制
row     date
[1] 1610000001000
[2] 1610000001100
[3] 1610000001200
[4] 1610000002000
[5] 1610000005000

我目前使用的(有问题的)方法如下:

代码语言:javascript
运行
复制
SELECT $__time(t.date), t.battery_level AS "SOC [%]" 
FROM (
  SELECT date, battery_level, row_number() OVER(ORDER BY date ASC) AS row
  FROM (
    SELECT battery_level, date
    FROM positions
    WHERE car_id = $car_id AND $__timeFilter(date)
    UNION ALL
    SELECT battery_level, date
    FROM charges c 
    JOIN charging_processes p ON p.id = c.charging_process_id
    WHERE $__timeFilter(date) AND p.car_id = $car_id) AS data
  ORDER BY date ASC) as t
  WHERE t.row % 100 = 0;

这个方法清楚地给出了一个问题,那就是只返回交替行,而不是我想要的行(给定最后一行读取t.row % 2 = 0)

PS:请忽略表结构和UNION从示例代码中,我没有深入挖掘这些表,这些表可能是其他调整,但无论如何都与这个问题无关。

提前感谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-02-21 01:23:31

您可以使用递归CTE:

代码语言:javascript
运行
复制
WITH RECURSIVE rec(cur_row, cur_date) AS (
    (
        SELECT row, date
        FROM t
        ORDER BY date
        LIMIT 1
    )

    UNION ALL

    (
        SELECT row, date
        FROM t
        JOIN rec
          ON t.date >= cur_date + 1000
        ORDER BY t.date
        LIMIT 1
    )
)
SELECT *
FROM rec;

cur_row

cur_date

1

1610000001000

4

1610000002000

5

1610000005000

在DB Fiddle上查看

改用函数可能会更快:

代码语言:javascript
运行
复制
CREATE OR REPLACE FUNCTION f() RETURNS SETOF t AS
$$
DECLARE
    row      t%ROWTYPE;
    cur_date BIGINT;
BEGIN
    FOR row IN
        SELECT *
        FROM t
        ORDER BY date
    LOOP
        IF row.date >= cur_date + 1000 OR cur_date IS NULL
        THEN
            cur_date := row.date;
            RETURN NEXT row;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql;
代码语言:javascript
运行
复制
SELECT *
FROM f();

row

date

1

1610000001000

4

1610000002000

5

1610000005000

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66294072

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档