Как стать автором
Обновить
1
0
Дмитрий Рябов @eranthis

Software developer, DBA, System architect

Отправить сообщение
1/200 «с потолка». просто на примере США. по логике модели у них через пару недель должны закончиться резервы популяции. по кривой заражения это явно не так.
Код работает с Российского IP, но спотыкается на привязке карты — не принимает цифровую VISA. Грусть-печаль…
Теперь, после этой публикации на хабре, нужно просто немного подождать. )
Представьте, что у вас первая зарплата в компании 15 февраля. Так же рассуждать будете? ;) Сдвиг на месяц не предполагает привяки к половинам и каким-либо другим частям месяца — это всегда либо то же число, что и в прошлом месяце, либо максимальное дата месяца, если число в месяце отсутствует.
Почему нельзя определить месяц? Это единичный интервал, для которого год и столетие кратные. Выразить эти интервалы в днях нельзя, но это не мешает их определению.
Начнём с того, что не всегда. Второй момент, мы оперируем с разными величинами — датами и интервалами. Последние, в довершение всего, ещё и могут быть выражены в величинах, однозначно не приводимых друг к другу. А как называть/обозначать эту операцию — дело десятое.
В операциях с месяцами основная ошибка — это попытка «взвесить» месяц в днях, что по определению невозможно, да и в корне неверно. Чтобы избежать неоднозначности, этого делать не нужно. В этом плане, очень правильно эта математика реализована, например, в PostgreSQL. Чтобы понять логику прибавления месяцев, проще всего взять пример зарплаты. Если заплата выплачивается каждый месяц, то месяц не может быть пропущен по определению. Иными словами, при добавлении месяца к любой дате в январе должна быть дата в феврале, но никак не в марте. Отсюда правда вытекают такие порой неочевидные моменты, как например:
28/29/30/31 января + 1 месяц = 28 февраля
(дата + N месяцев) - N месяцев не всегда равно дата
(дата + 1 месяц) + 1 месяц не всегда равно дата + 2 месяца
и т.п.
Это неявный lateral cross join, ввиду того что функция приведения типа напрямую не применима на функцию, возвращающую множество. На самом деле, можно было обойтись без этого, заменив
эту часть запроса
from generate_series(start_time::date, stop_time::date, '1 day')_, "timestamp"(_) period_day,
на такой вариант
from generate_series(date_trunc('day', start_time), date_trunc('day', stop_time), '1 day') period_day,
или такой вариант
from generate_series(start_time::date::timestamp, stop_time::date::timestamp, '1 day') period_day,
чтобы функция generate_series сразу возвращала timestamp. Это бы ещё и облегчило запрос. Но… хорошая мысля приходит опосля. :)
тут в идеале бы ещё знать, как представлен календарь праздников и рабочих уикендов, так как параметрически их вряд ли передают в запрос. задача была бы более приближена к реальной, а так решение выглядит несколько искусственно.
прошу прощения, в первом ответе не закрепил под спойлер.
либо то же самое приклеить к периодам контекстно
with periods (id, start_time, stop_time) as (
    values(1, '2019-03-29 07:00:00'::timestamp, '2019-04-08 14:00:00'::timestamp), 
          (2, '2019-04-10 07:00:00'::timestamp, '2019-04-10 20:00:00'::timestamp), 
          (3, '2019-04-11 12:00:00'::timestamp, '2019-04-12 16:07:12'::timestamp),
          (4, '2018-12-28 12:00:00'::timestamp, '2019-01-16 16:00:00'::timestamp)
)
select *, (
    select sum(upper(hours) - lower(hours)) hours
    from generate_series(start_time::date, stop_time::date, '1 day')_, "timestamp"(_) period_day,
    tsrange(period_day + '10:00', period_day + '19:00') working_time
    join tsrange(start_time, stop_time) period_range on period_range && working_time,
    tsrange(period_range * working_time) hours
    where 
    	extract(isodow from period_day) < 6
        and period_day <> all('{2019-01-01,2019-01-02,2019-01-03,2019-01-04,2019-01-07,2019-01-08,2019-03-08,2019-05-01,2019-05-02,2019-05-03,2019-05-09,2019-05-10,2019-06-12,2019-11-04,2018-01-01,2018-01-02,2018-01-03,2018-01-04,2018-01-05,2018-01-08,2018-02-23,2018-03-08,2018-03-09,2018-04-30,2018-05-01,2018-05-02,2018-05-09,2018-06-11,2018-06-12,2018-11-05,2018-12-31}')
        or period_day = any('{2018-04-28,2018-06-09,2018-12-29}')
) from periods

with periods (id, start_time, stop_time) as (
    values(1, '2019-03-29 07:00:00'::timestamp, '2019-04-08 14:00:00'::timestamp), 
          (2, '2019-04-10 07:00:00'::timestamp, '2019-04-10 20:00:00'::timestamp), 
          (3, '2019-04-11 12:00:00'::timestamp, '2019-04-12 16:07:12'::timestamp),
          (4, '2018-12-28 12:00:00'::timestamp, '2019-01-16 16:00:00'::timestamp)
)
select periods.*, hours from (
    select id, sum(upper(hours) - lower(hours)) hours
    from (
        select working_day::timestamp from periods
        join generate_series(start_time::date, stop_time::date, '1 day') working_day on extract(isodow from working_day::timestamp) < 6
        except -- holidays
        select * from unnest('{2019-01-01,2019-01-02,2019-01-03,2019-01-04,2019-01-07,2019-01-08,2019-03-08,2019-05-01,2019-05-02,2019-05-03,2019-05-09,2019-05-10,2019-06-12,2019-11-04,2018-01-01,2018-01-02,2018-01-03,2018-01-04,2018-01-05,2018-01-08,2018-02-23,2018-03-08,2018-03-09,2018-04-30,2018-05-01,2018-05-02,2018-05-09,2018-06-11,2018-06-12,2018-11-05,2018-12-31}'::date[])
        union -- working weekends
        select * from unnest('{2018-04-28,2018-06-09,2018-12-29}'::date[])
    )_, tsrange(working_day + '10:00', working_day + '19:00') working_time
    join (periods cross join tsrange(start_time, stop_time) period_range) on period_range && working_time,
    tsrange(period_range * working_time) hours
    group by 1
)_ join periods using (id)
Это лишь вопрос синтаксиса запроса, по использованию join'ов я с Вами согласен. Join тут не был использован исключительно из соображений компактности. Для конкретно этого случая второй запрос можно заменить на эквивалентный вариант с использованием явных join'ов:
select date, step + 1
from params
cross join cycle
join date(date1 + interval * step) on date <= date2
Другого варианта на чистом sql, кроме как рекурсивно прокрутить цикл, сходу в голову не пришло, но Ваше решение можно сделать несколько более изящным:
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
хм… Lenovo P2? тот же 625, 5100 мАч, 4гб ОЗУ. или же Asus Zenfone Zoom на подходе — тоже 5к мАч с аналогичными характеристиками.
Можно вспомнить относительно недавнюю комету Хейла-Боппа. Подобные кометы — явление, конечно, достаточно редкое, но не в рамках периодов порядка столетий. А Больших комет за столетие может быть и больше десятка.
Там вопрос о расширении группы томов. К тому же ответ приведён неточный. Физический том можно не создавать — он будет создан неявно vgextend, если его нет.

Информация

В рейтинге
Не участвует
Откуда
Varna, Varna, Болгария
Дата рождения
Зарегистрирован
Активность