Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reset a cumulative sum?

Tags:

oracle

I have the following dataset (table: stk):

S_Date       Qty     OOS (Out of Stock - 1 true, 0 false)
01/01/2013   0       1
02/01/2013   0       1
03/01/2013   0       1
04/01/2013   5       0
05/01/2013   0       1
06/01/2013   0       1

And what I want is:

S_Date       Qty     Cumulative_Days_OOS
01/01/2013   0       1
02/01/2013   0       2
03/01/2013   0       3
04/01/2013   5       0  -- No longer out of stock
05/01/2013   0       1
06/01/2013   0       2

The closest I've got so far is the following SQL:

SELECT
  S_DATE, QTY, 
  SUM(OOS) OVER (PARTITION BY OOS ORDER BY S_DATE) CUMLATIVE_DAYS_OOS
FROM
  STK
GROUP BY
  S_DATE, QTY, OOS
ORDER BY
  1

This gives me the following output:

S_Date       Qty     Cumulative_Days_OOS
01/01/2013   0       1
02/01/2013   0       2
03/01/2013   0       3
04/01/2013   5       0
05/01/2013   0       4
06/01/2013   0       5

It is close to what I want, but understandably, the sum is continued. Is it possible to reset this cumulative sum and start it again?

I've tried searching around on stackoverflow and google, but I'm not really sure what I should be searching for.

Any help much appreciated.

like image 996
Lyrical Avatar asked Dec 03 '13 10:12

Lyrical


People also ask

How do you reverse a cumulative sum in R?

Reverse cumulative sum of a column is calculated using rev() and cumsum() function. cumsum() function takes up column name as argument which computes the cumulative sum of the column and it is passed to rev() function which reverses the cumulative sum as shown below.

What is meant by cumulative sum?

Cumulative sums, or running totals, are used to display the total sum of data as it grows with time (or any other series or progression). This lets you view the total contribution so far of a given measure against time.

How do you do a cumulative sum in SQL?

In SQL server also you can calculate cumulative sum by using sum function. We can use same table as sample table. select dept_no Department_no, count(empno) Employee_Per_Dept, sum(count(*)) over (order by deptno) Cumulative_Total from [DBO].


2 Answers

You need to identify groups of consecutive days where oos = 1 or 0. This can be done by using LAG function to find when oos column changes and then summing over it.

with x (s_date,qty,oos,chg) as (
  select s_date,qty,oos,
         case when oos = lag(oos,1) over (order by s_date)
                then 0
                else 1
         end
  from stk
  )
select s_date,qty,oos,
       sum(chg) over (order by s_date) grp
from x;

output :

|                         S_DATE | QTY | OOS | GRP |
|--------------------------------|-----|-----|-----|
| January, 01 2013 00:00:00+0000 |   0 |   1 |   1 |
| January, 02 2013 00:00:00+0000 |   0 |   1 |   1 |
| January, 03 2013 00:00:00+0000 |   0 |   1 |   1 |
| January, 04 2013 00:00:00+0000 |   5 |   0 |   2 |
| January, 05 2013 00:00:00+0000 |   0 |   1 |   3 |
| January, 06 2013 00:00:00+0000 |   0 |   1 |   3 |

Then, you can sum over this oos, partitioned by grp column to get consecutive oos days.

with x (s_date,qty,oos,chg) as (
  select s_date,qty,oos,
         case when oos = lag(oos,1) over (order by s_date)
                then 0
                else 1
         end
  from stk
  ),
y (s_date,qty,oos,grp) as (
  select s_date,qty,oos,
         sum(chg) over (order by s_date)
  from x
  )
select s_date,qty,oos,
       sum(oos) over (partition by grp order by s_date) cum_days_oos
from y;

output:

|                         S_DATE | QTY | OOS | CUM_DAYS_OOS |
|--------------------------------|-----|-----|--------------|
| January, 01 2013 00:00:00+0000 |   0 |   1 |            1 |
| January, 02 2013 00:00:00+0000 |   0 |   1 |            2 |
| January, 03 2013 00:00:00+0000 |   0 |   1 |            3 |
| January, 04 2013 00:00:00+0000 |   5 |   0 |            0 |
| January, 05 2013 00:00:00+0000 |   0 |   1 |            1 |
| January, 06 2013 00:00:00+0000 |   0 |   1 |            2 |

Demo at sqlfiddle.

like image 153
Noel Avatar answered Oct 12 '22 18:10

Noel


First we need to divide rows to groups. In this case you can use the count of 0 values before current row as a group number. And then you can use SUM() OVER for these groups. To get 0 for OOS = 0 you can use CASE statement or just OOS*SUM(OOS) as soon as OOS = (0,1)

Something like this:

select T1.*,

OOS*SUM(OOS) OVER (PARTITION BY GRP ORDER BY S_DATE) CUMLATIVE_DAYS_OOS

FROM
(
select T.*,
(select count(*) from STK where S_Date<T.S_Date and OOS=0) GRP
FROM STK T
)  T1 
ORDER BY S_Date

SQLFiddle demo

like image 36
valex Avatar answered Oct 12 '22 19:10

valex