PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Nhận sự khác biệt của trường khác giữa dấu thời gian đầu tiên và cuối cùng của nhóm

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ứ hai sensor_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()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ước date cũng rẻ hơn đặt hàng bằng text , đ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' ::timestamptz

    WHERE  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 timestamptimestamptz 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()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ản trình diễn SQLfiddle.

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Xử lý EXCEPTION và trả về kết quả từ hàm

  2. Sự trở lại của XFS trên Linux

  3. Cách sử dụng vị trí thực của các hàng (ROWID) trong câu lệnh DELETE

  4. Làm thế nào để sửa đổi trường với jsonPath trong PostgreSQL?

  5. Tìm tên máy chủ trong postgresql