修改 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 查询以查找可用性