修改 Postgres 查询以使用 generate_series 对几个连续范围间隔中的每一个进行总体求和

Posted

技术标签:

【中文标题】修改 Postgres 查询以使用 generate_series 对几个连续范围间隔中的每一个进行总体求和【英文标题】:Modify Postgres query to use generate_series for overall summation over each of several consecutive range intervals 【发布时间】:2021-11-01 10:36:37 【问题描述】:

我对 SQL 还是很陌生,来自以 ORM 为中心的环境,所以请耐心等待。

提供表格形式:

CREATE TABLE event (id int, order_dates tsrange, flow int);
INSERT INTO event VALUES
    (1,'[2021-09-01 10:55:01,2021-09-04 15:16:01)',50),
    (2,'[2021-08-15 20:14:27,2021-08-18 22:19:27)',36),
    (3,'[2021-08-03 12:51:47,2021-08-05 11:28:47)',41),
    (4,'[2021-08-17 09:14:30,2021-08-20 13:57:30)',29),
    (5,'[2021-08-02 20:29:07,2021-08-04 19:19:07)',27),
    (6,'[2021-08-26 02:01:13,2021-08-26 08:01:13)',39),
    (7,'[2021-08-25 23:03:25,2021-08-27 03:22:25)',10),
    (8,'[2021-08-12 23:40:24,2021-08-15 08:32:24)',26),
    (9,'[2021-08-24 17:19:59,2021-08-29 00:48:59)',5),
    (10,'[2021-09-01 02:01:17,2021-09-02 12:31:17)',48); -- etc

下面的查询执行以下操作:

(这里,'the range'2021-08-03T00:00:00 从到 2021-08-04T00:00:00

    对于与the range 重叠的每个事件 将order_dates 的下时间戳值和上时间戳值修剪到the range 的范围内 将每个适用事件的剩余持续时间乘以 event.flow 值 将所有相乘的值相加得到最终的单值输出

基本上,我得到了所有与the range 重叠的事件,但仅根据每个事件在the range中的部分计算总值。

SELECT SUM("total_value")
FROM
    (SELECT (EXTRACT(epoch
        FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER * "event"."flow") AS "total_value"
    FROM "event"
    WHERE "event"."order_dates" && tsrange('2021-08-03T00:00:00'::timestamp, '2021-08-04T00:00:00'::timestamp, '[)')
    GROUP BY "event"."id",
        GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp),
        LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp),
        EXTRACT(epoch
            FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER, (EXTRACT(epoch
                FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER * "event"."flow")) subquery

DBFiddle 展示了这一点:https://www.db-fiddle.com/f/jMBtKKRS33Qf2FEoY5EdPA/1

这个查询一开始是一组复杂的 django 注释和聚合,我已经对其进行了简化,删除了这个问题不需要的部分。


因此,通过上述方法,我得到了输入范围内的单个总值(在本例中为 1 天范围)。

但我希望能够使用 generate_series 对几个连续范围间隔中的每一个执行相同的总体求和

例如:查询以下每个范围内的总数:

['2021-08-01T00:00:00', '2021-08-02T00:00:00')
['2021-08-02T00:00:00', '2021-08-03T00:00:00')
['2021-08-03T00:00:00', '2021-08-04T00:00:00')
['2021-08-04T00:00:00', '2021-08-05T00:00:00')

这与我之前的问题here 有点相关,但是由于查询范围内的很多地方都使用了查询范围的时间戳,所以我不知道该怎么做。

任何帮助/指导将不胜感激。

【问题讨论】:

【参考方案1】:

这应该可以帮助您入门:https://www.db-fiddle.com/f/qm4F7qqWZMrtXtMejimVJr/1。

基本上,我所做的是预先准备好具有 CTE 的范围,然后使用原始查询的 CROSS JOIN LATERAL 从该表表达式中进行选择。接下来,我将所有出现的 20210803 替换为 lower(target_range),将 20210804 替换为 upper(target_range),然后添加了 target_range 的 GROUP BY。请注意,只有在输入中至少与一行重叠的范围才会出现在输出中;将交叉连接更改为 LEFT JOIN 以始终在输出中看到您的输入范围,即使值为 null。 (如果是这样,ON TRUE 适合连接条件,因为您已经对内部子查询的 WHERE 进行了过滤。)

【讨论】:

AdamKG,非常感谢您!因为我想要所有范围,包括那些有空值的范围,所以我接受了你的建议。方便的是,我最初提供的数据集没有 20210801 的条目,因此我能够立即查看它是否有效 - 确实有效!这是修改后的 db-fiddle,以防将来有人需要它:db-fiddle.com/f/qm4F7qqWZMrtXtMejimVJr/2

以上是关于修改 Postgres 查询以使用 generate_series 对几个连续范围间隔中的每一个进行总体求和的主要内容,如果未能解决你的问题,请参考以下文章

在 Dbeaver 上创建 Postgres 表时不能使用“GENERATED ALWAYS AS IDENTITY”?

使用 postgres generate_series 生成定期计划

Spring Data JPA 方法或查询以使用 Postgres 列执行算术运算

使用 Postgres 范围的递归 SQL 查询以查找可用性

如何查询postgres数据库以获取某些坐标10公里半径内的所有点

postgres 基本使用