Need To select Data From One Table After Minus With One Value - sql-server

I have a table and a single value
Table 1
SNo Amount
1 100
2 500
3 400
4 100
Value: 800
Now I want the result from the table is Minus the value and finally
I would like to having rolling subtraction, that is subtract the Value 800 From Table 1's first Amount and then apply it to subsequent rows. Eg:
for type-1
800 - 100 (Record1) = 700
700 - 500 (record2) = 200
200 - 400 (record3) = -200
The table records starts from record 3 with Balance Values Balance 200
Table-Output
SNo Amount
1 200
2 100
that means if minus 800 in first table the first 2 records will be removed and in third record 200 is Balance

The easiest way to do this would be to use a running aggregate. In your original example, you had two tables, and if this is the case, simply run a sum on that table like I am doing in the subselect and store that value in the variable I created #Sum.
The CTE calculates what the value would be as it is added together for each record, and is then added to the total calculated, and then keeps the ones that are positive.
I believe that this will fit your need.
DECLARE #Sum INT;
SET #Sum = 800;
WITH RunningTotals
AS (
SELECT [SNo]
, [Amount]
, [Amount] + (
SELECT ISNULL(SUM([Amount]), 0)
FROM [Table1] t2
WHERE t2.[SNo] < t.SNo
) [sums]
FROM [Table1] t
),
option_sums
AS (
SELECT ROW_NUMBER() OVER ( ORDER BY [SNo] ) [SNo]
, CASE WHEN ( [Sums] - #Sum ) > 0 THEN [Sums] - #Sum
ELSE [Amount]
END AS [Amount]
, sums
, [Amount] [OriginalAmount]
, [OriginalID] = [SNo]
FROM [RunningTotals] rt
WHERE ( [Sums] - #Sum ) > 0
)
SELECT [SNo]
, CASE [SNo]
WHEN 1 THEN [Amount]
ELSE [OriginalAmount]
END AS [Amount]
, [OriginalID]
FROM option_sums
SNo Amount OriginalID
--- ------ ----------
1 200 3
2 100 4
3 100 5
4 500 6
5 400 7
6 100 8
7 200 9

Related

How to generate random 0 and 1 with 80-20 probability in sql server

I have a Table with 10 records, I have a column (name:RandomNumber) ,that its data type is bit .
now I want to insert data in to this column randomly in such a way that 80 percent of record (8 record) get 0 randomly and 20 percent (2 record) get 1.
For Example Like this:
Id
RandomNumber
1
0
2
0
3
0
4
1
5
0
6
0
7
0
8
1
9
0
10
0
One way is use ORDER BY NEWID() to assign 1 to two rows (20%) and assign 0 to others (remaining 80%) by excluding those assigned 1.
CREATE TABLE dbo.Example(
Id int NOT NULL CONSTRAINT PK_Test PRIMARY KEY
);
INSERT INTO dbo.Example VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
WITH ones AS (
SELECT TOP (2) Id, 1 AS RandomNumber
FROM dbo.Example
ORDER BY NEWID()
)
SELECT Id, 0 AS RandomNumber
FROM dbo.Example
WHERE Id NOT IN(SELECT Id FROM ones)
UNION ALL
SELECT Id, 1 AS RandomNumber
FROM ones
ORDER BY Id;
Alternatively, use ROW_NUMBER() OVER(ORDER BY NEWID()) and a CASE expression:
WITH example AS (
SELECT Id, ROW_NUMBER() OVER(ORDER BY NEWID()) AS rownum
FROM dbo.Example
)
SELECT Id, CASE WHEN rownum <= 2 THEN 1 ELSE 0 END AS RandomNumber
FROM example
ORDER BY Id;

T SQL Cursor Update field based on previous field

I'm using SQL Server 2016.
I have this table:
RowID SKU Shop Week Prioirty Replen Open_Stk
---------------------------------------------------------------
1 111 100 1 1 400 5000
2 111 200 1 2 400 NULL
3 111 300 1 3 400 NULL
4 111 400 1 4 400 NULL
This is the desired result:
RowID SKU Shop Week Prioirty Replen Open_Stk
---------------------------------------------------------------
1 111 100 1 1 400 5000
2 111 200 1 2 400 4600
3 111 300 1 3 400 4200
4 111 400 1 4 400 3800
The calculation for Open_Stk is based on the previous row:
[Open_Stk] = [Open_Stk]-IIF([Replen]<=IIF([Open_Stk]>=0,[Open_Stk],0),[Replen],0)
I am using the below cursor to update the Open_Stk but nothing happens - what am I missing:
DECLARE #CurrentRow INT;
DECLARE #PreviousRow INT
DECLARE ShopRank CURSOR FOR
SELECT RowID
FROM [tmp_tblTEST]
ORDER BY [SKU], [Week],Priority
OPEN ShopRank
FETCH NEXT FROM ShopRank INTO #CurrentRow
WHILE ##FETCH_STATUS = 0
BEGIN
IF ((SELECT [Open_Stk] FROM [tmp_tblTEST] WHERE RowID = #CurrentRow) IS NULL)
BEGIN
UPDATE [tmp_tblTEST]
SET [Open_Stk] = [Open_Stk] - IIF([Replen] <= IIF([Open_Stk] >= 0, [Open_Stk], 0), [Replen], 0)
WHERE RowID = #PreviousRow
END
SET #PreviousRow = #CurrentRow
FETCH NEXT FROM ShopRank INTO #CurrentRow
END
CLOSE ShopRank
DEALLOCATE ShopRank
There's no need for a CURSOR here at all. This is a little bit of guess work, but I suspect what you are actually after here is something like this:
SELECT V.RowID,
V.SKU,
V.Shop,
V.[Week],
V.Priority,
V.Replen,
FIRST_VALUE(V.Open_Stk) OVER (PARTITION BY V.SKU ORDER BY V.[Week], V.Priority
ROWS UNBOUNDED PRECEDING) -
ISNULL(SUM(V.Replen) OVER (PARTITION BY V.SKU ORDER BY V.[Week], V.Priority
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS OpenStk
FROM (VALUES (1,111,100,1,1,400,5000),
(2,111,200,1,2,400,NULL),
(3,111,300,1,3,400,NULL),
(4,111,400,1,4,400,NULL))V(RowID,SKU,Shop,[Week],Priority,Replen,Open_Stk)
ORDER BY V.Sku,
V.[Week],
V.Priority;
DB<>Fiddle (using original solution)
FIRST_VALUE does what is says on the tin. The SUM subtracts the values from every prior row from the value of Open_Stk on the first row; making the final result set. It only references the prior rows due to the ROWS BETWEEN clause. ROWS UNBOUNDED means to start at the beginning of the partitioned range, and 1 PRECEDING means the row prior.
WITH result AS
(
SELECT
a.*, ISNULL(NULLIF(a.Open_Stk, 0), 0) AS Output
FROM
table1 a
JOIN
table1 b ON a.Prioirty = b.Prioirty - 1
UNION ALL
SELECT
a.*, output - a.Replen
FROM
table1 a
JOIN
result b ON a.Prioirty = b.Prioirty+1
)
SELECT *
FROM result
WHERE output > 0

Group By Count and a field in the same query

I have a table (MyTable) with 3 columns; Number, Line, Amount.
If Number is the same in more than 3 records, I want to return these records grouped by Number. Otherwise I want to all other records grouped by Number and Line.
This is what I got so far. It gives an error as Line needs to be in an aggregated function
select * (
select Number, (case when count(Line) > 3 then -1 else Line end) as Line2, sum(Amount)
from MyTable
group by Number, Line2
) as x
order by x.Number
MyTable looks something like this:
Number Line Amount
------------------------
1 1 100
1 1 100
1 2 200
1 2 200
2 1 150
2 1 150
3 1 300
3 2 350
I want the result look something like this:
Number Line2 Amount
------------------------
1 -1 600 <- More than 3 lines
2 1 300 <- Grouped by Line
3 1 300
3 2 350
Try this:
SELECT Number, -1 AS Line, SUM(Amount) AS Amount
FROM mytable
GROUP BY Number
HAVING COUNT(*) > 3
UNION
SELECT Number, Line, SUM(Amount) AS Amount
FROM mytable
WHERE Number NOT IN (SELECT Number
FROM mytable
GROUP BY Number
HAVING COUNT(*) > 3)
GROUP BY Number, Line
You can use CTE like this:
DECLARE #t table
(Number int,Line int,Amount int)
INSERT #t VALUES
(1,1,100),(1,1,100),(1,2,200),
(1,2,200),(2,1,150),(2,1,150),
(3,1,300),(3,2,350)
;WITH CTE as
(
SELECT
Number, Line, Amount,
count(*) over (partition by number) cnt
FROM #t
)
SELECT
Number,
CASE WHEN cnt < 3 THEN Line ELSE -1 END Line2,
SUM(Amount) Amount
FROM CTE
GROUP BY number, CASE WHEN cnt < 3 THEN Line ELSE -1 END
Result:
Number Line Amount
1 -1 600
2 1 300
3 1 300
3 2 350

Access Previous Value of Row and use it in Calculations of SQL query

I have a table
ID OpeningBal Type
1 1000 -
2 100 IN
3 200 OUT
4 100 OUT
5 300 IN
I want Output in sql query without lag function like this
ID OpeningBal Type Closing Bal
1 1000 - 0
2 100 IN 1100
3 200 OUT 900
4 100 OUT 800
5 300 IN 1100
This will work with sql-server 2012:
DECLARE #t
table(ID int, OpeningBal int, Type varchar(3))
INSERT #t values
(1,1000,'-'),(2,100,'IN'),(3,200,'OUT'),(4,100,'OUT'),(5,300,'IN')
SELECT
*,
CASE WHEN Type = '-'
THEN 0
ELSE
sum(case when Type = 'Out' THEN -OpeningBal ELSE OpeningBal END) over (order by id)
END ClosingBal
FROM #t
Result:
ID OpeningBal Type ClosingBal
1 1000 - 0
2 100 IN 1100
3 200 OUT 900
4 100 OUT 800
5 300 IN 1100

regarding updating rows in table without using loop

I have table in which I have column called quantity also I have 10 rows which having same column value 200(it can be any value)
Requirement is: if a input value is x=500(or anynumber) then this value should be compared with quantity column value in a fasion below:
if 1 row's quantity is 200 then it should subtract it form 500 and x should be updated to 300 and quantity of that row should be made 0 then It should move to next row till x is 0
could you please help me write sql query for this...
it is ask that loops should not be used.
thanks,
What is the version of SQL Server? If it's 2012 or 2014, you can use the following:
DECLARE #x int = 500
;WITH cte_sum AS
(
SELECT quantity,
ISNULL(SUM(quantity) OVER (ORDER BY (SELECT NULL) ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) sum_running_before,
SUM(quantity) OVER (ORDER BY (SELECT NULL) ROWS UNBOUNDED PRECEDING) sum_running_total
FROM YourTable
)
UPDATE cte_sum
SET quantity = CASE
WHEN quantity >= #x - sum_running_before THEN
quantity - (#x - sum_running_before)
ELSE 0
END
WHERE (#x >= sum_running_total OR (#x < sum_running_total AND sum_running_before < #x))
It's a bit more tricky to get running totals in earlier versions but I think you got the main idea.
DECLARE #YourTable TABLE
(
CustId INT,
Quantity INT
)
INSERT INTO #YourTable
( CustId, Quantity )
VALUES
( 1, 10 ),
( 1, 10 ),
( 1, 10 ),
( 1, 10 ),
( 2, 20 ),
( 2, 20 ),
( 2, 20 ),
( 2, 20 );
;WITH cte_sum AS
(
SELECT
y.CustId,
y.Quantity,
ROW_NUMBER() OVER (PARTITION BY CustId ORDER BY Quantity) RN
FROM #YourTable y
)
SELECT s1.CustId, s1.Quantity, s2.Qty, s1.Quantity + ISNULL(s2.Qty, 0) RunningTotal, s1.RN
FROM cte_sum s1
OUTER APPLY
(
SELECT SUM(Quantity) Qty FROM cte_sum s2
WHERE s2.CustId = s1.CustId
AND s2.RN < s1.RN
) s2
ORDER BY s1.CustId, s1.RN
Here's an example of a running total that will work for Sql Server 2005+
This is the output:
CustId Quantity Qty RunningTotal RN
1 10 NULL 10 1
1 10 10 20 2
1 10 20 30 3
1 10 30 40 4
2 20 NULL 20 1
2 20 20 40 2
2 20 40 60 3
2 20 60 80 4

Resources