2012-11-05 48 views
5

我有这个表:平均值很难定义分区

create table t (value int, dt date); 

value |  dt  
-------+------------ 
    10 | 2012-10-30 
    15 | 2012-10-29 
    null | 2012-10-28 
    null | 2012-10-27 
    7 | 2012-10-26 

而且我想这样的输出:

value |  dt  
-------+------------ 
    10 | 2012-10-30 
    5 | 2012-10-29 
    5 | 2012-10-28 
    5 | 2012-10-27 
    7 | 2012-10-26 

我想要的空值,还有一个先前的非空值,当表格按日期降序排列时,用前面非空值的平均值替换。在这个例子中,值15是接下来的两个空值的前一个非空值。因此,15/3 = 5

SQL Fiddle

+0

+1非常好的问题。它拥有它需要的一切 - 好吧,我从小提琴中推断出PostgreSQL 9.2。 –

回答

4

我发现了一个令人惊讶的简单的解决方案:

SELECT max(value) OVER (PARTITION BY grp) 
    /count(*) OVER (PARTITION BY grp) AS value 
     ,dt 
FROM (
    SELECT *, count(value) OVER (ORDER BY dt DESC) AS grp 
    FROM t 
    ) a; 

-> sqlfiddle

由于count()忽略NULL值,你可以使用(在窗口函数默认)运行计数快速分组值( - >grp)。

每组都有一个非空值,所以我们可以使用min/max/sum在另一个窗口函数中得到相同的结果。在grp之间除以成员数(count(*)这次计数NULL的值!),结束了。

+0

不错,但似乎PostgreSQL特定。 – jsalvata

+0

@jsalvata:“但”?你有没有注意到[PostgreSQL]标签?另外,这是标准的SQL。 [ - > ** sqlfiddle for SQL server ** with identical query](http://www.sqlfiddle.com/#!6/fb11e/1)。 –

+1

不,我没有。蹩脚的mySQL不支持它。是的,这是标准的。 – jsalvata

1

作为一个谜,这是一个解决方案...在实践中,可根据您的数据的性质可怕执行。注意你的索引,在任何情况下:

create database tmp; 
create table t (value float, dt date); -- if you use int, you need to care about rounding 
insert into t values (10, '2012-10-30'), (15, '2012-10-29'), (null, '2012-10-28'), (null, '2012-10-27'), (7, '2012-10-26'); 

select t1.dt, t1.value, t2.dt, t2.value, count(*) cnt 
from t t1, t t2, t t3 
where 
    t2.dt >= t1.dt and t2.value is not null 
    and not exists (
     select * 
     from t 
     where t.dt < t2.dt and t.dt >= t1.dt and t.value is not null 
    ) 
    and t3.dt <= t2.dt 
    and not exists (
     select * 
     from t where t.dt >= t3.dt and t.dt < t2.dt and t.value is not null 
    ) 
group by t1.dt; 

+------------+-------+------------+-------+-----+ 
| dt   | value | dt   | value | cnt | 
+------------+-------+------------+-------+-----+ 
| 2012-10-26 |  7 | 2012-10-26 |  7 | 1 | 
| 2012-10-27 | NULL | 2012-10-29 | 15 | 3 | 
| 2012-10-28 | NULL | 2012-10-29 | 15 | 3 | 
| 2012-10-29 | 15 | 2012-10-29 | 15 | 3 | 
| 2012-10-30 | 10 | 2012-10-30 | 10 | 1 | 
+------------+-------+------------+-------+-----+ 
5 rows in set (0.00 sec) 

select dt, value/cnt 
from (
    select t1.dt , t2.value, count(*) cnt 
    from t t1, t t2, t t3 
    where 
     t2.dt >= t1.dt and t2.value is not null 
     and not exists (
      select * 
      from t 
      where t.dt < t2.dt and t.dt >= t1.dt and t.value is not null 
     ) 
    and t3.dt <= t2.dt 
    and not exists (
     select * 
     from t 
     where t.dt >= t3.dt and t.dt < t2.dt and t.value is not null 
    ) 
    group by t1.dt 
) x; 

+------------+-----------+ 
| dt   | value/cnt | 
+------------+-----------+ 
| 2012-10-26 |   7 | 
| 2012-10-27 |   5 | 
| 2012-10-28 |   5 | 
| 2012-10-29 |   5 | 
| 2012-10-30 |  10 | 
+------------+-----------+ 
5 rows in set (0.00 sec) 

说明:

  • T1是原始表
  • T2是与非空值
  • 最少的更大的日期表中的行
  • T3成为之间的所有行,因此我们可以通过其他组和计数

对不起,我不能再清楚不过了。这是混淆对我来说太:-)

+0

如果解释太复杂,很可能是,它太复杂了。 :) –

+0

确实。 Clodoaldo的编辑看起来几乎可以理解。 – jsalvata