2012-11-05 10 views
5

mam poniższej tabeli:Średnie ponad trudne do zdefiniowania partycji

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 

I chcę tego wyjścia:

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

Chcę wartości null, a także jeden poprzedni non wartość zerową, zostać zastąpione przez średnią poprzedniej wartości nie pustej, gdy tabela jest uporządkowana według daty malejącej. W tym przykładzie wartość 15 jest poprzednią wartością niezerową dwóch następnych wartości null. Więc 15/3 = 5.

SQL Fiddle

+0

+1 Bardzo ładne pytanie. I ma wszystko, czego potrzebuje - cóż, wywnioskuję PostgreSQL 9.2 z skrzypiec. –

Odpowiedz

4

znalazłem zaskakująco proste rozwiązanie:

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

Od count() ignoruje NULL wartości, można użyć uruchomiony licznik (domyślne funkcja okna) szybko grupować wartości (->grp).

Każda grupa ma dokładnie jedną wartość inną niż null, więc możemy użyć min/max/sum, aby uzyskać ten sam wynik w innej funkcji okna. Podziel przez liczbę członków (count(*) tym razem, aby policzyć wartości NULL!) W grp i gotowe.

+0

Nice, ale wydaje się specyficzne dla PostgreSQL. – jsalvata

+0

@jsalvata: "Ale"? Czy zauważyłeś tag [PostgreSQL]? Jest to również standardowy SQL. [-> ** sqlfiddle dla serwera SQL ** z identycznym zapytaniem] (http://www.sqlfiddle.com/#!6/fb11e/1). –

+1

Nie, nie zrobiłem tego. Crappy mySQL go nie obsługuje. Tak, to jest standard. – jsalvata

1

jak puzzle, jest to rozwiązanie ... w praktyce może to wykonać strasznie w zależności od rodzaju danych. Oglądaj swoje indeksy, w każdym razie:

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) 

Objaśnienie:

  • t1 jest oryginalny stół
  • t2 jest wiersz w tabeli z najmniejszą większej daty z niepuste wartości
  • t3 są wszystkie wiersze pomiędzy, więc możemy grupa przez innych i liczyć

Niestety nie mogę być bardziej przejrzyste. Jest to mylące dla mnie zbyt :-)

+0

Jeśli jest to zbyt skomplikowane, aby wyjaśnić, są szanse, jest to zbyt skomplikowane. :) –

+0

Rzeczywiście. Wygląda prawie zrozumiale po edycji Clodoaldo. – jsalvata