Cumulative sum in SQL using window function - sql

QTY
STOCK
RNK
ID KEY
CUM SUM
40
35
1
1
35
20
35
2
1
0
15
35
3
1
0
58
35
4
1
0
18
35
5
1
0
40
35
1
2
35
20
35
2
2
0
15
35
3
2
0
CUM SUM should be MIN(QTY, STOCK-SUM(all rows in cumsum before the current row)) for every other row and for 1st row it should be MIN(QTY, STOCK-SUM(0))=> MIN(QTY,STOCK)
QTY
STOCK
RNK
ID KEY
CUM SUM
40
35
1
1
5
20
35
2
1
-10
15
35
3
1
-30
58
35
4
1
-7
18
35
5
1
-24
40
35
1
2
5
20
35
2
2
-10
15
35
3
2
-30
After, I tried I am getting the above output
SELECT sum(qty-stock) over (
partition by ID KEY
ORDER BY rnk
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as CUM SUM
FROM TABLE
Need to get correct cumsum value using a window function in the existing table

You may use a rolling SUM() here, using SUM() as an analytic function:
SELECT *, SUM(QTY - STOCK) OVER (PARTITION BY ID_KEY ORDER BY RNK) AS CUM_SUM
FROM yourTable
ORDER BY ID_KEY, RNK;

Related

running total starting from a date column

I'm trying to get a running total as of a date. This is the data I have
Date
transaction Amount
End of Week Balance
jan 1
5
100
jan 2
3
100
jan 3
4
100
jan 4
3
100
jan 5
1
100
jan 6
3
100
I would like to find out what the daily end balance is. My thought is to get a running total from each day to the end of the week and subtract it from the end of week balance, like below
Date
transaction Amount
Running total
End of Week Balance
Balance - Running total
jan 1
5
19
100
86
jan 2
3
14
100
89
jan 3
4
11
100
93
jan 4
3
7
100
96
jan 5
1
4
100
97
jan 6
3
3
100
100
I can use
SUM(transactionAmount) OVER (Order by Date)
to get a running total, is there a way to specify that I only want the total of transactions that have taken place after the date?
You can use sum() as a window function, but accumulate in reverse:
select t.*,
(end_of_week_balance -
sum(transactionAmount) over (order by date desc)
)
from t;
If you have this example:
1> select i, sum(i) over (order by i) S from integers where i<10;
2> go
i S
----------- -----------
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
9 45
you can also do:
1> select i, sum(case when i>3 then i else 0 end) over (order by i) S from integers where i<10;
2> go
i S
----------- -----------
1 0
2 0
3 0
4 4
5 9
6 15
7 22
8 30
9 39

Update sequence in table - Oracle

I have a table:
table1
u_a_id element_id my_seq line_num
1 HI01-01 1 30
1 HI01-02 1 30
1 HI01-01 1 31
1 HI01-02 1 31
1 HI02-03 1 31
1 HI02-04 1 31
2 HI01-01 1 40
2 HI01-02 1 40
2 HI02-01 1 40
2 HI02-02 1 40
2 HI02-03 1 40
2 HI02-04 1 40
2 HI03-02 1 41
2 HI03-03 1 41
2 HI05-04 1 41
2 HI05-05 1 41
I need to update my_seq if a new HI01 appears in the same u_a_id or the counter for HI changes, for ex. HI01 -> HI02 for each u_a_id order by line_num.
I have this query, however this gives seq as 1 even for second instance of HI01-01 in u_a_id = 1:
select t.*,
dense_rank() over (partition by u_a_id order by substr(element_id, 1, 4)) as new_my_seq
from table1 t
The output would look like:
u_a_id element_id my_seq line_num
1 HI01-01 1 30
1 HI01-02 1 30
1 HI01-01 2 31
1 HI01-02 2 31
1 HI02-03 3 31
1 HI02-04 3 31
2 HI01-01 1 40
2 HI01-02 1 40
2 HI02-01 2 40
2 HI02-02 2 40
2 HI02-03 2 40
2 HI02-04 2 40
2 HI03-02 3 41
2 HI03-03 3 41
2 HI05-04 4 41
2 HI05-05 4 41
Is there a way in Oracle SQL to achieve this?
I think you just want:
select t.*,
dense_rank() over (partition by u_a_id
order by line_num,
substr(element_id, 1, 4)
) as my_seq
from t;
You may want to order by line_num instead of substr(element_id, 1, 4) and place substr(element_id, 1, 4) in partition by.
select t.*,
dense_rank() over (partition by u_a_id, substr(element_id, 1, 4) order by line_num) as new_my_seq
from table1 t

Flipping array in SQL

My original table looks like:
id, date, 1, 2, 3,
1 1 10 10 10
1 2 20 20 20
1 3 30 30 30
1 4 15 15 15
By running the query:
ARRAY_AGG(STRUCT(1, 2, 3)) OVER (PARTITION BY id ORDER BY date ROWS BETWEEN 2
PRECEDING AND CURRENT ROW)
I get this output
1 2 3
1 10 10 10
2 10 10 10
20 20 20
3 10 10 10
20 20 20
30 30 30
4 20 20 20
30 30 30
15 15 15
I would like the output to be:
1 2 3
1 10 10 10
2 20 20 20
10 10 10
3 30 30 30
20 20 20
10 10 10
4 15 15 15
30 30 30
20 20 20
So bascically, the values that I get in my output are all correct, but I would like order of the output to be flipped. Anyone know how to do that with an array(struct)) type of column?
One option is to reverse the sort order of the window clause, or you could use a simple solution of calling the ARRAY_REVERSE function:
ARRAY_REVERSE(
ARRAY_AGG(STRUCT(col1, col2, col3)) OVER (
PARTITION BY id ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
)
Added below for the sake of having reverse the sort order of the window clause option here
ARRAY_AGG(STRUCT(col1, col2, col3)) OVER(
PARTITION BY id ORDER BY date DESC
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)

table name t 10 20 30 40 want output as 10 30 50 70 in sql

t 10 20 30 40
output 10 30 50 70
One method of doing this is a cumulative sum with a window function:
select col,
sum(col) over (order by col
rows between 1 preceding and current row
)
from t;

Find duplicates on multiple columns in a SQL

I want to find the batchID who are having subjectID(0,1,2) only, pls help me, thanks in advance
I need answer BatchID=12 and BatchID=51, what can I do in sql
MyTable
uid BatchID SubjectID
6 2 0
7 2 1
8 2 2
9 2 4
10 3 0
11 3 1
12 4 5
13 4 0
14 5 5
15 6 5
17 7 0
18 7 1
19 7 2
26 12 0
27 12 1
28 12 2
29 1 0
30 1 1
31 1 4
62 45 5
63 46 0
64 46 1
65 46 4
107 49 6
108 49 2
109 49 4
110 50 1
111 50 3
116 0 1
117 0 4
118 51 0
119 51 1
120 51 2
You can use conditional aggregation for this:
select batchId
from your_table
group by batchId
having count(distinct case when subjectID in (0,1,2) then subjectID end) = 3
and count(case when subjectID not in (0,1,2) then 1 end) = 0
Explanation:
Group by batchId - Aggregate on batchId
count(distinct case when subjectID in (0,1,2) then subjectID end) - Produces 3 only if all three of them are present for this batchId
count(case when subjectID not in (0,1,2) then 1 end) - Produces 0 if there is no other subjectID except 0,1,2 assuming nulls are not allowed in the subjectId column.
You can use Row_Number()
;with cte as (
select *, RowN = row_number() over (partition by batchid order by uid) from #yourbatch where subjectid in (0,1,2)
) select distinct BatchId from cte where RowN > 1