PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Вземете разликата на друго поле между първия и последния времеви отпечатъци на групирането

Стъпка 1:Освободете ръчните спирачки

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;

Основни точки

  • Заменете запазени думи (в стандартен SQL) във вашите идентификатори.
    timestamp -> ts
    time -> min_time

  • Тъй като свързването е на идентични имена на колони, можете да използвате по-простия USING клаузата в условието за присъединяване:USING (sensor_id, ts)
    Въпреки това, след втората таблица sensor_values_cleaned е 100% неподходящ за тази заявка, премахнах го изцяло.

  • Както @joop вече посъветва, превключете min() и to_char() в първата си колона. По този начин Postgres може да определи минимума от оригиналната стойност на колоната , което обикновено е по-бързо и може да е в състояние да използва индекс. В този конкретен случай, поръчка по date също е по-евтино от поръчка чрез text , което също ще трябва да вземе предвид правилата за сортиране.

  • Подобно съображение се отнася и за вашето WHERE условие:
    WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptz

    WHERE  ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
    

    Вторият е sargable и може да използва обикновен индекс на ts - за голям ефект върху производителността в големи маси!

  • Използване на ts::date вместо date_trunc('day', ts) . По-просто, по-бързо, същият резултат.

  • Най-вероятно вашето второ WHERE условие е малко неправилно. По принцип бихте изключили горната граница :

    AND    ts <=  '2013-10-18T00:00:00+00:00' ...

    AND    ts <   '2013-10-18T00:00:00+00:00' ...
  • При смесване на timestamp и timestamptz човек трябва да е наясно с ефектите. Например вашето WHERE условието не прекъсва в 00:00 местно време (освен ако местното време съвпада с UTC). Подробности тук:
    Пълно игнориране на часовите зони в Rails и PostgreSQL

Стъпка 2:Вашата заявка

И с това предполагам имате предвид:
...разликата между стойността на най-новите и най-ранните времеви марки ...
В противен случай би било много по-просто.

Използвайте прозоречни функции за това, по-специално first_value() и last_value() . Внимавайте с комбинацията, искате не -стандартна дограма за last_value() в този случай. Сравнете:
PostgreSQL агрегатна или прозоречна функция за връщане само на последната стойност

Комбинирам това с DISTINCT ON , което е по-удобно в този случай от GROUP BY (което ще изисква друго ниво на подзаявка):

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;

-> Демонстрация на SQLfiddle.

Стъпка 3:Обобщена таблица

Въз основа на заявката по-горе използвам crosstab() от допълнителния модул 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);

Връщания (и много по-бързо от преди):

    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. PostgreSQL - прави първия ред да се показва като сбор от останалите редове

  2. PostgreSQL:най-добрият начин за свързване на малки подгрупи от големи таблици

  3. Вмъкнете множество ENUM стойности в PostgreSQL

  4. Множество извиквания на array_agg() в една заявка

  5. Най-новите тенденции в PostgreSQL:Най-отнемащи време задачи и важни показатели за проследяване