3

次の一連の表で定義されているように、3 つのプロセス A、B、および C があります。

http://sqlfiddle.com/#!2/48f54

CREATE TABLE processA
(date_time datetime, valueA int);

INSERT INTO processA
                (date_time, valueA)
VALUES
('2013-1-8  22:10:00', 100),
('2013-1-8  22:15:00', 100),
('2013-1-8  22:30:00', 100),
('2013-1-8  22:35:00', 100),
('2013-1-8  22:40:00', 100),
('2013-1-8  22:45:00', 100),
('2013-1-8  22:50:00', 100),
('2013-1-8  23:05:00', 100),
('2013-1-8  23:10:00', 100),
('2013-1-8  23:20:00', 100),
('2013-1-8  23:25:00', 100),
('2013-1-8  23:35:00', 100),
('2013-1-8  23:40:00', 100),
('2013-1-9  00:05:00', 100),
('2013-1-9  00:10:00', 100);


CREATE TABLE processB
(date_time datetime, valueB decimal(4,2));

INSERT INTO processB
                (date_time, valueB)
VALUES
('2013-1-08  21:46:00', 3),
  ('2013-1-08  22:11:00', 4),
  ('2013-1-08  22:31:00', 5),
  ('2013-1-08  22:36:00', 6),
  ('2013-1-08  22:41:00', 7),
  ('2013-1-08  23:06:00', 8),
  ('2013-1-08  23:20:00', 2),
  ('2013-1-08  23:46:00', 3),
  ('2013-1-09  00:34:00', 9);


CREATE TABLE processC
(date_time datetime, status varchar(4));

INSERT INTO processC

VALUES
('2013-1-08 18:00:00', 'yes'),
('2013-1-08 19:00:00', 'yes'),
('2013-1-08 20:00:00', 'yes'),
('2013-1-08 21:00:00', 'yes'),
('2013-1-08 22:00:00', 'yes'),
('2013-1-08 23:00:00', 'no'),
('2013-1-08 00:00:00', 'no'),
('2013-1-08 01:00:00', 'no');

ご覧のとおり、各プロセスで読み取りが発生する時間は同じではありません。

  1. ProcessA は、発生た場合、5 分間隔で発生します。

  2. プロセス B、測定値は予測できない時間に発生しますが、通常は 1 時間以内に複数回発生します

  3. ProcessC には常に時間単位の値 (yes または no) があります。

まず、5 分間隔で読み取りが行われるように processB を変換して、データが processA と一致するようにします。これにより、5 分間隔で両方のテーブルを簡単に結合できるようになります。変換のために、5 分ごとのデータは、[-30,30) 分ウィンドウ内で利用可能な最も近いプロセス B 観測に設定する必要があります。値が等距離の場合は、平均を取ります。30 分のウィンドウ内に何も利用できない場合は、null に設定します。

それができたら、次のような方法で %Y%m%d%H と ProcessC を単純に結合して、すべてのデータが 5 分間隔で整列された最終テーブルを取得できます。

date_format(date_time, '%Y%m%d%H') = date_format(date_time, '%Y%m%d%H')

誰かが何か指針/ガイダンスを持っているなら、私は何らかの方向性をいただければ幸いです. それは有り難いです。

出力例:

'2013-1-8  22:10:00', 100, 4, yes    <--- closer to 22:11 than 21:46
'2013-1-8  22:15:00', 100, 4, yes    <--- closer to 22:11 than 21:31
'2013-1-8  22:30:00', 100, 5, yes    <--- closer to 22:31 than 22:11
'2013-1-8  22:35:00', 100, 6, yes    <--- closer to 22:36 than 22:31
'2013-1-8  22:40:00', 100, 7, yes    <--- closer to 22:41 than 22:36
'2013-1-8  22:45:00', 100, 7, yes    <--- closer to 22:41 than 23:06
'2013-1-8  22:50:00', 100, 7, yes    <--- closer to 22:41 than 23:06
'2013-1-8  23:05:00', 100, 8, yes    <--- closer to 23:06 than 23:06
'2013-1-8  23:10:00', 100, 8, no     <--- closer to 23:06 than 23:20
'2013-1-8  23:20:00', 100, 2, no     <--- closer to 23:20 than 23:10
'2013-1-8  23:25:00', 100, 2, no    <--- closer to 23:20 than 23:10
'2013-1-8  23:35:00', 100, 3, no    <--- closer to 23:46 than 23:20
'2013-1-9  00:05:00', 100, 3, no    <--- closer to 23:46 than 00:34
'2013-1-9  00:10:00', 100, 6, no    <--- takes the avg of 3 and 9
4

1 に答える 1

4

processBこれのトリッキーな部分は、あなたが考え出したように、各行に対応する適切な行を取得するprocessAことです。

一歩一歩見ていきましょう。

まず、候補のタイムスタンプ ペアを取得するために、processA と processB を結合できる必要があります。次のようにしましょう。

               SELECT a.date_time a, 
                      TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
                 FROM processA a
                 JOIN processB b 
                   ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
                  AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) <   1800

これにより、[-30, 30) 基準を満たす a 時間と b 時間が得られます。この結果には多くの行があります。しかし、それを検査して、範囲比較が正しく行われたことを確認できます。 http://sqlfiddle.com/#!2/48f54/47/0

ここで、1 つ以上の一致する b レコードの各 a レコードを検索するための時間枠を生成する必要があります。そのようです。

       SELECT a, 
              MIN(ABS(timediff)) windowsize
          FROM (
               SELECT a.date_time a, 
                      TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
                 FROM processA a
                 JOIN processB b 
                   ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
                  AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) <   1800
         ) d
        GROUP BY a  

これにより、2 つの列が生成されます。最初の列は a からのタイムスタンプであり、2 番目は、範囲内にある最も近い b タイムスタンプ (複数のタイムスタンプを平均化する場合はタイムスタンプ) の時間範囲です。この結果セットには、考慮するのに十分なほど近くに b レコードがない a レコードの行がありません。 http://sqlfiddle.com/#!2/48f54/46/0

最後に、各 a レコードの b レコード値を取得して平均化する必要があります。こちらです。

SELECT processA.date_time date_time,
       processA.valueA valueA,
       AVG(processB.valueB) valueB
  FROM processA
  LEFT JOIN (
        SELECT a, 
               MIN(ABS(timediff)) windowsize
              FROM (
                   SELECT a.date_time a, 
                          TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
                     FROM processA a
                     JOIN processB b 
                       ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
                      AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) <   1800
             ) d
          GROUP BY a
    ) j ON processA.date_time = j.a
   LEFT JOIN processB ON (    processB.date_time >= j.a - INTERVAL j.windowsize SECOND
                          AND processB.date_time <= j.a + INTERVAL j.windowsize SECOND
                          AND processB.date_time < j.a + INTERVAL 1800 SECOND)
  GROUP BY processA.date_time, processA.valueA

ここにはいくつかの開いている範囲があることに注意してください (<演算子の代わりに<=演算子)。これらは、[-30, 30) オープン レンジに対応するためにあります。これがクエリです。 http://sqlfiddle.com/#!2/48f54/45/0

この最後のクエリは、3 つのテーブルを結合します: processA、各タイムスタンプの検索範囲を示す仮想テーブル、およびprocess B. 最後のON句は、実際の範囲検索を実行します。オープンレンジによって少し複雑になっています。

これがどうなるかわかりますか?内部からクエリを作成すると便利です。

processB.date_time にインデックスを付けることを忘れないでください。

processCこの仮想テーブルへの結合はあなたに任せます。

于 2013-06-10T23:28:11.417 に答える