【发布时间】:2018-10-16 07:33:43
【问题描述】:
我需要从重叠日期列表中获取彼此不重叠的日期范围列表,并获取重叠期间的硬币总和。我试过用谷歌搜索一个例子,但到目前为止还没有运气。我可能没有使用正确的关键词?
我有一个重叠日期列表
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 天。 6th 和 7th 之间没有差距,因为 7th 包含在 7.1.2018-10.1.2018。
更新2: 现在我明白了开放、半开放和封闭间隔之间的区别。在 joops 解决方案中,必须针对半开区间进行计算,但我想要的结果是闭区间。这就是为什么必须减少 enddate 以使结果为闭区间。如果我错了,请纠正我。
我还在示例数据中添加了用户 ID,并进一步修改了 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
;
【问题讨论】:
-
非常好的问题,带有示例数据和可视化。我希望所有第一次发帖的人都能在他们的问题上投入那么多精力。
-
我假设您想要的结果中第一个时期的结束日期有错字(如果我正确解释了您的日期格式,应该是 6.1.2018)
-
诀窍是将水平触发表示转换为边缘触发,将它们累加并转换回来。
-
不重叠的部分其实很简单:rextester.com/JMZC97931
-
添加键域(
user_id) 并没有真的改变问题。并且不应该更改问题并在问题主体中添加答案。相反,问一个新问题,(也许回答你自己的问题)
标签: sql postgresql overlap