我需要从一组重叠的日期中获取不重叠的日期范围列表,并计算在此重叠期间内硬币的总和。我尝试通过谷歌搜索示例,但目前为止没有找到合适的结果。可能是我使用的关键词不正确?
我有一组重叠的日期。
这里是一个演示它应该如何工作的图示。
结果:
我有一组重叠的日期。
1.1.2018 - 31.1.2018 80
7.1.2018 - 10.1.2018 10
7.1.2018 - 31.1.2018 10
11.1.2018 - 31.1.2018 5
25.1.2018 - 27.1.2018 5
2.2.2018 - 23.2.2018 100
期望的结果是:
1.1.2018 - 6.7.2018 80 coins
7.1.2018 - 10.1.2018 100 coins
11.1.2018 - 24.1.2018 95 coins
25.1.2018 - 27.1.2018 100 coins
28.1.2018 - 31.1.2018 95 coins
2.2.2018 - 23.2.2018 100 coins
这里是一个演示它应该如何工作的图示。
|------------------------------|
|---|
|-----------------------|
|-------------------|
|---|
|----------------------|
Outcome
|------|---|----------|---|----| |----------------------|
80 100 95 100 95 100
这是我的测试数据
drop table coinsonperiod2;
create table coinsonperiod2(
id serial,
startdate date,
enddate date,
coins integer,
userid integer
);
insert into coinsonperiod2 (startdate, enddate, coins,userid) values
('2018-01-01','2018-01-31', 80,1)
, ('2018-01-07','2018-01-10', 10,1)
, ('2018-01-07','2018-01-31', 10,1)
, ('2018-01-11','2018-01-31', 5,1)
, ('2018-01-25','2018-01-27', 5,1)
, ('2018-02-02','2018-02-23', 100,2)
, ('2018-01-01','2018-01-31', 80,2)
, ('2018-01-07','2018-01-10', 10,2)
, ('2018-01-07','2018-01-31', 10,2)
, ('2018-01-11','2018-01-31', 5,2)
, ('2018-01-25','2018-01-27', 5,2)
, ('2018-02-02','2018-02-23', 100,3)
;
更新: 实际上,StephenM和joops的答案都不符合我的期望结果。两个答案都显示enddate错误。
当一个周期结束时,下一个周期应该从第二天开始(如果有间隔,则稍后开始)。在我的期望结果中,1.1.2018-6.1.2018包括第6天。第6天和第7天之间没有间隔,因为第7天包括在7.1.2018-10.1.2018中。
更新2: 现在我明白了开放、半开放和闭合区间之间的区别。在joops的解决方案中,必须根据半开放区间进行计算,但我的期望结果是闭合区间。这就是为什么必须减少enddate以使结果成为闭合区间。如果我错了,请纠正我。
我还在示例数据中添加了userid并对joops的解决方案进行了一些修改。 这是给我想要的结果的查询。
with changes AS (
SELECT
userid,
startdate AS tickdate,
coins,
1 AS cover
FROM coinsonperiod2
UNION ALL
-- add 1 day to correct intervals into half open intervals, so the calculation is correct
SELECT
userid,
1 + enddate AS tickdate,
-1 * coins,
-1 AS cover
FROM coinsonperiod2
)
, sumchanges AS (
SELECT
userid,
tickdate,
SUM(coins) AS change,
SUM(cover) AS cover
FROM changes
GROUP BY tickdate, userid
)
, aggregated AS (
SELECT
userid AS userid,
tickdate AS startdate,
lead(tickdate)
over www AS enddate,
sum(change)
OVER www AS cash,
sum(cover)
OVER www AS cover
FROM sumchanges
WINDOW www AS (
partition by userid
ORDER BY tickdate )
)
-- reduce 1 day from the enddate to make closed interval
SELECT
userid
, startdate
, enddate-1 as enddate
, cash
, cover
FROM aggregated
WHERE cover > 0
ORDER BY userid, startdate
;
结果:
![结果](https://istack.dev59.com/KJJVZ.webp)