तर्क है:
- शुरुआत में किसी अंतराल के मान को संचयी योग में जोड़ें
- अंत में एक अंतराल के इस योग से इसका मान घटाएं
- लेकिन स्वीप करने के लिए डेटलाइन, हमें सभी (अद्वितीय) दिनांक/समय टिकटों को एकत्रित करना होगा, या तो प्रारंभ करें या रोकें।
तो बात यह है:डेटा को अंतराल . की एक श्रृंखला से परिवर्तित करें (प्रारंभ/रोक) ईवेंट . की एक श्रृंखला के लिए , और इन पर एकत्र करें।
-- \i tmp.sql
create table coinsonperiod(
id serial,
startdate date,
enddate date,
coins integer
);
insert into coinsonperiod (startdate, enddate, coins) values
('2018-01-01','2018-01-31', 80)
, ('2018-01-07','2018-01-10', 10)
, ('2018-01-07','2018-01-31', 10)
, ('2018-01-11','2018-01-31', 5)
, ('2018-01-25','2018-01-27', 5)
, ('2018-02-02','2018-02-23', 100)
;
WITH changes AS (
SELECT startdate AS tickdate , coins
, 1 AS cover
FROM coinsonperiod
UNION ALL
-- add 1 day to convert to half-open intervals
SELECT 1+enddate AS tickdate, -1* coins
, -1 AS cover
FROM coinsonperiod
)
, sumchanges AS (
SELECT tickdate, SUM(coins) AS change, SUM(cover) AS cover
FROM changes
GROUP BY tickdate
)
, aggregated AS (
SELECT
tickdate AS startdate
, lead(tickdate) over www AS enddate
, sum(change) OVER www AS cash
-- number of covered intervals
, sum(cover) OVER www AS cover
FROM sumchanges
WINDOW www AS (ORDER BY tickdate)
)
-- substract one day from enddate to correct back to closed intervals
SELECT startdate, enddate-1 AS enddate, cash, cover
FROM aggregated
WHERE cover > 0
ORDER BY startdate
;