SQL中每行的平均值来自多列和空值

时间:2015-05-14 10:22:26

标签: sql postgresql

我有一个记录传感器数据的应用程序,我希望能够从多个传感器产生平均值,可以是一个,两个,三个或很多......

编辑:这些是温度传感器,因此0是传感器可能在数据库中存储的值。

我的初始起点是这个SQL查询:

SELECT grid.t5||'.000000' as ts,
avg(t.sensorvalue)  sensorvalue1
, avg(w.sensorvalue)AS sensorvalue2
FROM 
(SELECT generate_series(min(date_trunc('hour', ts))                         
,max(ts), interval '5 min') AS t5   FROM device_history_20865735 where      
 ts between '2015/05/13 09:00' and '2015/05/14 09:00'   ) grid 

 LEFT JOIN device_history_20865735 t ON t.ts >= grid.t5 AND t.ts <  grid.t5 +  interval '5 min' 
 LEFT JOIN device_history_493417852 w ON w.ts >= grid.t5 AND w.ts <  grid.t5 +  interval '5 min' 
--WHERE t.sensorvalue notnull
GROUP  BY grid.t5 ORDER  BY grid.t5

我得到5分钟的平均值,因为它对我的应用程序更好。

预期的结果对于sensorvalue1或2具有NULL值:

ts;sensorvalue1;sensorvalue2
"2015-05-13 09:00:00.000000";19.9300003051758;
"2015-05-13 09:05:00.000000";20;
"2015-05-13 09:10:00.000000";;
"2015-05-13 09:15:00.000000";20.0599994659424;
"2015-05-13 09:20:00.000000";;
"2015-05-13 09:25:00.000000";20.1200008392334;

我的目标是计算所有可用传感器每5分钟间隔的平均值,因为NULL是一个问题我想到使用CASE语句,所以如果有一个NULL来获取另一个传感器的值...

SELECT grid.t5||'.000000' as ts,
CASE 
        WHEN avg(t.sensorvalue) ISNULL  THEN avg(w.sensorvalue)
        ELSE avg(t.sensorvalue)
END AS sensorvalue
,
CASE 
        WHEN avg(w.sensorvalue) ISNULL  THEN avg(t.sensorvalue)
        ELSE avg(w.sensorvalue)
END AS sensorvalue2
FROM 
(SELECT generate_series(min(date_trunc('hour', ts)),max(ts), interval '5 min') AS t5
FROM device_history_20865735 where      
 ts between '2015/05/13 09:00' and '2015/05/14 09:00'   ) grid 

 LEFT JOIN device_history_20865735 t ON t.ts >= grid.t5 AND t.ts <  grid.t5 +  interval '5 min' 
 LEFT JOIN device_history_493417852 w ON w.ts >= grid.t5 AND w.ts <  grid.t5 +  interval '5 min' 
GROUP  BY grid.t5 ORDER  BY grid.t5

然后要计算平均值我必须在此基础上做另一个选择并且每列数量(也就是传感器),如果它们只有两个就可以了,但是如果有3个或4个传感器,这可以得到非常好的因为可能有多个传感器每行都有NULL值...

SQL是使用postgres 9.4从应用程序(使用Python)进行语法派生的,所以有一种简单的方法可以实现我需要的东西,因为我觉得我在一条相当复杂的路线上......?

编辑#2:根据您的输入我已经生成了这个SQL代码,如果它可靠且可维护的话,它似乎相当复杂但对您的想法和审查持开放态度:

SELECT ts, sensortotal, sensorcount,
CASE
    WHEN sensorcount = 0 THEN -1000
    ELSE sensortotal/sensorcount
END AS sensorAvg

FROM (
    WITH grid as (
          SELECT t5
          FROM (SELECT generate_series(min(date_trunc('hour', ts)), max(ts), interval '5 min') as t5
                FROM device_history_20865735
               ) d
          WHERE t5 between '2015-05-13 09:00' and '2015-05-14 09:00'   
         )
    SELECT d1.t5 || '.000000' as ts
           , Coalesce(avg(d1.sensorvalue), 0) + Coalesce(avg(d2.sensorvalue),0) as sensorTotal
           , (CASE
                    WHEN avg(d1.sensorvalue) ISNULL THEN 0
                    ELSE 1
           END + CASE
            WHEN avg(d2.sensorvalue) ISNULL THEN 0
            ELSE 1
           END) as sensorCount

    FROM (SELECT grid.t5, avg(t.sensorvalue) as sensorvalue
          FROM grid LEFT JOIN
               device_history_20865735 t
               ON t.ts >= grid.t5 AND t.ts <grid.t5 +  interval '5 min' 
          GROUP BY grid.t5
         ) d1 LEFT JOIN
         (SELECT grid.t5, avg(t.sensorvalue) as sensorvalue
          FROM grid LEFT JOIN
               device_history_493417852 t
               ON t.ts >= grid.t5 AND t.ts <grid.t5 +  interval '5 min' 
         GROUP BY grid.t5
         ) d2 on d1.t5 = d2.t5
    GROUP BY d1.t5
    ORDER BY d1.t5
) tmp;

谢谢!

2 个答案:

答案 0 :(得分:0)

要获得准确的平均值,您需要在加入之前单独计算每个

WITH grid as (
      SELECT t5
      FROM (SELECT generate_series(min(date_trunc('hour', ts)), max(ts), interval '5 min') as t5
            FROM device_history_20865735
           ) d
      WHERE t5 between '2015-05-13 09:00' and '2015-05-14 09:00'   
     )
SELECT d1.t5 || '.000000' as ts,
       avg(d1.sensorvalue) as sensorvalue1
       , avg(d2.sensorvalue) as sensorvalue2
FROM (SELECT grid.t5, avg(t.sensorvalue) as sensorvalue
      FROM grid LEFT JOIN
           device_history_20865735 t
           ON t.ts >= grid.t5 AND t.ts <grid.t5 +  interval '5 min' 
      GROUP BY grid.t5
     ) d1 LEFT JOIN
     (SELECT grid.t5, avg(t.sensorvalue) as sensorvalue
      FROM grid LEFT JOIN
           device_history_493417852 t
           ON t.ts >= grid.t5 AND t.ts <grid.t5 +  interval '5 min' 
     GROUP BY grid.t5
     ) d2 on d1.t5 = d2.t5
GROUP BY d1.t5
ORDER BY d1.t5;

答案 1 :(得分:0)

听起来你想要这样的事情:

(coalesce(value1,0) + coalesce(value2,0) + coalesce(value3,0)) /
(value1 IS NOT NULL::int + value2 IS NOT NULL::int + value3 IS NOT NULL::int)
AS average

基本上,只需要为每一行做数学运算。唯一的&#34;棘手&#34;部分是如何计算&#34;非空值 - 我使用了强制转换,但还有其他选项,例如:

CASE WHEN value1 IS NULL THEN 0 ELSE 1 END