web-dev-qa-db-ja.com

MySQLデータベース(timeseries)の2つのイベント間の期間の計算

MySqlに(timeseries like)テーブルがあり、いくつかのオン/オフイベントを格納しています。これらのイベントはランダムな間隔で報告され、IoTスイッチの現在の状態を表します。

enter image description here

SQLクエリを作成して、スイッチがオンになっていた時間(無制限)を計算する必要があります。

いくつかの障害があります。

  1. オン/オフイベントは、期間中に数回報告されます。つまり、重複があります。
  2. MySQLはバージョン5.5であり、アップグレードできません(つまり、ウィンドウ処理機能がありません)。このバージョンは、ビューでのサブクエリのネストをサポートしていません。

[〜#〜] update [〜#〜]私の目標は、特定の期間にスイッチがオンになっていた合計時間を計算することです。例:2018-07-01 00:00:00と2018-07-10 23:59:59の間、スイッチは56分間オンでした。私の考えは、次のような表に「オン」の期間間隔を含めることでした。

date-time           | duration-in-munites
2018-07-10 13:47:27 | 47 
2018-07-11 00:01:13 | 12
...

日時が最初のONが発生したとき

そこから、特定の期間の期間を合計できます。これは正しい方法ではないかもしれないので、私はそれを行う方法を提案します。

  • オフからオンへの移行時間と継続時間のみに興味があります

  • 初期値は不明です。スイッチは、「オン」イベントを報告する前に、いくつかの「オフ」イベントを報告した可能性があります

  • 別の問題は、テーブルに他のソースからのデータが含まれているため、IDが連続していないことです。

  • MySqlサーバーはRaspberry Pi 3で実行されます。私の知る限り、MySQL 8のARMバージョンはありません。

サンプルデータのCreate/Insertステートメントはここにあります

1
iPath ツ

アプローチ1(すべてのバージョンで機能)

  1. シーケンス番号をテーブル(または目的の列のコピー)に追加します。
  2. 「自己結合」_ON a.seq = b.seq-1_を実行して、隣接する行を一致させます。
  3. ステータスの変更まで絞り込みます:_WHERE a.Value != b.Value_

テーブルには、たとえば次のものが含まれます。

_  3  13:44:13  off   4  13:47:27   on
 12  14:27:27   on  13  14:34:48  off
_

thisテーブルの行のペア間のタイムスパンが必要であると仮定して、シーケンス番号を再度追加し、自己結合を実行して、最後から最後までを抽出および減算しますカラム。

アプローチ2(アップグレード用プラグ)

MariaDB 10.2またはMySQL 8.0を使用している場合は、この作業を非常に簡単にする「ウィンドウ関数」があります。 LAG()を参照してください。自己結合および順序付けの必要がなくなります。

最初のパスでは、_ORDER BY DateTime_を使用しながらLAG(Value) != Valueを探します。 2番目のパスでは、時間のLAGを現在の時間と比較します。

アプローチ(おそらくウィンドウなしの5.5に最適)

ほぼ旧式の5.5で立ち往生しているので、VIEWではなく、ストアドプロシージャの使用を検討してください。 2つの追加のテーブルが含まれます。自己結合に一時テーブルを2回使用することはできないため、これらを_CREATE TEMPORARY TABLE_にすることはできません。

したがって、より良いアプローチ(ウィンドウ処理なし)は、_@variables_を使用してLAG()をシミュレートすることです。

アプローチ4(1と3の間のどこか)

これには、連続する行を確認する単一のパスが含まれます。連続する(日時に基づく)ペアの最初の行がONである場合、「定刻」を計算します。次にSUMが「オンタイム」です。

詳細(多分私はあなたに詳細を与えるでしょうが、最初に...)

サンプルデータを提供します(画像ではありません)すぐに実行できます(CREATEsおよびINSERTs)。期待される出力も提供します。 (私はONからOFFまでを理解していますが、OFFからONについてはどうですか?範囲の開始時間などを希望しますか?)

しゃっくり

「時間通り」に正時で停止したいので、合計などを停止するために追加のコードが必要になります。これを行うための1つの人工的な方法は、毎時0時に1秒間隔で2つの行を追加することです。最初はOFF、2番目(1秒後)はONです。これにより、結果に1秒のエラーが発生しますが、「時間帯」要件を単純化することを除いて、アプローチに影響はありません。

3
Rick James

ユーザー変数を使用したソリューション:

SELECT p_feed FeedId, MAX(p_start) dt_start, MAX(p_end) dt_end, TIMEDIFF(MAX(p_end), MAX(p_start)) duration 
FROM (
SELECT @start := CASE WHEN f.value = 'on' AND @value = 'off' THEN f.`datetime` ELSE null END p_start,
       @end   := CASE WHEN @value = 'on' AND f.value = 'off' AND @feed = f.FeedId THEN f.`datetime` ELSE null END p_end,
       @num   := @num + (@start IS NOT NULL) p_num,
       @feed  := f.FeedId p_feed,
       @value := f.value p_value
FROM feedsdata f, (SELECT @start := 0, @end := 0, @num := 0, @feed := 0, @value := '') init_vars
ORDER BY f.FeedId ASC, f.`DateTime` ASC
     ) subquery
WHERE p_num > 0
GROUP BY p_num
HAVING duration IS NOT NULL;

fiddle (2つの異なるFeedId値を使用)。

最適化のために(FeedId, `DateTime`)による索引が必要です。

0
Akina
SELECT FeedId, MIN(dt_start) dt_start, dt_end, TIMEDIFF(dt_end, MIN(dt_start)) duration
FROM ( SELECT t1.FeedId, t1.`DateTime` dt_start, t2.`DateTime` dt_end
       FROM feedsdata t1, feedsdata t2
       WHERE t1.value = 'on'
         AND t2.value = 'off'
         AND t1.FeedId = t2.FeedId
         AND t1.`DateTime` < t2.`DateTime`
         AND NOT EXISTS (SELECT 1
                         FROM feedsdata
                         WHERE value = 'off'
                           AND t1.`DateTime` < `DateTime`
                           AND `DateTime` < t2.`DateTime`
                           AND FeedId = t1.FeedId)
      ) dummy
GROUP BY FeedId, dt_end;

フィドル

0
Akina