Это пример проблемы с промежутками и островками. Предполагая, что временные рамки в таблице не имеют пропусков, самым простым методом, вероятно, является разница номеров строк:
select name, department, min(start_date), max(end_date)
from (select t.*,
row_number() over (partition by name order by start_date) as seqnum,
row_number() over (partition by name, department order by start_date) as seqnum_nd
from t
) t
group by name, department, (seqnum - sequm_nd);
Как это работает, объяснить немного сложно. Однако, если вы посмотрите на результаты подзапроса, вы, вероятно, увидите, как разница в номерах строк определяет соседние записи с одним и тем же отделом.
Если у вас есть пробелы - и вы хотите воспользоваться этим на счет - тогда можно использовать более доработанный вариант. В этом методе используйте lag()
, чтобы получить предыдущую дату окончания, и используйте ее для создания флага, определяющего, когда начинаются «острова». Остальное - это просто агрегирование:
select name, department, min(start_date), max(end_date)
from (select t.*,
sum(case when prev_end_date >= start_date - interval '1 day' then 1 else 0 end) over (partition by name order by start_date) as grp
from (select t.*,
lag(end_date) over (partition by name, department order by start_date) as prev_end_date
from t
) t
) t
group by name, department, grp;
Обратите внимание, что здесь используются операторы даты, которые, как известно, зависят от базы данных. Точный синтаксис зависит от используемой базы данных, поэтому код может потребовать настройки.