完全な書き直し:
;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
プレ>要求された結果を生成し、実際に 分離サブグループと重複を含む、考えられるすべてのケースをカバーしています。 data.SE での動作デモ .
- <リ>
CTE 1
重複する間隔の新しいグループが始まる (一意の!) 時点を見つけます。
CTE 2
新しいグループの開始を個々の間隔ごとに (それを含めて) カウントし、ユーザーごとに一意のグループ番号を形成します。
最終選択
グループをマージし、グループの開始を早め、終了を遅らせます。
T-SQL ウィンドウ関数 max()
のため、いくつかの困難に直面しました または sum()
ORDER BY
を受け入れない ウィンドウ内の句。パーティションごとに 1 つの値しか計算できないため、パーティションごとに現在の合計/カウントを計算することはできません。 PostgreSQL または Oracle で動作します (もちろん、MySQL では動作しません。ウィンドウ関数も CTE もありません)。
最終的なソリューションでは、CTE を 1 つ余分に使用しますが、同じくらい高速になるはずです。