Я лишь взял Ваше решение по вашей же ссылке. Почему у вас там оказался неверный набор выходных и праздников — спросите у себя.
Так что тут неизвестно кто первый не озаботился. :)
Ваше решение мне нравится лаконичностью, понятностью и чистотой.
Решение от OrmEugensson более мудреное и сложное, но и гораздо более производительное, чем наши с вами.
С чего вы взяли что ваша задача (подсчет общей суммы массива), более актуальнее моей (подсчет для конкретной заявки)?
Мне такие кейсы встречаются чаще. Как-то даже не пришло в голову иначе протестировать.
Интереса ради я написал запрос, который работает быстрее ваших ещё на (десятичный) порядок, избавившись от суммы по интервалам, и получив скорость работы линейно зависящую только от количества интервалов. Видимо придётся добавить в разбор задачи слова про производительность.
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)
),
holidays(date_key) as ( values
('2018-01-01'::date), ('2018-01-02'::date), ('2018-01-03'::date),
('2018-01-04'::date), ('2018-01-05'::date), ('2018-01-08'::date),
('2018-02-23'::date), ('2018-03-08'::date), ('2018-03-09'::date),
('2018-04-30'::date), ('2018-05-01'::date), ('2018-05-02'::date),
('2018-05-09'::date), ('2018-06-11'::date), ('2018-06-12'::date),
('2018-11-05'::date), ('2018-12-31'::date),
('2019-01-01'::date), ('2019-01-02'::date), ('2019-01-03'::date),
('2019-01-04'::date), ('2019-01-07'::date), ('2019-01-08'::date),
('2019-03-08'::date), ('2019-05-01'::date), ('2019-05-02'::date),
('2019-05-03'::date), ('2019-05-09'::date), ('2019-05-10'::date),
('2019-06-12'::date), ('2019-11-04'::date)
),
work_holidays(date_key) as ( values
('2018-04-28'::date), ('2018-06-09'::date), ('2018-12-29'::date)
)
select p.id, p.start_time, p.stop_time,
-- hours from start_time
case
when start_time::date = min(h.time_key) then '00:00:00'::interval
when start_time::date = stop_time::date then '00:00:00'::interval
when start_time::time > '19:00:00'::time then '00:00:00'::interval
when start_time::time < '10:00:00'::time then '09:00:00'::interval
else '19:00:00'::time - start_time::time
end +
-- hours from stop_time
case
when stop_time::date = max(h.time_key) then '00:00:00'::interval
when stop_time::date = start_time::date then least('19:00:00'::time, stop_time::time) - greatest('10:00:00'::time, start_time::time)
when stop_time::time > '19:00:00'::time then '09:00:00'::interval
when stop_time::time < '10:00:00'::time then '00:00:00'::interval
else stop_time::time - '10:00:00'::time
end +
--
(
-- count all days
greatest(p.stop_time::date - p.start_time::date - 1, 0)
-- count holidays
-sum(case when h.time_key > p.start_time::date and h.time_key < p.stop_time::date then 1 else 0 end)
)*'09:00:00'::interval as answer
from periods p
left outer join
(
select time_key::date
from generate_series(
(select min(start_time)::date from periods),
(select max(stop_time)::date from periods),
interval '1 day'
) as time_key
where to_char(time_key, 'D') in ('1', '7')
union
select date_key
from holidays
except
select date_key
from work_holidays
) h
on h.time_key between p.start_time::date and p.stop_time::date
group by p.id, p.start_time, p.stop_time
order by p.id
Визуальное решение, так сказать, без формул.
Можно обратить внимание, что стартовое положение спортсменов во втором забеге соответствует финишному положению первого забега.
Т.е. второй забег — это зеркальное отражение первого.
Следовательно, если отмотать «пленку», спортсмены во втором забеге финишируют одновременно.
Так что тут неизвестно кто первый не озаботился. :)
Ваше решение мне нравится лаконичностью, понятностью и чистотой.
Решение от OrmEugensson более мудреное и сложное, но и гораздо более производительное, чем наши с вами.
Мне такие кейсы встречаются чаще. Как-то даже не пришло в голову иначе протестировать.
Вот на это интересно посмотреть, буду ждать.
Тут можно сравнить более объективно:
Ваше решение
Мое
И у Вас ошибка где-то, не сходится сумма…
Но не по производительности.
Его решение работает в 10 раз медленнее, чем, например, мое.
Без претензий на лавры, PostgreSQL вообще не мой родной.
Можно обратить внимание, что стартовое положение спортсменов во втором забеге соответствует финишному положению первого забега.
Т.е. второй забег — это зеркальное отражение первого.
Следовательно, если отмотать «пленку», спортсмены во втором забеге финишируют одновременно.
Слить две очереди в одну.
Отсортировать с учетом требований.