Thiết kế DB
Trong khi bạn có thể làm việc với date
riêng biệt và time
thực sự không có lợi thế so với một timestamp
cột. Tôi sẽ điều chỉnh:
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;
Nếu ngày và giờ không phải là date
thực tế và time
kiểu dữ liệu, sử dụng to_timestamp()
. Có liên quan:
Truy vấn
Sau đó, truy vấn đơn giản hơn một chút:
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 tại đây
Tạo lưới thời gian bắt đầu trong grid
truy vấn con đầu tiên , chạy từ vòng loại đầu tiên đến cuối cùng hàng trong khung thời gian nhất định.
Tham gia vào các hàng nằm trong mỗi phân vùng bằng LATERAL
tham gia và tổng hợp ngay các giá trị trung bình trong truy vấn con avg
. Do các uẩn, nó luôn luôn trả về một hàng ngay cả khi không tìm thấy mục nhập nào. Giá trị trung bình mặc định là NULL
trong trường hợp này.
Kết quả bao gồm tất cả các khoảng thời gian giữa hàng đủ điều kiện đầu tiên và cuối cùng trong khung thời gian nhất định. Nhiều thành phần kết quả khác cũng sẽ có ý nghĩa. Như bao gồm tất cả các khe thời gian trong khung thời gian nhất định hoặc chỉ các khe thời gian có giá trị thực tế. Tất cả có thể, tôi phải chọn một cách diễn giải.
Chỉ mục
Ít nhất phải có chỉ mục đa cột này:
CRATE INDEX foo_idx ON tbl (sn, ts);
Hoặc trên (sn, ts, vin1, vin2, vin3)
để cho phép quét chỉ theo chỉ mục - nếu một số điều kiện tiên quyết được đáp ứng và đặc biệt nếu các hàng trong bảng rộng hơn nhiều so với trong bản trình diễn.
Liên quan chặt chẽ:
- THAM GIA TRÁI chậm trên CTE với các khoảng thời gian
- Cách tốt nhất để đếm các bản ghi theo khoảng thời gian tùy ý trong Rails + Postgres
Dựa trên bảng gốc của bạn
Theo yêu cầu và làm rõ trong nhận xét
và sau đó được cập nhật lại trong câu hỏi để bao gồm các cột mac
và loc
. Tôi giả sử bạn muốn các giá trị trung bình riêng biệt cho mỗi (mac, loc)
.
date
và time
vẫn là các cột riêng biệt, các cột vin * là loại float
và loại trừ các khoảng thời gian không có hàng:
Truy vấn cập nhật cũng di chuyển hàm set-return generate_series()
đến FROM
danh sách này rõ ràng hơn trước Postgres 10:
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;
Tạo chỉ mục biểu thức đa cột để hỗ trợ điều này:
CRATE INDEX bar_idx ON tbl (sn, (date+time));
db <> fiddle tại đây
Nhưng tôi muốn sử dụng timestamp
tất cả cùng.