Comments 13
select gs - interval '1 day'
from generate_series('2018-02-01', '2018-06-01', interval '1 month') as gs;
Или, для удобства построения параметров,
select gs + interval '1 month -1 day'
from generate_series('2018-01-01', '2018-12-01', interval '1 month') as gs;
Да, после первого абзаца статьи мне тоже пришла такая же мысль
Ваше решение хорошее и лаконичное, а голова в его сторону не думала, потому что задача изначально ставилась группировкой не на конец месяца, а по определенным числам. Например, если займ выдан 20 числа, то итоги по платежам подбивать на 20-е числа. Если выдан 31-го, то итоги подбивать на 31-е числа. И вот тут-то обнаружилась проблема.
При написании статьи я описывал уже вырожденный случай задачи, хотя она изначально стоит шире.
Под универсальным механизмом я подразумевал генерацию timestamp с любым шагом, хоть по часам, хоть по 1 месяц 2 дня. Именно так работает итоговый запрос.
Что касается вашего алгоритма генерации последних дней, то это тот же трюк — как задачу последних дней месяцев, свести к первым дням следующих месяцев и шажком назад. Неважно, в конце мы секунду или день отнимем, главное через границу перейти.
Если пользоваться именно таким подходом для частной задачи, то думаю, все же удобнее воспользоваться generate_series, установив в параметры первые числа месяцев, как привели в примере выше. Вы предлагаете по честному с with recursive решить ту же задачу с тем же трюком, но руками сгенерировать первые числа, вместо вызова одной функции — это длиннее и неочевиднее.
Если же вы имели ввиду, что ваш метод работает для любых месячных итераций дат, а не только последних дней, то он не сработает, к примеру, для генерации 28 чисел невисокосного года.
select ('2018-01-28 0:00:00'::timestamp + interval '1 day')::date + interval '1 mon' - interval '1 sec'
27.02.2018 23:59:59
После первых комментариев, я внес описание общей задачи в текст, чтобы по возможности остальные читатели в верном контексте восприняли текст.
with recursive params as (
select date('2018-01-31') date1, date('2018-05-31') date2, "interval"('1 month')
),
cycle as (
select date1 result, 1 step from params
union all
select date, step + 1 from params, cycle, date(date1 + interval * step)
where date <= date2
)
select result from cycle
Я недолюбливаю и крайне редко использую декартово произведение, обычно все задачи решаются через join где сразу и однозначно прописываются условия соединения, а не «где-то там». Запросы с join лучше выдерживают масштабирование и адаптацию.
Но это я про общий случай — в данном запросе вы ловко избавились от мусора.
select date, step + 1
from params
cross join cycle
join date(date1 + interval * step) on date <= date2
Генерация последовательности дат и generate_series в PostgreSQL