Penulisan Ulang Lengkap:
;WITH new_grp AS (
SELECT r1.UserId, r1.StartTime
FROM @requests r1
WHERE NOT EXISTS (
SELECT *
FROM @requests r2
WHERE r1.UserId = r2.UserId
AND r2.StartTime < r1.StartTime
AND r2.EndTime >= r1.StartTime)
GROUP BY r1.UserId, r1.StartTime -- there can be > 1
),r AS (
SELECT r.RequestId, r.UserId, r.StartTime, r.EndTime
,count(*) AS grp -- guaranteed to be 1+
FROM @requests r
JOIN new_grp n ON n.UserId = r.UserId AND n.StartTime <= r.StartTime
GROUP BY r.RequestId, r.UserId, r.StartTime, r.EndTime
)
SELECT min(RequestId) AS RequestId
,UserId
,min(StartTime) AS StartTime
,max(EndTime) AS EndTime
FROM r
GROUP BY UserId, grp
ORDER BY UserId, grp
Sekarang menghasilkan hasil yang diminta dan benar-benar mencakup semua kemungkinan kasus, termasuk sub-grup dan duplikat yang terpisah. Lihat komentar pada data uji di demo yang berfungsi di data.SE .
-
CTE 1
Temukan titik (unik!) dalam waktu di mana grup baru interval yang tumpang tindih dimulai. -
CTE 2
Hitung awal grup baru hingga (dan termasuk) setiap interval individu, sehingga membentuk nomor grup unik per pengguna. -
PILIH Akhir
Gabungkan grup, ambil awal awal dan akhir terakhir untuk grup.
Saya menghadapi beberapa kesulitan, karena jendela T-SQL berfungsi max()
atau sum()
tidak menerima ORDER BY
klausa dalam a di jendela. Mereka hanya dapat menghitung satu nilai per partisi, yang membuatnya tidak mungkin untuk menghitung jumlah / hitungan yang berjalan per partisi. Akan bekerja di PostgreSQL atau Oracle (tetapi tidak di MySQL, tentu saja - ia tidak memiliki fungsi jendela atau CTE).
Solusi terakhir menggunakan satu CTE tambahan dan seharusnya sama cepatnya.