sql >> データベース >  >> RDS >> Mysql

MySQLで時系列データのギャップを見つける方法は?

    まず、テーブルのエントリ数を1時間ごとに要約します。

    SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour,
           COUNT(*) samplecount
      FROM table
     GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME)
    

    これで、6分ごと(1時間に10回)に何かをログに記録すると、すべてのsamplecount値は10になります。この式:CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) 毛むくじゃらに見えますが、タイムスタンプを分と秒をゼロにすることで、タイムスタンプが発生する時間に切り捨てるだけです。

    これはかなり効率的であり、始めることができます。 entry_time列にインデックスを付けて、クエリを、たとえば、ここに示すように昨日のサンプルに制限できると、非常に効率的です。

    SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour,
           COUNT(*) samplecount
      FROM table
     WHERE entry_time >= CURRENT_DATE - INTERVAL 1 DAY
       AND entry_time < CURRENT_DATE
     GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME)
    

    しかし、サンプルが欠落している時間全体を検出するのはあまり得意ではありません。また、サンプリングのジッターにも少し敏感です。つまり、時間の最上位のサンプルが0.5秒早い(10:59:30)場合と0.5秒遅い(11:00:30)場合は、1時間ごとの要約カウントがオフになります。したがって、この時間の要約(または日の要約、または分の要約など)は防弾ではありません。

    完全に正しく処理するには、自己結合クエリが必要です。それはもう少し毛玉であり、ほとんど効率的ではありません。

    番号付きのサンプルを使用して、このような仮想テーブル(サブクエリ)を作成することから始めましょう。 (これはMySQLの苦痛です。他の高価なDBMSを使用すると、簡単になります。関係ありません。)

      SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
        FROM (
            SELECT entry_time, value
          FROM table
             ORDER BY entry_time
        ) C,
        (SELECT @sample:=0) s
    

    この小さな仮想テーブルは、entry_num、entry_time、valueを提供します。

    次のステップでは、それを結合します。

    SELECT one.entry_num, one.entry_time, one.value, 
           TIMEDIFF(two.value, one.value) interval
      FROM (
         /* virtual table */
      ) ONE
      JOIN (
         /* same virtual table */
      ) TWO ON (TWO.entry_num - 1 = ONE.entry_num)
    

    これにより、JOINのON句によって制御される単一のエントリによってオフセットされたテーブルが互いに隣接して整列します。

    最後に、このテーブルからintervalを使用して値を選択します しきい値よりも大きく、欠落しているサンプルの直前のサンプルの時間があります。

    全体的な自己結合クエリはこれです。毛玉だと言った。

    SELECT one.entry_num, one.entry_time, one.value, 
           TIMEDIFF(two.value, one.value) interval
      FROM (
        SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
          FROM (
              SELECT entry_time, value
                FROM table
               ORDER BY entry_time
          ) C,
          (SELECT @sample:=0) s
      ) ONE
      JOIN (
        SELECT @sample2:[email protected]+1 AS entry_num, c.entry_time, c.value
          FROM (
              SELECT entry_time, value
                FROM table
               ORDER BY entry_time
          ) C,
          (SELECT @sample2:=0) s
      ) TWO ON (TWO.entry_num - 1 = ONE.entry_num)
    

    大きなテーブルで本番環境でこれを実行する必要がある場合は、データのサブセットに対して実行することをお勧めします。たとえば、過去2日間のサンプルに対して毎日それを行うことができます。これはかなり効率的であり、真夜中に欠落しているサンプルを見落とさないようにすることもできます。これを行うには、行番号が小さい仮想テーブルは次のようになります。

      SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
        FROM (
            SELECT entry_time, value
          FROM table
             ORDER BY entry_time
             WHERE entry_time >= CURRENT_DATE - INTERVAL 2 DAY
               AND entry_time < CURRENT_DATE /*yesterday but not today*/
        ) C,
        (SELECT @sample:=0) s
    


    1. WebサイトのダウンとTwitterフィード

    2. phpMyAdminを使用してデータベースを複製する方法

    3. Google BigQueryで数千のカテゴリのダミー変数列を作成するにはどうすればよいですか?

    4. パラメータ化されたクエリが、パラメータ化されていないクエリと比較して非常に遅いクエリプランを生成するのはなぜですか