Това е проблем с пропуски и острови, но островите се дефинират от REQ
транзакцията я правят малко по-сложна от някои.
Можете да използвате вложени водещи и изоставащи функции и някои манипулации, за да получите това, от което се нуждаете:
select distinct item,
coalesce(start_tran,
lag(start_tran) over (partition by item order by timestamp)) as start_tran,
coalesce(end_tran,
lead(end_tran) over (partition by item order by timestamp)) as end_tran,
coalesce(end_time,
lead(end_time) over (partition by item order by timestamp))
- coalesce(start_time,
lag(start_time) over (partition by item order by timestamp)) as time
from (
select item, timestamp, start_tran, start_time, end_tran, end_time
from (
select item,
timestamp,
case when lag_tran is null or transaction like 'REQ%'
then transaction end as start_tran,
case when lag_tran is null or transaction like 'REQ%'
then timestamp end as start_time,
case when lead_tran is null or lead_tran like 'REQ%'
then transaction end as end_tran,
case when lead_tran is null or lead_tran like 'REQ%'
then timestamp end as end_time
from (
select item, transaction, timestamp,
lag(transaction)
over (partition by item order by timestamp) as lag_tran,
lead(transaction)
over (partition by item order by timestamp) as lead_tran
from transactions
)
)
where start_tran is not null or end_tran is not null
)
order by item, start_tran;
С допълнителни записи за втори цикъл за елементи 1 и 2, които биха могли да дадат:
ITEM START_TRAN END_TRAN TIME
---------- ---------- ---------- -----------
1 REQ-A PICKUP 0 1:53:30.0
1 REQ-E PICKUP 0 1:23:30.0
2 REQ-B MAIL 0 0:24:13.0
2 REQ-F REQ-F 0 0:0:0.0
3 REQ-C PICKUP 0 1:46:30.0
4 REQ-D PULL 0 0:23:59.0
5 REQ-A PICKUP 0 1:43:59.0
SQL Fiddle показва всички междинни стъпки.
Не е толкова страшно, колкото може да изглежда на пръв поглед. Най-вътрешната заявка взема необработените данни и добавя допълнителна колона за водещи и закъснели транзакции. Вземайки само първия набор от записи на елемент-1, който ще бъде:
ITEM TRANSACTION TIMESTAMP LAG_TRAN LEAD_TRAN
---------- ----------- ------------------------ ---------- ----------
1 REQ-A 2014-07-31T09:51:32Z PULL
1 PULL 2014-07-31T10:22:21Z REQ-A TRANSFER
1 TRANSFER 2014-07-31T10:22:23Z PULL ARRIVE
1 ARRIVE 2014-07-31T11:45:01Z TRANSFER PICKUP
1 PICKUP 2014-07-31T11:45:02Z ARRIVE REQ-E
Забележете REQ-E
появява се като последен lead_tran
? Това е първата transaction
за втория цикъл на записи за този елемент и ще бъде полезен по-късно. Следващото ниво на заявка използва тези изпреварващи и изоставащи стойности и третира REQ
стойности като начални и крайни маркери и използва тази информация, за да нулира всичко освен първия и последния запис за всеки цикъл.
ITEM TIMESTAMP START_TRAN START_TIME END_TRAN END_TIME
---------- ------------------------ ---------- ------------------------ ---------- ------------------------
1 2014-07-31T09:51:32Z REQ-A 2014-07-31T09:51:32Z
1 2014-07-31T10:22:21Z
1 2014-07-31T10:22:23Z
1 2014-07-31T11:45:01Z
1 2014-07-31T11:45:02Z PICKUP 2014-07-31T11:45:02Z
Следващото ниво на заявка премахва всички редове, които не представляват началото или края (или и двете - вижте REQ-F
във Fiddle), тъй като не се интересуваме от тях:
ITEM TIMESTAMP START_TRAN START_TIME END_TRAN END_TIME
---------- ------------------------ ---------- ------------------------ ---------- ------------------------
1 2014-07-31T09:51:32Z REQ-A 2014-07-31T09:51:32Z
1 2014-07-31T11:45:02Z PICKUP 2014-07-31T11:45:02Z
Вече имаме двойки редове за всеки цикъл (или един ред за REQ-F
). Последното ниво отново използва преднина и забавяне, за да запълни празните места; ако start_tran
е null, тогава това е краен ред и трябва да използваме началните данни на предишния ред; ако end_tran
е null, тогава това е начален ред и трябва да използваме крайните данни на следващия ред.
ITEM START_TRAN START_TIME END_TRAN END_TIME TIME
1 REQ-A 2014-07-31T09:51:32Z PICKUP 2014-07-31T11:45:02Z 0 1:53:30.0
1 REQ-A 2014-07-31T09:51:32Z PICKUP 2014-07-31T11:45:02Z 0 1:53:30.0
Това прави двата реда еднакви, така че distinct
премахва дубликатите.