2012-11-05 7 views
5

Ich habe diese Tabelle:Durchschnitt über harte Partition definieren

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 

Und ich möchte diese Ausgabe:

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

Ich möchte die Nullwerte, sowie die einer vorherigen nicht Nullwert, durch den Durchschnitt des vorherigen Nicht-Null-Werts ersetzt werden, wenn die Tabelle nach Datum absteigend sortiert wird. In diesem Beispiel ist der Wert 15 der vorherige Nicht-Null-Wert der nächsten zwei Nullen. So 15/3 = 5.

SQL Fiddle

+0

+1 Sehr nette Frage. Und es hat alles, was es braucht - nun, ich schließe PostgreSQL 9.2 von der Geige ab. –

Antwort

4

ich eine überraschend einfache Lösung gefunden:

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

Da count() ignoriert NULL Werte, können Sie eine laufende Zählung (Standard in Fensterfunktion) verwenden um Werte schnell zu gruppieren (->grp).

Jede Gruppe hat genau einen Nicht-Null-Wert, so dass wir min/max/sum verwenden können, um zu demselben Ergebnis in einer anderen Fensterfunktion darüber zu gelangen. Dividiere durch die Anzahl der Mitglieder (count(*) dieses Mal, um NULL Werte zu zählen!) In der grp und wir sind fertig.

+0

Schön, aber scheint PostgreSQL-spezifisch. – jsalvata

+0

@jsalvata: "Aber"? Haben Sie das [PostgreSQL] -Tag bemerkt? Dies ist auch Standard-SQL. [-> ** SQLFiddle für SQL Server ** mit identischer Abfrage] (http://www.sqlfiddle.com/#!6/fb11e/1). –

+1

Nein, habe ich nicht. Crappy mySQL unterstützt dies nicht. Ja, es ist Standard. – jsalvata

1

wie ein Puzzle, das ist eine Lösung ... in der Praxis durchführen kann schrecklich abhängig von der Art Ihrer Daten. Schauen Sie sich Ihre Indizes in jedem Fall:

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) 

Erläuterung:

  • t1 ist die ursprüngliche Tabelle
  • t2 die Zeile in der Tabelle mit dem geringsten größer Datum mit einem Nicht-Null-Wert ist
  • t3 sind alle Zeilen dazwischen, so dass wir von den anderen Gruppe und

Leider kann ich nicht klarer sein zählen. Es ist verwirrend für mich auch :-)

+0

Wenn es zu kompliziert zu erklären ist, sind die Chancen, es ist einfach zu kompliziert. :) –

+0

In der Tat. Sieht nach Clodoaldos Bearbeitung fast verständlich aus. – jsalvata

Verwandte Themen