识别有一次非超额预订的重叠预约

问题描述 投票:0回答:1

场景:

预约有开始和结束时间。 预约可以重叠,但出于报告目的,我们需要知道何时存在重叠预约(称为超额预订)以及何时不存在。 两个或多个预约可能会相互重叠,但总有一个预约涉及超额预订,但未归类为超额预订。

规则:

1.预约的重叠部分(片段)被视为超额预订,但其中一次重叠的情况除外,该情况不被视为超额预订。

2.被选择不被视为超额预订的重叠的发生并不重要;它可以是集合中的任何重叠部分。

样本数据:

应用程序ID 预约开始 预约结束
1 2:00 5:00
2 3:00 6:00
3 2:00 6:00
4 6:00 7:00

期望的结果:

应用程序ID 分类 分段开始 段结束
1 没有超额预订 2:00 5:00
2 超额预订 3:00 5:00
2 没有超额预订 5:00 6:00
3 超额预订 2:00 6:00
4 没有超额预订 6:00 7:00

...或者...

应用程序ID 分类 分段开始 段结束
1 没有超额预订 2:00 5:00
2 超额预订 3:00 6:00
3 超额预订 2:00 5:00
3 没有超额预订 5:00 6:00
4 没有超额预订 6:00 7:00

等等

我尝试过的:

DROP TABLE IF EXISTS #appts
CREATE TABLE #appts 
(
    appt_id INT 
    ,st TIME
    ,et TIME
)

INSERT INTO #appts (appt_id,st,et) VALUES (1,'2:00','5:00')
INSERT INTO #appts (appt_id,st,et) VALUES (2,'3:00','6:00')
INSERT INTO #appts (appt_id,st,et) VALUES (3,'2:00','6:00')
INSERT INTO #appts (appt_id,st,et) VALUES (4,'6:00','7:00')

;With a1 AS
(
    SELECT * FROM #appts
)
,a2 AS 
(
    SELECT * FROM a1
)

SELECT DISTINCT
    a1.appt_id
    ,a2.appt_id
    ,ost = CASE WHEN a2.st > a1.st THEN a2.st ELSE a2.st END
    ,oet = CASE WHEN a2.et < a1.et THEN a2.et ELSE a1.et END
FROM 
    a1
    INNER JOIN a2 
        ON (a1.st > a2.st
        AND a1.st < a2.et
        OR a1.et > a2.st
        AND a1.et < a2.et)
WHERE
    a1.appt_id < a2.appt_id
appt_id appt_id
1 2 3:00 5:00
1 3 2:00 5:00
2 3 2:00 6:00

这并没有给我带来可接受的结果集之一。 它似乎为我提供了可能的超额预订,但我需要它也为我提供“未超额预订”,允许每个重叠中的超额预订之一算作未超额预订。

sql common-table-expression recursive-query overlap gaps-and-islands
1个回答
0
投票

对于 MYSQL 和 comp.:

with Appointments(Appt_ID, Appt_Start, Appt_End) as (
    select 1, cast('2:00' as time), cast('5:00' as time) union all
    select 2, cast('3:00' as time), cast('6:00' as time) union all
    select 3, cast('2:00' as time), cast('6:00' as time) union all
    select 4, cast('6:00' as time), cast('7:00' as time)
)
, slices(Appt_Start, Appt_End) as (
    select Appt_Start, Appt_End from (
        select dt as Appt_Start, lead(dt) over(order by dt, typ) as Appt_End
        from (
            select Appt_Start as dt, 1 as typ from Appointments
            union all 
            select Appt_End, -1 from Appointments
        ) d
    ) d
    where Appt_Start < Appt_End
)
select Appt_ID, classification, min(segment_Start) as segment_Start, max(segment_end) as segment_end
from (
    select Appt_ID,
        case 
            when rn_by_slice = 1 then 'NOT OVERBOOKED' 
            else 'OVERBOOKED' end as classification,
        segment_Start, segment_end
    from (
        select d.Appt_ID, s.Appt_Start as segment_Start, s.Appt_End as segment_End,
             d.Appt_Start, d.Appt_End,
            row_number() over(partition by s.Appt_Start, s.Appt_End order by d.Appt_ID) as rn_by_slice 
        from slices s
        join Appointments d
            on least(d.Appt_End - INTERVAL '1' SECOND, s.Appt_End - INTERVAL '1' SECOND) >= greatest(d.Appt_Start, s.Appt_Start)
    ) d
) d     
group by Appt_ID, classification
order by Appt_ID, segment_Start, segment_End
;

https://dbfiddle.uk/xH2WhG49

© www.soinside.com 2019 - 2024. All rights reserved.