あなたがしたいのは、データ内のシーケンスを見つけることです。通常、これは一連の日です。あなたはおそらく半日であるこれらのスロットを持っているので、あなたのケースはより難しいです。
このソリューションは、スロットを時間に変換することで機能します。朝は午前6時、夕方は午後6時を恣意的に使っています。cross join
これは、「All Days」の予定に対して新しい行が作成されるため、を使用して行います。
このデータ構造を使用すると、SQLトリックを使用して、一緒に属するスロットを識別できます。アイデアは、日時ごとに各エンジニアのスロットを列挙することです。次に、半日を引くと、列挙された値が日時から結び付けられます。これは、値が正しい場合は定数です。ギャップがあると、新しい値が表示されます。
最後に、engineer_idとgroup idでグループ化して、必要なデータを取得します。
次のクエリは、これが実際に動作していることを示しています。最終結果をスロットに戻すのではなく、日時として残します。
with t as (
select 1 as engineer_id, cast('2012-10-20' as date) as visit_date, 'All Day' as visit_slot union all
select 1 as engineer_id, cast('2012-10-21' as date) as visit_date, 'AM' as visit_slot union all
select 2 as engineer_id, cast('2012-10-20' as date) as visit_date, 'All Day' as visit_slot union all
select 2 as engineer_id, cast('2012-10-21' as date) as visit_date, 'All Day' as visit_slot union all
select 2 as engineer_id, cast('2012-10-22' as date) as visit_date, 'All Day' as visit_slot union all
select 3 as engineer_id, cast('2012-10-20' as date) as visit_date, 'PM' as visit_slot union all
select 3 as engineer_id, cast('2012-10-21' as date) as visit_date, 'All Day' as visit_slot union all
select 3 as engineer_id, cast('2012-10-22' as date) as visit_date, 'PM' as visit_slot
),
tslots as (
select t2.*
from (select t.engineer_id,
(CAST(visit_date as datetime) +
(case when t.visit_slot in ('All Day', const.slot) then const.hr/24.0 end)
) as visit_datetime
from t cross join
(select 'AM' as slot, 6 as hr union all select 'PM', 18) const
) t2
where visit_datetime is not null
group by engineer_id, visit_datetime
)
select engineer_id, MIN(visit_datetime) as min_datetime, MAX(visit_datetime) as max_datetime
from (select ts.*,
ROW_NUMBER() over (partition by engineer_id order by visit_datetime) as seqnum,
visit_datetime - 0.5*(ROW_NUMBER() over (partition by engineer_id order by visit_datetime)) as groupid
from tslots ts
) ts1
group by engineer_id, groupid
order by 1, 2