2012-10-25 15 views
5

エンティティ名、年、アクティビティ番号が となっているテーブルがあります。何年もの間、何の活動もありません。x年前からの計算実行合計

name | year | act_num 
-----+------+--------- 
aa | 2000 |  2 
aa | 2001 |  6 
aa | 2002 |  9 
aa | 2003 |  15 
aa | 2005 |  17 
b | 2000 |  3 
b | 2002 |  4 
b | 2003 |  9 
b | 2005 |  12 
b | 2006 |  2 

postgresqlで作成する。

CREATE TABLE entity_year_activity (
name character varying(10), 
year integer, 
act_num integer 
); 

INSERT INTO entity_year_activity 
VALUES 
    ('aa', 2000, 2), 
    ('aa', 2001, 6), 
    ('aa', 2002, 9), 
    ('aa', 2003, 15), 
    ('aa', 2005, 17), 
    ('b', 2000, 3), 
    ('b', 2002, 4), 
    ('b', 2003, 9), 
    ('b', 2005, 12), 
    ('b', 2006, 2); 

私は怒鳴るように毎年のために各エンティティの今年の活動の 数の過去のx年間の合計数を持っていると思います。

x = 3年の例として。

name | year | act_num | total_3_years 
-----+------+---------+--------------- 
aa | 2000 |  2 |  2 
aa | 2001 |  6 |  8 
aa | 2002 |  9 |  17 
aa | 2003 |  15 |  30 
aa | 2004 |  0 |  24 
aa | 2005 |  17 |  32 
b | 2000 |  3 |  3 
b | 2001 |  0 |  3 
b | 2002 |  4 |  7 
b | 2003 |  9 |  13 
b | 2005 |  12 |  21 
b | 2006 |  2 |  14 
+2

大きな質問です。サンプルデータ、期待される出力、DDLしかし、スタックオーバーフローはタブを保持しないので、あなたの 'COPY'アウトプットはマングルされてしまいます。 'COPY ... CSV'を使うのが良いです。 –

答えて

3

ここ範囲ベースの窓枠と窓関数としてsum骨材を使用する能力を使用するアプローチだ - 参照SUM(...) OVER (PARTITION BY name ORDER BY year ROWS 2 PRECEDING)window framingは。

WITH name_years(gen_name, gen_year) AS (
    SELECT gen_name, s 
    FROM generate_series(
    (SELECT min(year) FROM entity_year_activity), 
    (SELECT max(year) FROM entity_year_activity) 
) s CROSS JOIN (SELECT DISTINCT name FROM entity_year_activity) n(gen_name) 
), 
windowed_history(name, year,act_num,last3_actnum) AS (
    SELECT 
    gen_name, gen_year, coalesce(act_num, 0), 
    SUM(coalesce(act_num,0)) OVER (PARTITION BY gen_name ORDER BY gen_year ROWS 2 PRECEDING) 
    FROM name_years 
    LEFT OUTER JOIN entity_year_activity ON (gen_name = name AND gen_year = year) 
) 
SELECT name, year, act_num, sum(last3_actnum) as total_3_years 
FROM windowed_history 
GROUP BY name, year, act_num 
HAVING sum(last3_actnum) <> 0 
ORDER BY name, year; 

SQLFiddleを参照してください。

エントリのない年のエントリを生成する必要があるため、このクエリが複雑になります。私はすべての(名前、年)のペアのテーブルを生成し、次にウィンドウの合計を行う前にleft outer join entity_year_activityを生成するので、すべての名前セットのすべての年が表現されます。だから、これはとても複雑です。次に集計結果をフィルタリングして、合計でゼロのエントリを除外します。

+0

これも欠落した年をスキップしないので、正しくありません。 –

+0

@GordonLinoff ...したがって "まだ"。 –

+0

@GordonLinoff更新 –

2
SELECT en_key.name, en_key.year, en_key.act_num, SUM(en_sum.act_num) as total_3_years 
FROM entity_year_activity en_key 
    INNER JOIN entity_year_activity en_sum 
    ON en_key.name = en_sum.name 
WHERE en_sum.year BETWEEN en_key.year - 2 AND en_key.year 
GROUP BY en_key.name, en_key.year 
+0

'where'節を' on'節に移動し、 'en_key.year = en_sum.year'を削除することでこれを解決すれば、それは動作します。 –

+0

編集してen_key.year = en_sum.yearを削除します。それは目的を破るものです。 where句をon句に移動する必要があるかどうかはわかりません。私はそれがいずれかの方法で動作すると思います。文体的には、私はそれを結合の一部として見るのではなく、むしろレポート上の条件と見なしました。私はそれが意見と好みの問題だと思っています。 –

+0

。 。一般的な慣習として、結合条件は 'on'節に入れなければならないので、' where'節ではなく意図的にそこに属します。ただし、2つのバージョンのパフォーマンスは同じにする必要があります。 –

3

SQL Fiddle

select 
    s.name, 
    d "year", 
    coalesce(act_num, 0) act_num, 
    coalesce(act_num, 0) 
    + lag(coalesce(act_num, 0), 1, 0) over(partition by s.name order by d) 
    + lag(coalesce(act_num, 0), 2, 0) over(partition by s.name order by d) 
    total_3_years 
from 
    entity_year_activity eya 
    right join (
     generate_series(
      (select min("year") from entity_year_activity), 
      (select max("year") from entity_year_activity) 
     ) d cross join (
     select distinct name 
     from entity_year_activity 
     ) f 
    ) s on s.name = eya.name and s.d = eya."year" 
order by s.name, d 
+0

データのない年をスキップしないため、これは機能しません。 –

+0

@GordonLinoff新しいバージョンがあります。 –

1

もう一度お試しください。しかし、これには0行年が欠けています:

select t1.name, t1.year, t1.act_num, 
     (select sum(t2.act_num) from entity_year_activity t2 
           where t2.year between t1.year - 2 and t1.year 
            and t2.name = t1.name) total 
from entity_year_activity t1;