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 こちら
しかし、私はむしろタイムスタンプ
を使用したいと思います ずっと。