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

テーブルが日付の降順で並べ替えられている場合、null 値と 1 つの前の非 null 値を、前の非 null 値の平均に置き換えたいと考えています。この例では、値 15 は、次の 2 つの null の前の非 null 値です。したがって、15 / 3 = 5 です。

SQL フィドル

4

2 に答える 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)。

すべてのグループにはnull以外の値が1つだけあるため、min / max / sumを使用して、その上にある別のウィンドウ関数で同じ結果を得ることができます。count(*)のメンバー数(今回はNULL値をカウントします!)で除算するgrpと完了です。

于 2012-11-05T19:12:29.593 に答える
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 は、null 以外の値を持つ最小の日付を持つテーブル内の行です。
  • t3 はすべて間にある行なので、他の行でグループ化してカウントできます

申し訳ありませんが、明確にすることはできません。私にとっても紛らわしいです:-)

于 2012-11-05T18:34:14.670 に答える