Мне нужно получить список диапазонов дат, которые НЕ перекрываются друг с другом, из списка перекрывающихся дат и получить сумму монет за это перекрытие.Я попробовал поискать в Google в качестве примера, но пока не повезло.Я не могу использовать правильные ключевые слова?
У меня есть список перекрывающихся дат
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 не соответствуют моему желаемому результату.Оба ответа показывают неправильную конечную дату.
Когда один период заканчивается, следующий должен начаться на следующий день (или позже, если есть разрыв).В мой желаемый результат 1.1.2018-6.1.2018 входит 6-й день.Между 6-м и 7-м нет разрыва, потому что 7-е включено в 7.1.2018-10.1.2018.
ОБНОВЛЕНИЕ 2: Теперь я понял, в чем разница между открытым, полуоткрытым и закрытым интервалами.В решении joops вычисление должно выполняться с полуоткрытыми интервалами, но желаемый результат - закрытый интервал.Вот почему конечная дата должна быть уменьшена, чтобы сделать результат закрытым интервалом.Поправьте меня, если я ошибаюсь.
Я также добавил 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
;
Результат: