如何在 TimeScaleDB、PostgreSQL 的 time_bucket_gapfill() 中使用 AVG() 和 GROUP BY?

问题描述

我在我的 Postgresql 中使用 TimescaleDB,我有以下两个表:

windows_log

| windows_log_id |      timestamp      | computer_id | log_count |
------------------------------------------------------------------
|        1       | 2021-01-01 00:01:02 |     382     |     30    |
|        2       | 2021-01-02 14:59:55 |     382     |     20    |
|        3       | 2021-01-02 19:08:24 |     382     |     20    |
|        4       | 2021-01-03 13:05:36 |     382     |     10    |
|        5       | 2021-01-03 22:21:14 |     382     |     40    |

windows_reliability_score

| computer_id (FK) |      timestamp      | reliability_score |
--------------------------------------------------------------
|        382       | 2021-01-01 22:21:14 |          6        |
|        382       | 2021-01-01 22:21:14 |          6        |
|        382       | 2021-01-01 22:21:14 |          6        |
|        382       | 2021-01-02 22:21:14 |          1        |
|        382       | 2021-01-02 22:21:14 |          3        |
|        382       | 2021-01-03 22:21:14 |          7        |
|        382       | 2021-01-03 22:21:14 |          8        |
|        382       | 2021-01-03 22:21:14 |          9        |

注意:在两个表中都是在时间戳列(hypertable)上建立索引

所以我试图获得每个时间段的平均可靠性分数,但它只是给了我所有内容的平均值,而不是每个特定时间段的平均值......

这是我的查询

SELECT time_bucket_gapfill(CAST(1 * INTERVAL '1 day' AS INTERVAL),wl.timestamp) AS timestamp,COALESCE(SUM(log_count),0) AS log_count,AVG(reliability_score) AS reliability_score
FROM windows_log wl
JOIN reliability_score USING (computer_id)
WHERE wl.time >= '2021-01-01 00:00:00.0' AND wl.time < '2021-01-04 00:00:00.0'
GROUP BY timestamp
ORDER BY timestamp asc

这是我要找的结果:

|      timestamp      | log_count | reliability_score |
-------------------------------------------------------
| 2021-01-01 00:00:00 |     30    |          6        |
| 2021-01-02 00:00:00 |     20    |          2        |
| 2021-01-03 00:00:00 |     20    |          8        |

但这就是我得到的:

|      timestamp      | log_count | reliability_score |
-------------------------------------------------------
| 2021-01-01 00:00:00 |     30    |        5.75       |
| 2021-01-02 00:00:00 |     20    |        5.75       |
| 2021-01-03 00:00:00 |     20    |        5.75       |

解决方法

主要问题是连接条件在列 computer_id 上,其中两个表具有相同的值 382。因此,表 windows_log 中的每一列都将与表 reliability_score 中的每一列(所有行的笛卡尔积)连接起来。此外,分组是在列 timestamp 上完成的,这是不明确的,很可能从 timestamp 解析为 windows_log。这导致平均值将使用 reliability_score 的每个时间戳值的所有值,并解释不希望的结果。

SELECT documentation中的windows_log描述中解释了有利于内部列(即表列)的解决歧义:

如果出现歧义,GROUP BY 名称将被解释为输入列名称而不是输出列名称。

为了避免分组,包括所有匹配计算机 id 的行,GROUP BY 可用于分组。这将允许将 windows_log_id 带入查询结果。如果希望保留输出名称 log_count,GROUP BY 应使用对位置的引用。例如:

timestamp

对于 ORDER BY 这不是问题,因为使用了输出名称。来自同一个文档:

如果 ORDER BY 表达式是一个匹配输出列名和输入列名的简单名称,ORDER BY 会将其解释为输出列名。

,

鉴于我们可以从您的示例中收集到的信息,没有简单的方法可以使用给定的函数在这两个表之间进行连接并获得您想要的结果。所呈现的模式只是让这变得困难。

如果这确实是您的数据/架构的样子,那么一种解决方案是使用多个 CTE 从每个不同的表中获取两个值,然后根据存储桶和计算机进行连接。

WITH wrs AS (
    SELECT time_bucket_gapfill('1 day',timestamp) AS bucket,computer_id,AVG(reliability_score) AS reliability_score  
    FROM windows_reliability_score
    WHERE timestamp >= '2021-01-01 00:00:00.0' AND timestamp < '2021-01-04 00:00:00.0'
    GROUP BY 1,2
),wl AS (
    SELECT time_bucket_gapfill('1 day',wl.timestamp) bucket,wl.computer_id,sum(log_count) total_logs
    FROM windows_log wl
    WHERE timestamp >= '2021-01-01 00:00:00.0' AND timestamp < '2021-01-04 00:00:00.0'
    GROUP BY 1,2
)
SELECT wrs.bucket,wrs.computer_id,reliability_score,total_logs
FROM wrs LEFT JOIN wl ON wrs.bucket = wl.bucket AND wrs.computer_id = wl.computer_id;

过滤必须在内部应用于每个查询,因为可能不会发生对外部查询的下推,因此您将在应用日期过滤器之前扫描整个超表(我假设不是您想要的)。

我尝试快速重新创建您的示例架构,因此如果我在某处弄错了名称,我深表歉意。