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

Rails+Postgresで任意の時間間隔でレコードをカウントするための最良の方法

    幸い、PostgreSQLを使用しています。ウィンドウ関数generate_series() はあなたの友達です。

    テストケース

    次のテストテーブル(あなた 提供する必要があります):

    CREATE TABLE event(event_id serial, ts timestamp);
    INSERT INTO event (ts)
    SELECT generate_series(timestamp '2018-05-01'
                         , timestamp '2018-05-08'
                         , interval '7 min') + random() * interval '7 min';
    

    7分ごとに1つのイベント(さらに0〜7分、ランダムに)。

    基本的な解決策

    このクエリは、任意の時間間隔のイベントをカウントします。例では17分:

    WITH grid AS (
       SELECT start_time
            , lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
       FROM  (
          SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
          FROM   event
          ) sub
       )
    SELECT start_time, count(e.ts) AS events
    FROM   grid       g
    LEFT   JOIN event e ON e.ts >= g.start_time
                       AND e.ts <  g.end_time
    GROUP  BY start_time
    ORDER  BY start_time;
    
    • クエリは最小および最大のtsを取得します ベーステーブルから完全な時間範囲をカバーします。代わりに任意の時間範囲を使用できます。

    • 任意を提供する 時間間隔 必要に応じて。

    • すべてに対して1行を生成します タイムスロット。その間隔中にイベントが発生しなかった場合、カウントは0です。 。

    • 必ず上限と下限を処理してください 正しく:

      • タイムスタンプ間のSQLクエリからの予期しない結果
    • ウィンドウ関数lead() 見過ごされがちな機能があります。先頭の行が存在しない場合のデフォルトを提供できます。 'infinity'を提供する 例では。それ以外の場合、最後の間隔は上限のNULLで切り捨てられます 。

    最小限の同等物

    上記のクエリはCTEとlead()を使用しています 冗長な構文。エレガントで理解しやすいかもしれませんが、少し高価です。これは、より短く、より速く、最小限のバージョンです:

    SELECT start_time, count(e.ts) AS events
    FROM  (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
    LEFT   JOIN event e ON e.ts >= g.start_time
                       AND e.ts <  g.start_time + interval '17 min'
    GROUP  BY 1
    ORDER  BY 1;
    

    「先週15分ごと」の例`

    そして、to_char()でフォーマットします 。

    SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
    FROM   generate_series(date_trunc('day', localtimestamp - interval '7 days')
                         , localtimestamp
                         , interval '15 min') g(start_time)
    LEFT   JOIN event e ON e.ts >= g.start_time
                       AND e.ts <  g.start_time + interval '15 min'
    GROUP  BY start_time
    ORDER  BY start_time;

    それでもORDER BY およびGROUP BY 基になるタイムスタンプの 、フォーマットされた文字列ではありません。それはより速く、より信頼性があります。

    db<>ここでフィドル

    実行カウントを生成する関連回答 時間枠全体:

    • PostgreSQL:クエリの行数を「分単位」で実行



    1. SQL ServerのPARSE()とTRY_PARSE()の違い:違いは何ですか?

    2. 'localhost'のMySQLサーバーに接続できません(10061)

    3. テーブルに存在しないINリストの要素に対しても結果を返します

    4. 列のセットに対するNOTNULL制約