按日期进行有条件的分组

时间:2015-11-19 20:07:07

标签: sql postgresql aggregate

我有点麻烦这个问题。

我有两张表itemsstocks

items
id | name 
1  | item_1    
2  | item_2    

stocks
id | item_id | quantity | expired_on
1  |    1    |    5     |  2015-11-12
2  |    1    |    5     |  2015-11-13
3  |    2    |    5     |  2015-11-12
4  |    2    |    5     |  2015-11-14

我希望能够检索按日期分组的大表,并且对于每个日期,按item_id分组,并显示未过期的数量总和。

result
date        | item_id | unexpired 
2015-11-11  |    1    |    10     
2015-11-11  |    2    |    10     
2015-11-12  |    1    |    5     
2015-11-12  |    2    |    5     
2015-11-13  |    1    |    0     
2015-11-13  |    2    |    5     
2015-11-14  |    1    |    0     
2015-11-14  |    2    |    0

如果只有一天,我就能检索到结果

SELECT 
  items.id, SUM(stocks.quantity) as unexpired
FROM 
  items LEFT OUTER JOIN stocks 
  ON items.id = stocks.item_id
WHERE 
  stocks.expired_on > '2015-11-11'
GROUP BY
  items.id, stocks.quantity

我四处搜索,找到了一个名为DatePart的东西,但它看起来不像我需要的东西。

2 个答案:

答案 0 :(得分:5)

使用从booleaninteger的方便演员,产生0,1或null,仅对未到期的

求和
select
    to_char(d, 'YYYY-MM-DD') as date,
    item_id, 
    sum(quantity * (expired_on > d)::int) as unexpired
from
    stocks
    cross join 
    generate_series(
        '2015-11-11'::date, '2015-11-14', '1 day'
    ) d(d)
group by 1, 2
order by 1, 2
;
    date    | item_id | unexpired 
------------+---------+-----------
 2015-11-11 |       1 |        10
 2015-11-11 |       2 |        10
 2015-11-12 |       1 |         5
 2015-11-12 |       2 |         5
 2015-11-13 |       1 |         0
 2015-11-13 |       2 |         5
 2015-11-14 |       1 |         0
 2015-11-14 |       2 |         0

cross join generate_series提供给定范围内的所有日期。

上面使用的数据:

create table stocks (
    id int,
    item_id int,
    quantity int,
    expired_on date
);
insert into stocks (id,item_id,quantity,expired_on) values
(1,1,5,'2015-11-12'),
(2,1,5,'2015-11-13'),
(3,2,5,'2015-11-12'),
(4,2,5,'2015-11-14');

答案 1 :(得分:1)

您需要生成日期列表,然后使用交叉联接来获取日期和项目的完整组合。然后,库存表的left join表示每个日期到期。累计和 - 反向计算unexpired

select d.dte, i.item_id,
       sum(quantity) over (partition by i.item_id
                           order by d.dte desc
                           rows between unbounded preceding and 1 preceding
                          ) as unexpired
from (select generate_series(min(expired_on) - interval '1 day', max(expired_on), interval '1 day') as dte
       from stocks
      ) d cross join
      items i left join
      stocks s
      on d.dte = s.expired_on and i.item_id = s.item_id;