How to reduce the value of a column in a row with? - sql

I have a table with two columns:
No Value
1 20
2 10
3 50
4 35
5 17
I also have a variable or parameter where the variables will reduce the value of a column in a row.
So, if my variable V = 5 then my column will update:
No Value
1 15
2 10
3 50
4 35
5 17
Or if V = 50 then:
No Value
1 0
2 0
3 30
4 35
5 17
How can I do that?

First prepare the structure and the data:
CREATE TABLE TAB
(
[No] int identity(1,1) primary key,
[Value] int
);
INSERT INTO TAB VALUES (20);
INSERT INTO TAB VALUES (10);
INSERT INTO TAB VALUES (50);
INSERT INTO TAB VALUES (35);
INSERT INTO TAB VALUES (17);
So now we define your variable to reduce the [Value]:
DECLARE #var int
SET #var = 5
And now you can query your table:
SELECT [No], CASE WHEN [Value] - #var < 0 THEN 0 ELSE [Value] - #var END AS [Value]
FROM TAB
Very easy. You can set your variable to 50 an try again.
Here is a fiddle for this example.

Use CTE can be one of the options
declare #Values table
(
[No] int identity(1,1) not null,
Value int not null
)
declare #DeductAmount int = 50
INSERT #Values VALUES (20), (10), (50), (35), (17)
;WITH cte AS
(
SELECT *,
#DeductAmount - CASE WHEN Value >= #DeductAmount THEN #DeductAmount ELSE Value END AS RemainDeductAmount,
Value - CASE WHEN Value >= #DeductAmount THEN #DeductAmount ELSE Value END DeductedValue
FROM #Values WHERE [No] = 1
UNION ALL
SELECT
v.*,
cte.RemainDeductAmount - CASE WHEN v.Value >= cte.RemainDeductAmount THEN cte.RemainDeductAmount ELSE v.Value END,
v.Value - CASE WHEN v.Value >= cte.RemainDeductAmount THEN cte.RemainDeductAmount ELSE v.Value END DeductedValue
FROM #Values v INNER JOIN cte ON v.[No] = cte.[No] + 1
)
UPDATE target SET Value = DeductedValue
FROM #Values target INNER JOIN cte ON target.[No] = cte.[No]
SELECT * FROM #Values
The secret inside the cte
No Value RemainDeductAmount DeductedValue
----------- ----------- ------------------ -------------
1 20 30 0
2 10 20 0
3 50 0 30
4 35 0 35
5 17 0 17

Change 50 with your variable
Query:
SELECT t.No,
CASE WHEN SUM(t.Value)over(ORDER BY t.No) - 50 <0 THEN 0
WHEN SUM(t2.Value)over(ORDER BY t2.No) > 50
AND SUM(t.Value)over(ORDER BY t.No) >50 THEN t.Value
ELSE SUM(t.Value)over(ORDER BY t.No) - 50
END AS Value
FROM Table1 t
LEFT JOIN Table1 t2 ON t.No - 1 = t2.No

Here is another version:
DECLARE #t TABLE(ID INT IDENTITY(1, 1) , v INT )
DECLARE #x INT = 5
INSERT INTO #t VALUES ( 20 ), ( 10 ), ( 50 ), ( 35 ), ( 17 );
WITH cte
AS ( SELECT * ,
#x - ( SELECT ISNULL(SUM(v), 0) AS v
FROM #t t2
WHERE t2.ID <= t1.ID) s
FROM #t t1
)
SELECT ID ,
CASE WHEN s >= 0 THEN 0
WHEN s < 0 AND v + s > 0 THEN -s
ELSE v END
FROM cte

DECLARE #qty int
SET #qty= 50
WHILE #qty> 0
BEGIN
SELECT #qty= #qty- value
FROM table
WHERE no = (SELECT MIN(no) FROM table WHERE value > 0)
IF #qty< 0
BEGIN
UPDATE table
SET value = ABS(#qty)
WHERE (SELECT MIN(no) FROM table WHERE value > 0)
END
ELSE
BEGIN
UPDATE table
SET value = 0
WHERE (SELECT MIN(no) FROM table WHERE value > 0)
END
END

Related

How to effeciently update table that depends on previous records

My SQL Server table looks like this:
id Distance a b Grp
--------------------------------
1 0.0000000000 100 114 NULL
2 0.1000000000 64 125 NULL
3 0.1000000000 88 100 NULL
4 0.1000000000 65 125 NULL
5 0.1000000000 63 64 NULL
6 0.1000000000 65 66 NULL
7 0.2000000000 63 66 NULL
8 0.2000000000 10 61 NULL
9 0.2000000000 19 61 NULL
10 0.2000000000 30 61 NULL
11 0.2000000000 10 65 NULL
12 0.2000000000 10 94 NULL
13 0.2000000000 19 65 NULL
14 0.2000000000 19 94 NULL
15 0.2000000000 30 94 NULL
16 0.2000000000 60 94 NULL
17 0.2000000000 61 94 NULL
The Grp column should be filled as follows
first record Grp is 1
if the next row's values of a & b are in any of the previous rows, then it will take the first rows Grp value
if the next row's values of a & b are not in any of the previous rows, then Grp value will be max Grp + 1
if the record id = 3 then the value of b = 100 which exists in the previous rows, the first one it appears in was id = 1 which is Grp = 1 so the Grp will be 1 for id 3
This is what my table should look like:
id Distance a b Grp
--------------------------------
1 0.0000000000 100 114 1
2 0.1000000000 64 125 2
3 0.1000000000 88 100 1
4 0.1000000000 65 125 2
5 0.1000000000 63 64 2
6 0.1000000000 65 66 2
7 0.2000000000 63 66 2
8 0.2000000000 10 61 3
9 0.2000000000 19 61 3
10 0.2000000000 30 61 3
11 0.2000000000 10 65 2
12 0.2000000000 10 94 3
13 0.2000000000 19 65 2
14 0.2000000000 19 94 3
15 0.2000000000 30 94 3
16 0.2000000000 60 94 3
17 0.2000000000 61 94 3
I have built this script that works fine, but it's extremely slow, any way I can get it better (without a loop)?
DECLARE #T AS TABLE
(
id int IDENTITY,
Distance decimal(18, 10),
a int,
b int,
Grp int
)
INSERT INTO #T(Distance, a, b)
SELECT Distance, a, b
FROM MyTable
ORDER BY Distance
UPDATE #T
SET Grp = 1
WHERE id = 1
DECLARE #i int = 2, #max int, #min int,
#grp int, #a int, #b int, #maxgrp int = 1
SELECT #max = MAX(id) FROM #T
WHILE #i <= #max
BEGIN
SELECT #a = a, #b = b
FROM #T
WHERE id = #i
SELECT #min = MIN(id)
FROM #T
WHERE id < #i AND a IN (#a, #b) OR b IN (#a, #b)
SELECT #grp = grp
FROM #T
WHERE id = #min
IF #grp IS NULL
BEGIN
SET #maxgrp = #maxgrp + 1
SET #grp = #maxgrp
END
UPDATE #T
SET Grp = #grp
WHERE id = #i
SET #i = #i + 1
END
SELECT * FROM #T
An answer using recursion instead of a loop.
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=5410e95927e81d41f27f1d569b5d3cd3
First, identify the first row any value (from a or b) appears on...
CREATE TABLE #node(
row_id INT,
old_val INT,
new_val INT,
link_count INT
INDEX node_old_new CLUSTERED (link_count, old_val, new_val, row_id),
);
INSERT INTO
#node
SELECT
e.id, twinned.*, COUNT(*) OVER (PARTITION BY e.id)
FROM
#example AS e
CROSS APPLY
(
SELECT e.a AS old_val, e.b AS new_val
UNION ALL
SELECT e.b AS old_val, e.a AS new_val
)
AS twinned
WHERE
NOT EXISTS (
SELECT *
FROM #example AS lookup
WHERE twinned.new_val IN (lookup.a, lookup.b)
AND lookup.id < e.id
)
;
Where link_count = 2, both a and b appear for the first time on this row, which means this row will start a new group.
Where link_count = 1, new_val has never been seen previously, but old_val has been. So, once old_val has been assigned a group, we can propagate that group to new_val.
That's just a tree traversal creating a closure table.
WITH
closure AS
(
SELECT
new_val AS val,
DENSE_RANK() OVER (ORDER BY row_id) AS grp,
row_id AS row_id,
0 AS depth
FROM
#node
WHERE
link_count = 2
UNION ALL
SELECT
r.new_val,
c.grp,
r.row_id,
c.depth + 1
FROM
closure AS c
INNER JOIN
#node AS r
ON r.old_val = c.val
AND r.link_count = 1
)
Now, for any value in a or b we can look up the group for that value in the closure table. We'll likely get two different groups, one from looking up a and one from looking up b; so, we take the group that was allocated from the earliest row.
SELECT
e.*, g.grp
FROM
#example e
CROSS APPLY
(
SELECT TOP 1
c.grp
FROM
#closure AS c
WHERE
c.val IN (e.a, e.b)
ORDER BY
c.row_id
)
AS g
ORDER BY
e.id
If you only need Grp to group by it, it can be simplified this way:
declare #t table (id int identity, Distance decimal(18,10)
, a int, b int, Grp int)
insert #t (Distance, a, b)
-- select Distance, a, b From MyTable order by Distance
values
(0.0,100,114),(0.1, 64,125),(0.1, 88,100),(0.1, 65,125),
(0.1, 63, 64),(0.1, 65, 66),(0.2, 63, 66),(0.2, 10, 61),
(0.2, 19, 61),(0.2, 30, 61),(0.2, 10, 65),(0.2, 10, 94),
(0.2, 19, 65),(0.2, 19, 94),(0.2, 30, 94),(0.2, 60, 94),
(0.2, 61, 94)
declare #i int, #d float, #a int, #b int, #g int
declare c1 cursor for select * from #t for update of Grp
open c1
fetch next from c1 into #i, #d, #a, #b, #g
update #t set Grp = 1 where current of c1
fetch next from c1 into #i, #d, #a, #b, #g
while ##fetch_status = 0
begin
update #t set Grp =
isNull((select top 1 (Grp)
from #t t2
where t2.id < #i
and (#a in (t2.a , t2.b)
or #b in (t2.a , t2.b)))
, #i)
where current of c1
fetch next from c1 into #i, #d, #a, #b, #g
end
close c1
deallocate c1
-- If you need consecutive Grp numbers ..
declare #u table (id int identity, Grp int)
insert #u (Grp)
select distinct Grp from #t order by Grp
update #t set Grp = u.id
from #t t
join #u u on (u.Grp = t.Grp and u.Grp<>u.id)
select * from #t
The following query produces the output you want.
WITH t3 as(
SELECT *
FROM
(SELECT id,Distance,a,b,rnk,
CASE WHEN rnk > 0 THEN NULL ELSE grp END AS Grp,
Row_Number() OVER(ORDER BY id) AS seq
FROM
(SELECT id,Distance,a,b,rnk,ROW_NUMBER() OVER(PARTITION BY rnk ORDER BY id) AS grp
FROM
(SELECT id,Distance,a,b,
ISNULL((SELECT top 1 id FROM tb s2 WHERE s2.id < s1.id AND (s2.a = s1.a OR s2.b = s1.b OR s2.b = s1.a OR s2.a = s1.b)),0) AS rnk
FROM tb s1) T) T) T
WHERE Grp > 0
)
SELECT id,Distance,a,b,min(grp)
FROM
(SELECT distinct *
FROM
(SELECT t1.id,t1.Distance,t1.a,t1.b,t3.grp
FROM
(SELECT id,Distance,a,b,grp,
ISNULL((SELECT top 1 id FROM tb s2 WHERE s2.id < s1.id AND (s2.a = s1.a OR s2.b = s1.b OR s2.b = s1.a OR s2.a = s1.b)),0) AS rnk
FROM tb s1) t1
JOIN tb t2 ON t1.rnk = t2.id
JOIN t3 ON
t1.a = t3.a OR t1.a = t3.b OR
t1.b = t3.b OR t1.b = t3.a OR
t2.b = t3.b OR t2.b = t3.a OR
t2.a = t3.a OR t2.a = t3.b) t1
UNION ALL
SELECT id,Distance,a,b,grp
FROM t3) T
GROUP BY id,Distance,a,b
ORDER BY id
Demo in db<>fiddle

How to use #tmp Table in Loop in Recursive Function

I am trying to create a loop that when given a part id, it will search a table of assembly parts and explode all the parts into a large list.
It needs to be recursive because Part 123 may have parts 1, 2, 3, 4, 5 and parts 4 and 5 are also assembly items.
I thought I had come up with something pretty good and easily returns the part id and the part level for each item. Then I find out that I can't use temp tables, so it shoots my loop down.
What can I use in place of the temp table to give me the same function here?
CREATE FUNCTION [dbo].[fn_getParts] (
#source_part_id int
, #level int
)
RETURNS #parts_list TABLE (
[part] int NOT NULL,
[level] int NOT NULL
)
AS
BEGIN
DECLARE
#max int = 0,
#cnt int = 0,
#PID int = 0,
#Plvl int = 0,
#id int = 0
INSERT INTO #parts_list VALUES (#source_part_id, #level)
SET #level += 1
SELECT [Comp_Part_ID] AS [PID], #level AS [level]
INTO #chkParts
FROM [assemblies]
WHERE [Assy_PID] = #source_part_id
SELECT #max = COUNT(*) FROM #chkParts
WHILE #cnt <= #max
BEGIN
SELECT #PID = [PID], #Plvl = [level] FROM #chkParts
INSERT INTO #parts_list
SELECT * FROM [fn_getParts](#PID, #Plvl)
SET #cnt += 1
END
RETURN
END
Here's some sample data:
CREATE TABLE [Assemblies] (
[PartID] int,
[Comp_PartID] int
)
INSERT INTO [Assemblies] VALUES
(1,2),
(1,3),
(1,4),
(1,5),
(1,6),
(3,9),
(3,10),
(10,11),
(10,23),
(10,24),
(10,31),
(11,24),
(11,23)
If I enter SELECT * FROM [fn_getParts](1,0) I would expect the following:
part,level
1,0
2,1
3,1
4,1
9,2
10,2
11,3
23,3
24,3
The code can be simplified somewhat by wrapping an Inline Table-Valued Function around a Recursive CTE, e.g.:
create function dbo.fn_getParts (
#source_part_id int
)
returns table as return (
with PartsHierarchy as (
select #source_part_id as part, 0 as level
union all
select Comp_PartID, 1 + level
from Assemblies
join PartsHierarchy on part = PartID
)
select part, level
from PartsHierarchy
);
And then, invoking it for different part numbers...
select * from dbo.fn_getParts(1);
part level
---- ----
1 0
2 1
3 1
4 1
5 1
6 1
9 2
10 2
11 3
23 3
24 3
31 3
24 4
23 4
select * from dbo.fn_getParts(10);
part level
---- -----
10 0
11 1
23 1
24 1
31 1
24 2
23 2
select * from dbo.fn_getParts(11);
part level
---- -----
11 0
24 1
23 1

How to increment value when they are same

I have a data
By using this query I'm getting data like this
Select ID,Val,Premium,Row_number()OVER(PARTITION BY ID,Val ORDER BY ID) RN From Table1
Present Data
ID Val Premium RN
1 CH 201 1
1 CH 0 1
1 CHH 301 2
1 CHS 401 3
How can I make this data to like on the present Query
ID Val Premium RN
1 CH 201 1
1 CH 0 4
1 CHH 301 2
1 CHS 401 3
I just want to make the data which is same RN to increment to maximum number
Val = CH have RN = 1 then I want to make Premium = 0 record to RN = 4
In the present Row number I need to pick MAX(RN) and increment to that duplicate value
This should do the trick:
UPDATE table1
SET rn = (SELECT Max(rn) + 1
FROM table1)
WHERE id IN (SELECT id
FROM table1
WHERE rn IN (SELECT rn
FROM table1
GROUP BY rn
HAVING Count(*) > 1))
AND premium = 0;
Update the table. Setting the new RN value to the highest value+1. Only updating the rows where there are multiple RN values and where premium is 0.
--Try This
BEGIN TRAN
Declare #Strt INT,#End INT,#MaxNo INT
CREATE TABLE #TEMP_DATA (ID INT, VAL NVARCHAR(6),Premium INT)
INSERT INTO #TEMP_DATA
SELECT 1,'CH',201 UNION ALL
SELECT 1,'CH',0 UNION ALL
SELECT 1,'CHH',301 UNION ALL
SELECT 1,'CHS',401 UNION ALL
SELECT 1,'CHHS',501 UNION ALL
SELECT 1,'CHHS',0
SELECT ID,Val,Premium,DENSE_RANK()OVER(ORDER BY Val) RN INTO #T
FROM #TEMP_DATA
SET #Strt=1
SELECT #End= MAX(RN) FROM #T
WHILE #Strt<=#End BEGIN
SELECT #MaxNo=MAX(RN)+1 FROM #T
UPDATE #T SET RN= #MaxNo WHERE Premium=0 AND RN=#Strt
SET #Strt=#Strt+1
END
SELECT * FROM #T
ROLLBACK TRAN

How to use aggregate function in update in SQL server 2012

I Tried as shown below:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
Expected Output:
Table consists of employeeID's along with amount assigned to Employee I need to subtract amount from amount filed depending on employee usage. Amount "10" should be deducted from ID = 1 and amount "1" should be deducted from ID = 2.
Amount: Credits available for that particular employee depending on date.
So i need to reduce credits from table depending on condition first i need to subtract from old credits. In my condition i need to collect 11 rupees from empID = 1 so first i need to collect 10 rupee from ID=1 and 1 rupee from the next credit i.e ID=2. For this reason in my expected output for ID=1 the value is 0 and final output should be like
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
Need help to update records. Check error in my update statement.
Declare #Deduct int = -11,
#CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set #CurrentDeduct = case when abs(#Deduct) >= AMOUNT then Amount else abs(#Deduct) end
, #Deduct = #Deduct + #CurrentDeduct
,AMOUNT = AMOUNT - #CurrentDeduct
where EmpID= 1
I think you want the following: subtract amounts from 11 while remainder is positive. If this is true, here is a solution with recursive cte:
DECLARE #t TABLE ( id INT, amount INT )
INSERT INTO #t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM #t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM #t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM #t t
JOIN cte c ON c.id = t.id
SELECT * FROM #t
Output:
id amount
1 0
2 0
3 1
4 2
Here I use 17 as start remainder.
If you use sql server 2012+ then you can do it like:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM #t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
First you should get a cumulative sum on amount:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
From here we should put 0 on rows before running_sum exceeds the value 11. Update the row where the running sum exceeds 11 and do nothing to rows after precedent row.
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
From here we can do the update:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
See an SQLDFIDDLE

How can I avoid cursor in this situation?

I have a table with following columns
Period, WIP_Close, WIP_Open, WIP_Add, WIP_Minus.
I need to update
WIP_Open = WIP_Close for previous period
and then
update WIP_Close = WIP_Open + WIP_Add - WIP_Minus.
Currently I am using cursor as follows:
declare y_curs cursor for select distinct period
from abc
order by period
declare #period as int
declare #old_period as int
set #old_period = 0
open y_curs
fetch y_curs into #period
while ##fetch_status = 0
begin
update f set f.wip_open = isnull(f1.wip_close,0)
from abc f join abc f1 on 1=1
where f.period = #period and f1.period=#old_period
update abc set wip_close = (isnull(wip_open,0) + wip_add - wip_minus) where period = #period
set #old_period = #period
fetch y_curs into #period
end
close y_curs
deallocate y_curs
This is working fine and giving correct result, however due to having more than 5 million records, it takes almost an hour to process.
Is there a better way where I can avoid cursor for better performance?
Thanks for any suggestions.
Regards
Sorry i don't have data to test this out. Setup a sample table and give it a try.
But maybe something like this:
WITH abc_Order (n,period,WIP_Close,WIP_Open,WIP_Add,WIP_Minus) AS (
select
ROW_NUMBER() OVER(
ORDER BY
period
) n,
n,period,WIP_Close,WIP_Open,WIP_Add,WIP_Minus
from abc
)
update curr
set
WIP_Open = prev.WIP_Close,
WIP_Close = prev.WIP_Close + curr.WIP_Add - curr.WIP_Minus
from abc_Order curr
inner join abc_Order prev on
curr.n = prev.n+1
Lets make some test data
DECLARE #MyTable TABLE
(
Period int IDENTITY,
WIP_Open int,
WIP_Close int,
WIP_Add int,
WIP_Minus int
)
the first record has the default opening data set.
INSERT INTO #MyTable
( WIP_Open, WIP_ADD, WIP_Minus )
VALUES
( 10, 1, 2 )
Now we have some plus / minus from inventory being added / sold
INSERT INTO #MyTable
( WIP_Add, WIP_Minus )
VALUES
( 2, 1 ),
( 5, 3 ),
( 6, 1 ),
( 1, 7 );
Now lets sum up all of the changes along with the original record to see the running total and get the closing for each record.
WITH T AS
(
SELECT
Period,
WIP_Open,
WIP_Add,
WIP_Minus,
SUM(ISNULL(WIP_Open, 0) + WIP_Add - WIP_Minus) OVER (ORDER BY Period) RunningWipClose
FROM #MyTable
)
SELECT * FROM T
here is the output:
Period WIP_Open WIP_Add WIP_Minus RunningWipClose
1 10 1 2 9
2 NULL 2 1 10
3 NULL 5 3 12
4 NULL 6 1 17
5 NULL 1 7 11
After this, you use lag function to set the null opens with the prev close.
Input -
period WIP_Close WIP_Open WIP_Add WIP_Minus
1 1 1 1 10
2 8 3 7 5
3 6 4 9 15
Output-
period WIP_Close WIP_Open WIP_Add WIP_Minus
1 1 1 1 10
2 3 1 7 5
3 -3 3 9 15
I guess you want to solve above problem.
Here is the solution.Let me know if you are not able to understand this.
SELECT * INTO #T
FROM
(
SELECT 1 period, 1 WIP_Close, 1 WIP_Open, 1 WIP_Add, 10 WIP_Minus UNION ALL
SELECT 2 period, 8 WIP_Close, 3 WIP_Open, 7 WIP_Add, 5 WIP_Minus UNION ALL
SELECT 3 period, 6 WIP_Close, 4 WIP_Open, 9 WIP_Add, 15 WIP_Minus ) TAB
SELECT * FROM #T
DECLARE #TopC INT = (SELECT TOP 1 WIP_Close FROM #T)
DECLARE #TopO INT = (SELECT TOP 1 WIP_Open FROM #T)
DECLARE #TopDIff INT = (SELECT TOP 1 WIP_Add - WIP_Minus FROM #T)
SELECT period,
#TopC + SUM(WIP_Add - WIP_Minus) OVER (ORDER BY period ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) - #TopDIff AS WIP_Close,
COALESCE(#TopC + SUM(WIP_Add - WIP_Minus) OVER (ORDER BY period ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) - #TopDIff,#TopO) AS WIP_Open,
WIP_Add,
WIP_Minus
FROM #T