Reputation: 1930
I'm having a bit trouble figure this one out.
I have two tables items
and stocks
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
I want to be able to retrieve a big table grouped by date, and for each date, group by item_id and show the sum of the quantity that's not expired.
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
I'm able to retrieve the result if it's just one day
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
I searched around, found something called DatePart, but it doesn't seem like what I need.
Upvotes: 4
Views: 105
Reputation: 125214
Using the convenient cast from boolean
to integer
, which yields 0, 1 or null, to sum the unexpired only
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
The cross join
to the generate_series
supplies all dates in the given range.
The data used above:
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');
Upvotes: 5
Reputation: 1269563
You need to generate the list of dates and then use cross join to get the full combinations of dates and items. Then, a left join
to the stock table gives the expired on each date. A cumulative sum -- in reverse -- calculated 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;
Upvotes: 1