SQL 查询对多个数据进行平均并与一个数据点进行比较以检查偏移量

问题描述 投票:0回答:1

我是 SQL 新手,所以请耐心等待。在我的一个表格中,我有一系列逆变器电压值,每分钟写入一次。我正在 GRAFANA 上显示数据,并希望找到异常点(如果有)。现在对我来说一个非常简单的解决方案是获取特定时间所有电压的平均值,并查看 1 个值是否偏离“很多”。

一些背景:使用 postresql,以及“timestamp”、“value”、“name”列 name 是标识符,现在逆变器的格式为“INVX_INVERTER_SG_BranchVoltageY”,其中 Y = {1,3,5,7,9,11,13,15,17,19,21,23} 且 X = {01 -18}.

第一个临时查询按预期工作,它生成一个包含当时所有组合的时间戳和平均值的表。在第二个 SELECT 查询中,我得到了胡说八道的值。 DB 表中没有名称包含“SG_BranchVoltage”的其他数据。我们友好的chatgpt正在吐垃圾。

理想情况下,结果将显示异常点的时间戳和值。然后我将在图表上绘制这些点。这只是表明该点不符合预期。

WITH average AS (
    SELECT
        "timestamp",
        AVG(value) AS avg_value
    FROM
        st_values
    WHERE
        "timestamp" BETWEEN NOW() - INTERVAL '1 day' AND NOW() AND
        "name" LIKE 'INV%_INVERTER_SG_Branch Voltage%'
    GROUP BY "timestamp"
)
SELECT
    v."timestamp",
    v.value
FROM
    st_values v
JOIN
    average av ON av."timestamp" = v."timestamp"
WHERE
    v."timestamp" BETWEEN NOW() - INTERVAL '1 day' AND NOW() AND
    v."name" LIKE 'INV%_INVERTER_SG_Branch Voltage%' AND
    ABS(v.value - av.avg_value) / av.avg_value > 0.3 
ORDER BY
    v."timestamp";

上面的代码(半?)不起作用。我不知道如何调试这个。这会产生真正异常的点(即位于我的一条线上),但有些点(假的?)不位于任何线上。

sql postgresql grafana
1个回答
0
投票

您的查询中没有任何内容可能会产生虚假值。重新审视您的

WHERE
条件可能是值得的:

  1. 如果您只想要这些来源:

    X = {01-18} and Y = {1,3,5,7,9,11,13,15,17,19,21,23}.
    

    您可以具体表达这一点,而不是诉诸正则表达式中的包罗万象的

    %

    "name" SIMILAR TO 
    'INV(0[1-9]|1[0-8])_INVERTER_SG_Branch Voltage(1|3|5|7|9|11|13|15|17|19|21|23)' 
    
    "name" ~ 
    'INV(0[1-9]|1[0-8])_INVERTER_SG_Branch Voltage(1|3|5|7|9|11|13|15|17|19|21|23)' 
    

    这对于 SQL 标准正则表达式 (

    SIMILAR TO
    ) 和 POSIX 语法 (
    ~
    ) 也同样有效。我不会怀疑您忽略了通配符违背您意图的文本变体,但从技术上讲可能是这样。

  2. 您还可以确保您获得的意外值不存在,因为您将数据输入到具有与在此处阅读时不同的

    TimeZone
    设置的表中。在这种情况下

    "timestamp" BETWEEN NOW() - INTERVAL '1 day' AND NOW()`
    
    如果源落后,则

    可能会比最后 24 小时的数据少;如果源提前 4 小时,则可能会获得 4 小时到 28 小时前的 24 小时数据。如果您的 "timestamp"

     列实际上是 
    timestamptz
    ,那么这应该不是问题。

  3. 您没有分享“线条”是什么以及您如何准确地绘制它们,但这也可能是错误的根源。

不幸的是,如果没有可重现的示例,那大多只是猜测。


窗口函数可以得到相同的结果,速度明显更快并且代码更少:DB<>Fiddle demo

SELECT "timestamp", value FROM ( SELECT "timestamp", value, avg(value)over w1 as avg_value, ABS(value - avg(value)over w1) / avg(value)over w1 as deviation FROM st_values WHERE "timestamp" BETWEEN NOW() - INTERVAL '1 day' AND NOW() AND "name" LIKE 'INV%_INVERTER_SG_Branch Voltage%' WINDOW w1 AS (PARTITION BY "timestamp") ) AS subq WHERE .3 < deviation ORDER BY "timestamp"
由于您正在处理时间序列数据,因此您可以删除索引中的“默认 10% 空白空间”以加快速度。您还可以将所有感兴趣的内容压缩到索引中并获得

仅索引扫描 create index tnv_idx on st_values ("timestamp","name") include("value") with(fillfactor=100);

如果您的查询仍需要运行顺序扫描,这些查询可能会受益于
cluster

:

cluster st_values using tnv_idx;

    


© www.soinside.com 2019 - 2024. All rights reserved.