【问题标题】:Query for time-series-like counters in psql在 psql 中查询类似时间序列的计数器
【发布时间】:2023-01-20 03:16:13
【问题描述】:

我有以下内容仅追加psql中的表:

CREATE TABLE IF NOT EXISTS data (
    id UUID DEFAULT gen_random_uuid () PRIMARY KEY,

    test_id UUID NOT NULL,
    user_id UUID NOT NULL,
    completed BOOL NOT NULL DEFAULT False,

    inserted_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
);

CREATE INDEX some_idx ON data (user_id, test_id, inserted_at DESC);
CREATE INDEX some_idx2 ON data (test_id, inserted_at DESC);

对于给定的test_id,单个user_id 可能有多个条目,但只有一个可以是completedcompleted 条目也是最后一个)。

我正在查询给定的 test_id。我需要的是过去一周中每一天的类似时间序列的数据。对于每一天,我应该有以下内容:

  • 全部的- 唯一用户的总条目 WHERE inserted_at < "day"
  • 完全的- inserted_at < "day" 唯一用户的总完成条目

最终,totalcompleted 就像计数器,我只是想获取过去一周每一天的值。例如:

| date       | total | completed |
|------------|-------|-----------|
| 2022.01.19 | 100   | 50        |
| 2022.01.18 | 90    | 45        |
| ...        |       |           |

什么是具有高效查询计划的查询?我可以考虑添加新索引或修改现有索引。

PS:我这里有一个工作版本:

SELECT date, entered, completed
FROM (
     SELECT d::date AS date
     FROM generate_series('2023-01-12', now(),INTERVAL '1 day') AS d
) AS dates
cross join lateral (
    SELECT COUNT(DISTINCT user_id) AS entered,
           COUNT(1) FILTER (WHERE completed) AS completed // no need for distinct as completed is guaranteed to be once per user
    FROM data
    WHERE
        test_id = 'someId' AND
        inserted_at < dates.date
) AS vals

我不认为这是一个好的/高性能的解决方案,因为它会在每次横向连接迭代时重新扫描表。这是查询计划:

+---------------------------------------------------------------------------------------------------------------------------->
| QUERY PLAN                                                                                                                 >
|---------------------------------------------------------------------------------------------------------------------------->
| Nested Loop  (cost=185.18..185218.25 rows=1000 width=28) (actual time=0.928..7.687 rows=8 loops=1)                         >
|   ->  Function Scan on generate_series d  (cost=0.01..10.01 rows=1000 width=8) (actual time=0.009..0.012 rows=8 loops=1)   >
|   ->  Aggregate  (cost=185.17..185.18 rows=1 width=16) (actual time=0.957..0.957 rows=1 loops=8)                           >
|         ->  Bitmap Heap Scan on data  (cost=12.01..183.36 rows=363 width=38) (actual time=0.074..0.197 rows=779 loops>
|               Recheck Cond: ((test_id = 'someId'::uuid) AND (inserted_at < (d.d)::date))  >
|               Heap Blocks: exact=629                                                                                       >
|               ->  Bitmap Index Scan on some_idx2  (cost=0.00..11.92 rows=363 width=0) (actual time=>
|                     Index Cond: ((test_id = 'someId'::uuid) AND (inserted_at < (d.d)::date>
| Planning Time: 0.261 ms                                                                                                    >
| Execution Time: 7.733 ms                                                                                                   >
+---------------------------------------------------------------------------------------------------------------------------->

我确定我在这里缺少一些方便的功能,这些功能会有所帮助。感谢所有帮助:祈祷:

【问题讨论】:

  • 您可以使用带范围的窗口函数来获得相同的结果,而无需疯狂的交叉连接。
  • @霍根谢谢!我以前从未在 psql 中使用过窗口函数,所以我需要试验一下。你知道它的大致外观吗?
  • 是的...给我几分钟

标签: sql postgresql


【解决方案1】:

好的,当我查看它时,您毕竟不需要窗口函数——只是 SUM() 中带有 GROUP BY 的 CASE 语句的技巧

SELECT COUNT(DISTINCT user_id) AS entered,
       SUM(CASE WHEN completed THEN 1 ELSE 0 END) AS completed
FROM data
WHERE test_id = 'someId' 
GROUP BY inserted_at

【讨论】:

    猜你喜欢
    • 2013-05-01
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 2020-02-09
    • 2013-10-26
    • 1970-01-01
    相关资源
    最近更新 更多