Bước 1:Nhả phanh tay
SELECT to_char(MIN(ts)::timestamptz, 'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
,SUM(CASE WHEN sensor_id = 572 THEN value ELSE 0.0 END) AS nickname1
,SUM(CASE WHEN sensor_id = 542 THEN value ELSE 0.0 END) AS nickname2
,SUM(CASE WHEN sensor_id = 571 THEN value ELSE 0.0 END) AS nickname3
FROM sensor_values
-- LEFT JOIN sensor_values_cleaned s2 USING (sensor_id, ts)
WHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
AND ts < '2013-10-18T00:00:00+00:00'::timestamptz::timestamp
AND sensor_id IN (572, 542, 571, 540, 541, 573)
GROUP BY ts::date AS day
ORDER BY 1;
Những điểm chính
-
Thay thế từ dành riêng (trong SQL chuẩn) trong số nhận dạng của bạn.
timestamp
->ts
time
->min_time
-
Vì liên kết nằm trên các tên cột giống nhau nên bạn có thể sử dụng
USING
mệnh đề trong điều kiện tham gia:USING (sensor_id, ts)
Tuy nhiên, kể từ bảng thứ haisensor_values_cleaned
100% không liên quan đến truy vấn này, tôi đã xóa nó hoàn toàn. -
Như @joop đã được tư vấn, hãy chuyển đổi
min()
vàto_char()
trong cột đưa ra đầu tiên của bạn. Bằng cách này, Postgres có thể xác định giá trị tối thiểu từ giá trị cột ban đầu , thường nhanh hơn và có thể sử dụng một chỉ mục. Trong trường hợp cụ thể này, đặt hàng trướcdate
cũng rẻ hơn đặt hàng bằngtext
, điều này cũng sẽ phải xem xét các quy tắc đối chiếu. -
Một cân nhắc tương tự cũng áp dụng cho
WHERE
của bạn điều kiện:
WHERE ts ::timestamptz> ='2013-10-14T00:00:00 + 00:00' ::timestamptzWHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
Cái thứ hai là sargable và có thể sử dụng một chỉ mục thuần túy trên
ts
- có tác dụng lớn đến hiệu suất trong các bảng lớn! -
Sử dụng
ts::date
thay vìdate_trunc('day', ts)
. Đơn giản hơn, nhanh hơn, cùng một kết quả. -
Rất có thể điều kiện WHERE thứ hai của bạn hơi không chính xác. Nói chung, bạn sẽ loại trừ đường viền trên :
AND ts <= '2013-10-18T00:00:00+00:00' ...
AND ts < '2013-10-18T00:00:00+00:00' ...
-
Khi trộn
timestamp
vàtimestamptz
một người cần phải nhận thức được các hiệu ứng. Ví dụ:WHERE
của bạn điều kiện không bị cắt lúc 00:00 giờ địa phương (trừ khi giờ địa phương trùng với UTC). Thông tin chi tiết tại đây:
Bỏ qua hoàn toàn các múi giờ trong Rails và PostgreSQL
Bước 2:Yêu cầu của bạn
Và tôi cho rằng ý của bạn là:
... sự khác biệt giữa giá trị của dấu thời gian mới nhất và sớm nhất ...
Nếu không thì sẽ đơn giản hơn nhiều.
Sử dụng chức năng cửa sổ
cho điều đó, cụ thể là first_value()
và last_value()
. Cẩn thận với sự kết hợp, bạn muốn có một không phải -khung cửa sổ tiêu chuẩn
cho last_value () trong trường hợp này. So sánh:
Hàm tổng hợp hoặc cửa sổ PostgreSQL để chỉ trả về giá trị cuối cùng
Tôi kết hợp điều này với DISTINCT ON
, trong trường hợp này thuận tiện hơn GROUP BY
(sẽ cần một cấp truy vấn con khác):
SELECT DISTINCT ON (ts::date, sensor_id)
ts::date AS day
,to_char((min(ts) OVER (PARTITION BY ts::date))::timestamptz
,'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
,sensor_id
,last_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
- first_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts)
AS val_range
FROM sensor_values
WHERE ts >= '2013-10-14T00:00:00+0'::timestamptz::timestamp
AND ts < '2013-10-18T00:00:00+0'::timestamptz::timestamp
AND sensor_id IN (540, 541, 542, 571, 572, 573)
ORDER BY ts::date, sensor_id;
Bước 3:Bảng tổng hợp
Dựa trên truy vấn ở trên, tôi sử dụng crosstab()
từ mô-đun bổ sung tablefunc
:
SELECT * FROM crosstab(
$$SELECT DISTINCT ON (1,3)
ts::date AS day
,to_char((min(ts) OVER (PARTITION BY ts::date))::timestamptz,'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
,sensor_id
,last_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
- first_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts) AS val_range
FROM sensor_values
WHERE ts >= '2013-10-14T00:00:00+0'::timestamptz::timestamp
AND ts < '2013-10-18T00:00:00+0'::timestamptz::timestamp
AND sensor_id IN (540, 541, 542, 571, 572, 573)
ORDER BY 1, 3$$
,$$VALUES (540), (541), (542), (571), (572), (573)$$
)
AS ct (day date, min_time text, s540 numeric, s541 numeric, s542 numeric, s571 numeric, s572 numeric, s573 numeric);
Lợi nhuận (và nhiều nhanh hơn trước):
day | min_time | s540 | s541 | s542 | s571 | s572 | s573
------------+--------------------------+-------+-------+-------+-------+-------+-------
2013-10-14 | 2013-10-14 03:00:00 CEST | 18.82 | 18.98 | 19.97 | 19.47 | 17.56 | 21.27
2013-10-15 | 2013-10-15 00:15:00 CEST | 22.59 | 24.20 | 22.90 | 21.27 | 22.75 | 22.23
2013-10-16 | 2013-10-16 00:16:00 CEST | 23.74 | 22.52 | 22.23 | 23.22 | 23.03 | 22.98
2013-10-17 | 2013-10-17 00:17:00 CEST | 21.68 | 24.54 | 21.15 | 23.58 | 23.04 | 21.94