編集:
レビューすると、この回答に関する私の元の作業はどれも非常に優れていませんでした。これは実際にはgaps-and-islandsとして知られる問題のクラスに属しており、この改訂された回答は、この質問に最初に回答して以来、同様の質問から収集した/学んだ情報を使用します。
このクエリは、当初考えていたよりもはるかに簡単に実行できることがわかりました。
WITH Grouped_Run AS (SELECT heartRate, dateTime,
ROW_NUMBER() OVER(ORDER BY dateTime) -
ROW_NUMBER() OVER(PARTITION BY heartRate ORDER BY dateTime) AS groupingId
FROM HeartRate)
SELECT heartRate, MIN(dateTime), MAX(dateTime)
FROM Grouped_Run
GROUP BY heartRate, groupingId
HAVING COUNT(*) > 2
SQL Fiddle Demo
ここで何が起こっているのでしょうか?ギャップと島の問題の定義の 1 つは、連続した値の「グループ」が必要である (またはその欠如) です。多くの場合、これを解決するためにシーケンスが生成されます。これは、見過ごされがちな/直感的すぎる事実を利用して行われます: シーケンスを減算すると定数値が得られます。
たとえば、次のシーケンスと減算を想像してください (行の値は重要ではありません)。
position positionInGroup subtraction
=========================================
1 1 0
2 2 0
3 3 0
4 1 3
5 2 3
6 1 5
7 4 3
8 5 3
position
すべてのレコードに対して生成される単純なシーケンスです。異なるレコード
positionInGroup
のセットごとに生成される単純なシーケンスです。この場合、実際には 3 つの異なるレコード セットがあります ( から始まります)。他の 2 つの列の違いの結果です。グループごとに値が繰り返される場合があることに注意してください。シーケンスが共有しなけれ
ばならない重要なプロパティの 1 つは、データの行に対して同じ順序で生成する必要があることです。そうしないと、シーケンスが壊れます。 position = 1, 4, 6
subtraction
では、SQL はこれをどのように行っているのでしょうか。この関数を使用するとROW_NUMBER()
、レコードの「ウィンドウ」に対して一連の数字が生成されます。
ROW_NUMBER() OVER(ORDER BY dateTime)
シーケンスを生成しposition
ます。
ROW_NUMBER() OVER(PARTITION BY heartRate ORDER BY dateTime)
それぞれが異なるグループであるpositionInGroup
シーケンスを生成します。
このタイプのほとんどのクエリの場合、2 つのシーケンスの値は重要ではありません。重要なのは (シーケンス グループを取得するための) 減算であるため、減算の結果だけが必要です。また、答えを提供するために、それらが発生した時間
も必要です。heartRate
heartRate
元の回答では、スタックしたハートビートの「実行」のそれぞれの開始時刻と終了時刻を尋ねました。これは標準のMIN(...)
/です。MAX(...)
つまり、GROUP BY
. 元の列 (非集計列であるため)と生成された列(スタック値ごとの現在の「実行」を識別する) の両方を使用する必要があります。heartRate
groupingId
質問の一部では、3 回以上繰り返された実行のみが求められました。これHAVING COUNT(*) > 2
は、長さが 2 以下のランを無視する命令です。グループごとに行をカウントします。