Trong một số trường hợp, tôi đã làm điều gì đó tương tự. Về cơ bản, phân nhóm dựa trên sự phân tách trong một trật tự phức tạp. Những điều cơ bản về cách tiếp cận mà tôi sử dụng liên quan đến vấn đề này như sau:
- Xây dựng một bảng về mọi phạm vi thời gian được quan tâm.
- Tìm thời gian bắt đầu cho từng nhóm phạm vi thời gian được quan tâm.
- Tìm thời gian kết thúc cho từng nhóm phạm vi thời gian được quan tâm.
- Kết hợp thời gian bắt đầu và kết thúc vào danh sách phạm vi thời gian và nhóm.
Hoặc, chi tiết hơn:(mỗi bước trong số này có thể là một phần của một CTE lớn, nhưng tôi đã chia nhỏ nó thành các bảng tạm thời để dễ đọc ...)
Bước 1:Tìm danh sách tất cả các phạm vi thời gian được quan tâm (tôi đã sử dụng một phương pháp tương tự như phương pháp được liên kết bởi @Brad). LƯU Ý:như @Manfred Sorg đã chỉ ra, điều này giả định rằng không có "giây bị thiếu" nào trong dữ liệu của xe buýt. Nếu có dấu ngắt trong dấu thời gian, mã này sẽ diễn giải phạm vi đơn lẻ là hai (hoặc nhiều) phạm vi riêng biệt.
;with stopSeconds as (
select BusID, BusStopID, TimeStamp,
[date] = cast(datediff(dd,0,TimeStamp) as datetime),
[grp] = dateadd(ss, -row_number() over(partition by BusID order by TimeStamp), TimeStamp)
from #test
where BusStopID is not null
)
select BusID, BusStopID, date,
[sTime] = dateadd(ss,datediff(ss,date,min(TimeStamp)), 0),
[eTime] = dateadd(ss,datediff(ss,date,max(TimeStamp)), 0),
[secondsOfStop] = datediff(ss, min(TimeStamp), max(Timestamp)),
[sOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,min(TimeStamp))),
[eOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,max(TimeStamp)))
into #ranges
from stopSeconds
group by BusID, BusStopID, date, grp
Bước 2:Tìm thời gian sớm nhất cho mỗi điểm dừng
select this.BusID, this.BusStopID, this.sTime minSTime,
[stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.sTime)
into #starts
from #ranges this
left join #ranges prev on this.BusID = prev.BusID
and this.BusStopID = prev.BusStopID
and this.sOrd = prev.sOrd+1
and this.sTime between dateadd(mi,-10,prev.sTime) and dateadd(mi,10,prev.sTime)
where prev.BusID is null
Bước 3:Tìm thời gian muộn nhất cho mỗi điểm dừng
select this.BusID, this.BusStopID, this.eTime maxETime,
[stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.eTime)
into #ends
from #ranges this
left join #ranges next on this.BusID = next.BusID
and this.BusStopID = next.BusStopID
and this.eOrd = next.eOrd-1
and this.eTime between dateadd(mi,-10,next.eTime) and dateadd(mi,10,next.eTime)
where next.BusID is null
Bước 4:Kết hợp mọi thứ lại với nhau
select r.BusID, r.BusStopID,
[avgLengthOfStop] = avg(datediff(ss,r.sTime,r.eTime)),
[earliestStop] = min(r.sTime),
[latestDepart] = max(r.eTime)
from #starts s
join #ends e on s.BusID=e.BusID
and s.BusStopID=e.BusStopID
and s.stopOrder=e.stopOrder
join #ranges r on r.BusID=s.BusID
and r.BusStopID=s.BusStopID
and r.sTime between s.minSTime and e.maxETime
and r.eTime between s.minSTime and e.maxETime
group by r.BusID, r.BusStopID, s.stopOrder
having count(distinct r.date) > 1 --filters out the "noise"
Cuối cùng, để hoàn thiện, hãy dọn dẹp:
drop table #ends
drop table #starts
drop table #ranges