2012-10-25 7 views
5

Ich habe eine Tabelle mit Entitätsname, Jahr und Aktivitätsnummer als unten. Während einiger Jahre gibt es keine Aktivität.Berechnen der laufenden Summe ab x Jahren vor

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 

Um es auf postgresql zu erstellen;

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); 

Ich mag die Gesamtzahl der in den letzten x Jahre haben, mit der Zahl dieses Jahr Aktivitäten für jede Einheit für jedes Jahr als Gebrüll.

Als Beispiel für x = drei Jahre.

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

Große Frage. Beispieldaten, erwartete Ausgabe, DDL. Es ist erwähnenswert, dass Stack Overflow die Registerkarten nicht speichert, so dass Ihre COPY-Ausgabe fehlerhaft war. Besser, "COPY ... CSV" zu verwenden. –

Antwort

3

Hier ist ein Ansatz, der die Fähigkeit, das sum Aggregat als Fensterfunktion mit einem bereichsbasierte Fensterrahmen zu verwenden, verwendet - siehe SUM(...) OVER (PARTITION BY name ORDER BY year ROWS 2 PRECEDING) und 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; 

Siehe SQLFiddle.

Die Notwendigkeit, Einträge für Jahre zu generieren, die selbst keinen Eintrag enthalten, erschweren diese Abfrage. Ich erzeuge eine Tabelle aller (Name, Jahr) Paare, dann left outer join entity_year_activity darauf, bevor ich die Fenstersumme mache, also werden alle Jahre für alle Namensmengen dargestellt. Deshalb ist das so kompliziert. Dann filtere ich das aggregierte Ergebnis, um Einträge mit Null in der Summe auszuschließen.

+0

Dies überspringt auch nicht das fehlende Jahr, also ist es nicht korrekt. –

+0

@GordonLinoff ... daher "noch". –

+0

@GordonLinoff Aktualisiert –

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

Wenn Sie dies beheben, indem Sie die 'where'-Klausel in die 'on'-Klausel verschieben und' en_key.year = en_sum.year' entfernen, dann wird es funktionieren. –

+0

Bearbeitet, um en_key.year = en_sum.year zu entfernen. Das besiegt den Zweck. Ich bin mir nicht sicher, ob die WHERE-Klausel in die ON-Klausel verschoben werden muss. Ich denke, es würde so oder so funktionieren. Stilistisch sah ich es nicht als Teil des Beitritts, sondern eher als Bedingung für den Bericht. Ich erwarte, dass es eine Frage der Meinung und der Vorliebe ist. –

+0

. . Als allgemeine Praxis sollten Join-Bedingungen in die On-Klausel gehen, also gehört sie ästhetisch eher dorthin als in die Wo-Klausel. Die Leistung der beiden Versionen sollte jedoch gleich sein. –

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

Dies funktioniert nicht, da es Jahre mit keinen Daten überspringt. –

+0

@GordonLinoff Neue Version tut. –

1

Ein weiterer Versuch. Dieser fehlt die 0-Reihe Jahre, obwohl:

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; 
Verwandte Themen