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

Postgresで時間間隔の平均値を取得する方法

    DB設計

    できる 別のdateで作業する およびtime 列の場合、単一のタイムスタンプ<に勝る利点はありません。 / code> 桁。私は適応します:

    ALTER TABLE tbl ADD column ts timestamp;
    UPDATE tbl SET ts = date + time;  -- assuming actual date and time types
    ALTER TABLE tbl DROP column date, DROP column time;
    

    日付と時刻が実際の日付でない場合 およびtime データ型については、 to_timestamp()を使用してください 。関連:

    クエリ

    その場合、クエリは少し簡単になります:

    SELECT *
    FROM  (
       SELECT sn, generate_series(min(ts), max(ts), interval '5 min') AS ts
       FROM   tbl
       WHERE  sn = '4as11111111'
       AND    ts >= '2018-01-01'
       AND    ts <  '2018-01-02'
       GROUP  BY 1
       ) grid
    CROSS  JOIN LATERAL (
       SELECT round(avg(vin1), 2) AS vin1_av
            , round(avg(vin2), 2) AS vin2_av
            , round(avg(vin3), 2) AS vin3_av
       FROM   tbl
       WHERE  sn =  grid.sn
       AND    ts >= grid.ts
       AND    ts <  grid.ts + interval '5 min'
       ) avg;
    

    db <> fiddle こちら

    最初のサブクエリgridで開始時刻のグリッドを生成します 、最初から最後の予選まで実行 指定された時間枠内の行。

    LATERALを使用して各パーティションに分類される行に結合します サブクエリavgに参加し、すぐに平均を集計します 。骨材のため、常に エントリが見つからない場合でも行を返します。平均はデフォルトでNULL この場合。

    結果には、指定された時間枠内の最初と最後の適格行の間のすべてのタイムスロットが含まれます。他のさまざまな結果構成も意味があります。 すべてを含めるように 指定された時間枠内のタイムスロット、または実際の値を持つタイムスロット。可能な限り、私は1つの解釈を選ばなければなりませんでした。

    インデックス

    少なくともこの複数列のインデックスがあります:

    CRATE INDEX foo_idx ON tbl (sn, ts);
    

    または(sn、ts、vin1、vin2、vin3) インデックスのみのスキャンを許可する-いくつかの前提条件が満たされている場合、特にテーブルの行がデモよりもはるかに広い場合。

    密接に関連している:

    元のテーブルに基づく

    コメントで要求および明確化されたとおり 、後で質問で再度更新され、 mac列が含まれるようになりました およびloc(mac、loc)ごとに個別の平均が必要だと思います 。

    日付 およびtime まだ別々の列であり、vin*列はタイプfloat 、および行のないタイムスロットを除外します:

    更新されたクエリは、セットを返す関数 generate_series()も移動します FROMへ リスト。Postgres10より前の方がクリーンです:

    SELECT t.mac, sn.sn, t.loc, ts.ts::time AS time, ts.ts::date AS date
         , t.vin1_av, t.vin2_av, t.vin3_av
    FROM  (SELECT text '4as11111111') sn(sn)  -- provide sn here once
    CROSS  JOIN LATERAL (
       SELECT min(date+time) AS min_ts, max(date+time) AS max_ts
       FROM   tbl
       WHERE  sn = sn.sn
       AND    date+time >= '2018-01-01 0:0'   -- provide time frame here
       AND    date+time <  '2018-01-02 0:0'
       ) grid
    CROSS  JOIN LATERAL generate_series(min_ts, max_ts, interval '5 min') ts(ts)
    CROSS  JOIN LATERAL (
       SELECT mac, loc
            , round(avg(vin1)::numeric, 2) AS vin1_av  -- cast to numeric for round()
            , round(avg(vin2)::numeric, 2) AS vin2_av  -- but rounding is optional
            , round(avg(vin3)::numeric, 2) AS vin3_av
       FROM   tbl
       WHERE  sn = sn.sn
       AND    date+time >= ts.ts
       AND    date+time <  ts.ts + interval '5 min'
       GROUP  BY mac, loc
       HAVING count(*) > 0  -- exclude empty slots
       ) t;
    

    これをサポートする複数列の式インデックスを作成します:

    CRATE INDEX bar_idx ON tbl (sn, (date+time));
    

    db <> fiddle こちら

    しかし、私はむしろタイムスタンプを使用したいと思います ずっと。




    1. GoogleChartのタイムラインアイテムからリンクを作成する

    2. hasMany、belongsTo、またはその両方を続編しますか?

    3. PHP MySQLの結果をキャッシュする最良の方法は?

    4. OracleでLONGをvarcharに変換する