Я бы подошел к этому, используя lag()
, совокупный sum()
и агрегацию. Вот пошаговое объяснение.
Сначала вы можете использовать lag()
, чтобы восстановить предыдущую дату начала и окончания для того же идентификатора:
select
t.*,
lag(startdate) over(partition by id order by startdate) lagstartdate,
lag(enddate) over(partition by id order by startdate) lagenddate
from mytable t
ID | STARTDATE | ENDDATE | HOURS | LAGSTARTDATE | LAGENDDATE
:--- | :------------------ | :------------------ | ----: | :------------------ | :------------------
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 | 8.17 | <em>null</em> | <em>null</em>
a124 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 | 4 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00
bc24 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 | 4 | <em>null</em> | <em>null</em>
bc24 | 2019-10-10 10:30:00 | 2019-10-10 15:30:00 | 5 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 | 2 | <em>null</em> | <em>null</em>
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 | 1 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00
Затем вы можете настроить совокупный sum
для разрезания записей, имеющих одинаковые id
внутри групп (которые впоследствии будут агрегированы). Когда даты не перекрываются, начинается новая группа:
select
t.*,
sum(
case when startdate <= lagenddate or enddate <= lagstartdate
then 0
else 1
end
) over(partition by id order by startdate) grp
from (
select
t.*,
lag(startdate) over(partition by id order by startdate) lagstartdate,
lag(enddate) over(partition by id order by startdate) lagenddate
from mytable t
) t
ID | STARTDATE | ENDDATE | HOURS | LAGSTARTDATE | LAGENDDATE | GRP
:--- | :------------------ | :------------------ | ----: | :------------------ | :------------------ | --:
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 | 8.17 | <em>null</em> | <em>null</em> | 1
a124 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 | 4 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 | 1
bc24 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 | 4 | <em>null</em> | <em>null</em> | 1
bc24 | 2019-10-10 10:30:00 | 2019-10-10 15:30:00 | 5 | 2019-10-10 07:30:00 | 2019-10-10 11:30:00 | 1
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 | 2 | <em>null</em> | <em>null</em> | 1
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 | 1 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 | 2
Наконец, вы можете сгруппировать записи по id
и grp
: min()
и max()
даст вам диапазон дат, затем вы сможете вычислить разницу дат.
Окончательный запрос:
select
id,
min(startdate) startdate,
max(enddate) enddate,
round((max(enddate) - min(startdate)) * 24, 2) hours
from (
select
t.*,
sum(
case when startdate <= lagenddate or enddate <= lagstartdate
then 0
else 1
end
) over(partition by id order by startdate) grp
from (
select
t.*,
lag(startdate) over(partition by id order by startdate) lagstartdate,
lag(enddate) over(partition by id order by startdate) lagenddate
from mytable t
) t
) t
group by id, grp
order by id, grp
ID | STARTDATE | ENDDATE | HOURS
:--- | :------------------ | :------------------ | ----:
a124 | 2019-10-10 07:00:00 | 2019-10-10 15:10:00 | 8.17
bc24 | 2019-10-10 07:30:00 | 2019-10-10 15:30:00 | 8
er67 | 2019-10-10 09:30:00 | 2019-10-10 11:30:00 | 2
er67 | 2019-10-10 15:30:00 | 2019-10-10 16:30:00 | 1
Демонстрация на DB Fiddle