GROUP BY由间隙分隔的连续日期

Mic*_*ski 10 sql postgresql aggregate window-functions

假设您(在Postgres 9.1中)有这样一个表:

date | value 
Run Code Online (Sandbox Code Playgroud)

它有一些空白(我的意思是:并非每个可能的日期在最小(日期)和最大(日期)之间有它的行).

我的问题是如何聚合这些数据,以便分别处理每个一致的组(没有间隙),如下所示:

min_date | max_date | [some aggregate of "value" column] 
Run Code Online (Sandbox Code Playgroud)

有什么想法怎么做?我相信这是可能的窗口功能,但是过了一段时间,试图lag()lead()我有点卡住了.

例如,如果数据是这样的:

 date          | value  
---------------+-------  
 2011-10-31    | 2  
 2011-11-01    | 8  
 2011-11-02    | 10  
 2012-09-13    | 1  
 2012-09-14    | 4  
 2012-09-15    | 5  
 2012-09-16    | 20  
 2012-10-30    | 10  
Run Code Online (Sandbox Code Playgroud)

输出(sum作为聚合)将是:

   min     |    max     |  sum  
-----------+------------+-------  
2011-10-31 | 2011-11-02 |  20  
2012-09-13 | 2012-09-16 |  30  
2012-10-30 | 2012-10-30 |  10  
Run Code Online (Sandbox Code Playgroud)

Clo*_*eto 10

create table t ("date" date, "value" int);
insert into t ("date", "value") values
    ('2011-10-31', 2),
    ('2011-11-01', 8),
    ('2011-11-02', 10),
    ('2012-09-13', 1),
    ('2012-09-14', 4),
    ('2012-09-15', 5),
    ('2012-09-16', 20),
    ('2012-10-30', 10);
Run Code Online (Sandbox Code Playgroud)

更简单,更便宜的版本:

select min("date"), max("date"), sum(value)
from (
    select
        "date", value,
        "date" - (dense_rank() over(order by "date"))::int g
    from t
) s
group by s.g
order by 1
Run Code Online (Sandbox Code Playgroud)

我的第一次尝试更复杂,更昂贵:

create temporary sequence s;
select min("date"), max("date"), sum(value)
from (
    select 
        "date", value, d,
        case 
            when lag("date", 1, null) over(order by s.d) is null and "date" is not null 
                then nextval('s')
            when lag("date", 1, null) over(order by s.d) is not null and "date" is not null 
                then lastval()
            else 0 
        end g
    from 
        t
        right join
        generate_series(
            (select min("date") from t)::date, 
            (select max("date") from t)::date + 1, 
            '1 day'
        ) s(d) on s.d::date = t."date"
) q
where g != 0
group by g
order by 1
;
drop sequence s;
Run Code Online (Sandbox Code Playgroud)

输出:

    min     |    max     | sum 
------------+------------+-----
 2011-10-31 | 2011-11-02 |  20
 2012-09-13 | 2012-09-16 |  30
 2012-10-30 | 2012-10-30 |  10
(3 rows)
Run Code Online (Sandbox Code Playgroud)