我有一个 R 代码,我正尝试在 PostgreSQL 中重写它以提供 grafana 仪表板。我确实有基础知识,所以我几乎完成了脚本的其他部分,但我现在想在 PostgreSQL 中完成的事情超出了我的能力范围。我在 StackOverflow 上看到非常相似的已解决问题,但我似乎无法让它们为我工作。以下是一些我尝试改编的代码链接
https://stackoverflow.com/a/54370027/7885817
https://stackoverflow.com/a/44139381/7885817
对于我发布的重复性问题,我表示赞同。
非常感谢任何帮助!
所以,我的问题是:
我有时间戳重叠的消息。这些消息具有优先级:A和B(A更重要),开始时间和结束我。
严格来说:我想将 A 和 B 的持续时间相加 但是如果有重叠,我想找到优先级为 A 的消息的第一个开始时间和最后一个结束时间之间的持续时间,对于优先级为 B 的消息也是如此。如果 A 消息与 B 消息重叠,我想拆分这个持续时间在 A 消息的结束时间,直到那一点 B 消息的持续时间被分配给 A。 我做了一个视觉来支持我的神秘解释和我的数据的简化版本:
CREATE TABLE activities(
id int,
name text,
start timestamp,
"end" timestamp
);
INSERT INTO activitiesVALUES
(1, 'A', '2018-01-09 17:00:00', '2018-01-09 20:00:00'),
(2, 'A', '2018-01-09 18:00:00', '2018-01-09 20:30:00'),
(3, 'B', '2018-01-09 19:00:00', '2018-01-09 21:30:00'),
(4, 'B', '2018-01-09 22:00:00', '2018-01-09 23:00:00');
SELECT * FROM activities;
非常感谢您的宝贵时间!
最佳答案
更新
我原来的解决方案是不正确的。范围合并无法在常规窗口中处理。我使用相同的名称混淆了自己,trange
,忘记了窗口位于源行上方而不是结果行上方。请参阅更新的 SQL Fiddle使用完整的查询以及添加的记录来说明问题。
您可以使用 PostgreSQL range types 简化重叠要求并识别间隙和孤岛。 .
以下查询故意冗长以显示流程的每个步骤。可以组合多个步骤。
首先,添加一个包容性的[start, end]
每条记录的范围。
with add_ranges as (
select id, name, tsrange(start, "end", '[]') as t_range
from activities
),
id | name | t_range
----+------+-----------------------------------------------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"]
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"]
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"]
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"]
(4 rows)
识别由 &&
确定的重叠范围运算符并用 1
标记新岛屿的开始.
mark_islands as (
select id, name, t_range,
case
when t_range && lag(t_range) over w then 0
else 1
end as new_range
from add_ranges
window w as (partition by name order by t_range)
),
id | name | t_range | new_range
----+------+-----------------------------------------------+-----------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] | 1
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] | 0
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 1
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 1
(4 rows)
根据 new_range
的总和对组进行编号在 name
内.
group_nums as (
select id, name, t_range,
sum(new_range) over (partition by name order by t_range) as group_num
from mark_islands
),
id | name | t_range | group_num
----+------+-----------------------------------------------+-----------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] | 1
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] | 1
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 1
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 2
分组依据 name, group_num
获得在岛上花费的总时间以及完整的t_range
用于重叠扣除。
islands as (
select name,
tsrange(min(lower(t_range)), max(upper(t_range)), '[]') as t_range,
max(upper(t_range)) - min(lower(t_range)) as island_time_interval
from group_nums
group by name, group_num
),
name | t_range | island_time_interval
------+-----------------------------------------------+----------------------
A | ["2018-01-09 17:00:00","2018-01-09 20:30:00"] | 03:30:00
B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 02:30:00
B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 01:00:00
(3 rows)
对于统计A
之间重叠时间的需求消息和B
消息,查找出现 A
的时间消息与 B
重叠消息,并使用 *
求交点运算符。
priority_overlaps as (
select b.name, a.t_range * b.t_range as overlap_range
from islands a
join islands b
on a.t_range && b.t_range
and a.name = 'A' and b.name != 'A'
),
name | overlap_range
------+-----------------------------------------------
B | ["2018-01-09 19:00:00","2018-01-09 20:30:00"]
(1 row)
用name
将每次重叠的总时间相加.
overlap_time as (
select name, sum(upper(overlap_range) - lower(overlap_range)) as total_overlap_interval
from priority_overlaps
group by name
),
name | total_overlap_interval
------+------------------------
B | 01:30:00
(1 row)
计算每个name
的总时间.
island_times as (
select name, sum(island_time_interval) as name_time_interval
from islands
group by name
)
name | name_time_interval
------+--------------------
B | 03:30:00
A | 03:30:00
(2 rows)
加入每个name
的总时间从 overlap_time
调整CTE,并减去最终的调整 duration
值(value)。
select i.name,
i.name_time_interval - coalesce(o.total_overlap_interval, interval '0') as duration
from island_times i
left join overlap_time o
on o.name = i.name
;
name | duration
------+----------
B | 02:00:00
A | 03:30:00
(2 rows)
关于sql - 通过排除重叠本身,将具有优先级的重叠时间段的持续时间相加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62953977/