2

同じ値を持つ隣接するレコードの開始時刻と終了時刻を見つけるには?

心拍数の読み取り値 (1 分あたりの拍数) と日時フィールドを含むテーブルがあります。(実際には、フィールドはheartrate_idheartrate、およびdatetimeです。) データは、心拍数と時間を 6 秒ごとに記録するデバイスによって生成されます。心拍数モニターが誤った測定値を示し、記録された 1 分あたりの心拍数が一定期間「固着」することがあります。スティックとは、1 分あたりの拍数の値が隣接する時間で同じになることを意味します。

基本的に、心拍数が同じであるすべてのレコード (例: 毎分 5 ビート、毎分 100 ビートなど) を見つける必要がありますが、隣接するレコードのみです。デバイスが 3 回の連続読み取り (または 100 回の連続読み取り) で毎分 25 ビートを記録する場合、これらのイベントを特定する必要があります。結果には、心拍数、心拍数の開始時刻、心拍数の終了時刻が含まれている必要があり、理想的には、結果は次のようになります。

heartrate starttime endtime
--------- --------- --------
1.00      21:12:00  21:12:24
35.00     07:00:12  07:00:36

私はいくつかの異なるアプローチを試しましたが、今のところ私は打ちのめしています。どんな助けでも大歓迎です!

4

2 に答える 2

3

編集:

レビューすると、この回答に関する私の元の作業はどれも非常に優れていませんでした。として知られる問題のクラスに属しており、この改訂された回答は、この質問に最初に回答して以来、同様の質問から収集した/学んだ情報を使用します。

このクエリは、当初考えていたよりもはるかに簡単に実行できることがわかりました。

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. 元の列 (非集計列であるため)生成された列(スタック値ごとの現在の「実行」を識別する) の両方を使用する必要があります。heartRategroupingId

質問の一部では、3 回以上繰り返された実行のみが求められました。これHAVING COUNT(*) > 2は、長さが 2 以下のランを無視する命令です。グループごとに行をカウントします。

于 2011-07-08T20:43:16.287 に答える
0

隣接問題に適用される間隔パッキングに関する Ben-Gan の記事をお勧めします。

tsql-challenge-packing-date-and-time-intervals

解決策-パッキング-日時-間隔-パズル

于 2011-07-20T20:00:48.717 に答える