SQL Server calculating the current stock of items with stock taking corrections - sql-server

First of all, if this is answered somewhere, I apologize.
I'm trying to wrap my head around the following problem. I have a query that returns the following values from the inventory regarding one product:
RowId
Entry
Exit
StockTaking
1
0
0
2880
2
1200
0
0
3
0
800
0
4
0
800
0
5
0
480
2000
6
600
0
0
7
0
800
0
8
0
1000
0
This part is easy but the result should look like this:
RowId
ProductId
Entry
Exit
StockTaking
CurrentStock
1
3
0
0
2880
2880
2
3
1200
0
0
4080
3
3
0
800
0
3280
4
3
0
800
0
2480
5
3
0
480
2000
2000
6
3
600
0
0
2600
7
3
0
800
0
1800
8
3
0
1000
0
800
and from that the final value is 800 pcs on stock:
ProductID
CurrentStock
3
800
In CurrentStock column is calculated value based on Entry, Sale and StockTaking columns. When we have stock-taking at the start is simple (for this sample let's say that all products start with stock-taking), in RowId = 1 we have CurrentStock identical to StockTaking value.
In RowId = 2 we have, entry of 1200 pcs, so CurrentStock is now 2880+1200 = 4080 pcs....
Now the problem arises in RowID = 5, CurrentStock before StockTaking was 2480 pcs, but we found only 2000 pcs
So we are taking StockTaking value as CurrentStock value so we can calculate from that value forward, and at the same time to level the columns we are making an exit for 480 pcs which are missing at that time.
I managed to handle this over some temp table and then iterating trough the records and updating the CurrentStock value but with larger dataset, it takes some time.
Any suggestions how to handle this with CTEs to avoid writing to temp tables is greatly appreciated.
Thanks

A bit quick n dirty solution:
select *
, SUM(StockTaking + CASE WHEN StockTaking <= 0 THEN Entry - [Exit] ELSE 0 END) OVER(PARTITION BY StockGroup ORDER BY RowId) CurrentStock
from (
select COUNT(CASE WHEN StockTaking > 0 THEN 1 END) OVER(ORDER BY RowID) AS StockGroup
, *
from
(
VALUES (1, 0, 0, 2880)
, (2, 1200, 0, 0)
, (3, 0, 800, 0)
, (4, 0, 800, 0)
, (5, 0, 480, 2000)
, (6, 600, 0, 0)
, (7, 0, 800, 0)
, (8, 0, 1000, 0)
) t (RowId,Entry,[Exit],StockTaking)
) x
First you create groups of rows by counting each inventory row as start of a group. Then you summarize changes to the stock sorted by rowID.
Important that for rows where stocktaking is taken, we don't add Entry / Exit values.
So if you first do inventory and then take stock on the same "row", it will display wrong results. Probably you have info that can solve this problem, but it's not in your example.

Related

SQL Server query problem. example is in excel sheet picture

Please see the following pic and i want to convert this formula in SQL Server.
in excel sheet
M N
15 1 0
16 3 1
17 5 2
18 8 4
19 9 4
N= IF(M16-M15<=1,N15,M16-M15-1+N15
Please see the screenshot for reference:
As per your tags, this can be done with LAG and then doing a running total.
For each row, first calculate the difference in M from the previous row (using LAG) - I call this Dif_Last_M. This mirrors the 'M24-M23' part of your formula.
If Dif_Last_M is <= 1, add 0 to the running total (effectively making the running total the same as for the previous row)
Else if Dif_Last_M is > 1, add (Dif_Last_M minus 1) to the running total
Here is the code assuming your source table is called #Temp and has an ID (sorting value)
WITH M_info AS
(SELECT ID, M, (M - LAG(M, 1) OVER (ORDER BY ID)) AS Dif_Last_M
FROM #Temp
)
SELECT ID,
M,
SUM(CASE WHEN Dif_Last_M > 1 THEN Dif_Last_M - 1 ELSE 0 END) OVER (ORDER BY ID) AS N
FROM M_info;
And here are the results
ID M N
1 1 0
2 3 1
3 5 2
4 8 4
5 9 4
6 12 6
7 13 6
Here is a db<>fiddle with the above. It also includes additional queries showing
The result from the CTE
The values used in the running total
Note that while it possible to do this with recursive CTEs, they tend to have performance problems (they are loops, fundamentally). Soit is better (performance-wise) to avoid recursive CTEs if possible.

Set based stacking update query

I have a table that needs one of two Flag values are set, Flag1 and Flag2.
Create Table StackUpdateTable
(Id Int, GroupId Int, Flag1 Bit, Flag2 Bit, Requirements Int)
Now, here are the rules:
Requirements will be either a 1 or a 2.
Requirements specifies the number of preceding Flag1 values that must be set before a Flag2 value can be set for a given GroupId in Id order (ascending).
Once Flag2 is set, the sequence starts again (for each GroupId)
I've been trying to wrack my head on if there's a way to do a set based Update on this table (and all new ones that come in) that can set the flags for each set of GroupId records.
To further illustrate, consider the following data:
Insert Into StackUpdateTable Values
(1, 100, 0, 0, 1)
,(2, 100, 0, 0, 1)
,(3, 101, 0, 0, 1)
,(4, 102, 0, 0, 2)
,(5, 102, 0, 0, 2)
,(6, 102, 0, 0, 2)
,(7, 103, 1, 0, 1)
,(8, 103, 0, 0, 1)
,(9, 103, 0, 0, 1)
,(10,104, 1, 0, 2)
,(11,105, 1, 0, 2)
,(12,106, 0, 0, 2)
,(13,106, 0, 0, 2)
,(14,106, 0, 0, 2)
,(15,106, 0, 0, 2)
;
Given this data, here's what the resulting Updated data should look like
1 100 1 0 1 <-- Flag1 Set
2 100 0 1 1 <-- Flag2 Set
3 101 1 0 1 <-- Flag1 Set
4 102 1 0 2 <-- 1st Flag1 Set
5 102 1 0 2 <-- 2nd Flag1 Set
6 102 0 1 2 <-- Flag2 Set
7 103 1 0 1 <-- Unchanged
8 103 0 1 1 <-- Flag2 Set
9 103 1 0 1 <-- Flag1 Set
10 104 1 0 2 <-- Unchanged
11 104 1 0 2 <-- Unchanged
12 106 1 0 2 <-- 1st Flag1 Set
13 106 1 0 2 <-- 2nd Flag1 Set
14 106 0 1 2 <-- Flag2 Set
15 106 1 0 2 <-- 1st Flag1 Set
If more data is added, thusly...
Insert Into StackUpdateTable Values
(16,100, 0, 0, 1)
,(17,103, 0, 0, 1)
,(18,106, 0, 0, 2)
;
...then the updated dataset (in GroupId \ Id order) should look like:
1 100 1 0 1 <-- Unchanged
2 100 0 1 1 <-- Unchanged
16 100 1 0 1 <-- Flag1 Set
3 101 1 0 1 <-- Unchanged
4 102 1 0 2 <-- Unchanged
5 102 1 0 2 <-- Unchanged
6 102 0 1 2 <-- Unchanged
7 103 1 0 1 <-- Unchanged
8 103 0 1 1 <-- Unchanged
9 103 1 0 1 <-- Unchanged
17 103 0 1 1 <-- Flag2 Set
10 104 1 0 2 <-- Unchanged
11 104 1 0 2 <-- Unchanged
12 106 1 0 2 <-- Unchanged
13 106 1 0 2 <-- Unchanged
14 106 0 1 2 <-- Unchanged
15 106 1 0 2 <-- Unchanged
18 106 1 0 2 <-- Flag1 Set
Finally, if the following row should be inserted:
Insert Into StackUpdateTable Values
(19,106, 0, 0, 2)
;
then I'd expect:
1 100 1 0 1 <-- Unchanged
2 100 0 1 1 <-- Unchanged
16 100 1 0 1 <-- Flag1 Set
3 101 1 0 1 <-- Unchanged
4 102 1 0 2 <-- Unchanged
5 102 1 0 2 <-- Unchanged
6 102 0 1 2 <-- Unchanged
7 103 1 0 1 <-- Unchanged
8 103 0 1 1 <-- Unchanged
9 103 1 0 1 <-- Unchanged
17 103 0 1 1 <-- Flag2 Set
10 104 1 0 2 <-- Unchanged
11 104 1 0 2 <-- Unchanged
12 106 1 0 2 <-- Unchanged
13 106 1 0 2 <-- Unchanged
14 106 0 1 2 <-- Unchanged
15 106 1 0 2 <-- Unchanged
18 106 1 0 2 <-- Flag1 Set
19 106 0 1 2 <-- Flag2 Set
I've been fritzing with using Windowing functions such as Row_Number() Over (Partition By GroupId Order By Id) As _seq to organize the data, then a couple of Lag functions Lag(Flag1, 1, 0) Over (Partition By GroupId Order By Id) As _Calc1 etc but I fall over trying to handle new records (where the values start off with a zero).
I think I need to start doing record counts as well, but am unsure how to handle that - thinking that I might be able to do something with _seq perhaps, but still getting no-where.
What I'd like is to find out how I could do a Set based Update each time I parse this table. It doesn't have to be a single update, in fact I'm more than happy to drop a Temp table in the mix, or a CTE or anything else. The one thing I don't want to do is do have to cursor my way through this, either by Id or GroupId.
I'm quite prepared to hear that what I ask for just cannot be done, however I live in hopes!
Using ZLK's query from the comments, I converted it to an UPDATE statement that you can run after each time records are added to the table.
UPDATE s
SET s.Flag1 = CASE WHEN t.RN > 0 THEN 1 ELSE 0 END
,s.Flag2 = CASE WHEN t.RN = 0 THEN 1 ELSE 0 END
FROM
(
SELECT
*
,RN = ROW_NUMBER() OVER ( PARTITION BY GroupID, Requirements ORDER BY ID ) % ( Requirements + 1 )
FROM StackUpdateTable
) T
INNER JOIN StackUpdateTable s ON s.Id = T.Id

total days in Arrears calculation

guys i have problem with one old SP which calculates total days late when the costumer is late with the payments of an instalment
it goes like this:
#total days paid# #1st inst days due# #2nd inst days due# #total days#
---------------------------------------------------------------------------
---------------------------------------------------------------------------
0 1 0 1
0 2 0 2
0 3 0 3
0 4 0 4
0 30 0 30
0 31 1 31
0 32 2 32
32 0 3 35
so the procedure calculates (total days paid) + max of the days due
0+32 =32
32+3 =35
etc
and makes mistakes whenever the costumer is latemore then 30 days
its should always increment by 1 and not overlap the calculations
can anyone think of a quick way to fix this without over writhing the whole thing
so you have an existing formula for calculating #total days#, if you can locate the final place where that is returned, it could be a formula or a field name, let's call that (...) because we don't know what it is here, you can change it to
(...) + CASE WHEN (...) >= 30 THEN 1 ELSE 0 END AS '#total days#'

How can I create query that (counting values inside Column)

I have 3 companies 1001,1002 ,1003 it could be more and 11 containers with different sizes 1,2,3,4, 5 I want return only the containers that are in the companies that have the same amount or more of specified numbers. for example if I want 2 containers from size 1 and 3 containers from size 2 then only the containers in the company that has 2 or more of size 1 and 3 or more of size 2 should appear let's say that only company 1001 has them then it should appear alone.
I tried different queries and post one here but they recommend me to post a new question with the problem that I'm training to make query for.
(Company info and containers info are in two separate tables)
this is what I get when I remove having (basically all the containers in the city that has been selected)
CoID CoName ContainerID Price size1 size2 size3 size4 size5
6000001 hbjjvCompany 2000002 50 1 0 0 0 0
6000001 hbjjvCompany 2000003 50 1 0 0 0 0
6000002 NCompany 2000004 50 1 0 0 0 0
6000001 hbjjvCompany 2000005 100 0 1 0 0 0
6000002 NCompany 2000007 100 0 1 0 0 0
6000001 hbjjvCompany 2000008 200 0 0 1 0 0
6000001 hbjjvCompany 2000009 200 0 0 1 0 0
6000001 hbjjvCompany 2000010 200 0 0 1 0 0
6000002 NCompany 2000011 200 0 0 1 0 0
6000001 hbjjvCompany 2000012 400 0 0 0 0 1
6000003 ghhaCo 2000014 200 0 1 0 0 0
what should I get is
CoID CoName size1 size2 size3 size4 size5
6000001 hbjjvCompany 2 1 3 0 1
of course I want the containers id and the price but I put it heare like this to make it clear that my query show all the containers even if i removed the ContainerID and price.
I think it's this you're looking for:
CREATE TABLE #YourTable(CoID INT,CoName VARCHAR(100),ContainerID INT,Price DECIMAL(10,4),size1 INT,size2 INT,size3 INT,size4 INT,size5 INT);
INSERT INTO #YourTable VALUES
(6000001,'hbjjvCompany',2000002,50,1,0,0,0,0)
,(6000001,'hbjjvCompany',2000003,50,1,0,0,0,0)
,(6000002,'NCompany',2000004,50,1,0,0,0,0)
,(6000001,'hbjjvCompany',2000005,100,0,1,0,0,0)
,(6000002,'NCompany',2000007,100,0,1,0,0,0)
,(6000001,'hbjjvCompany',2000008,200,0,0,1,0,0)
,(6000001,'hbjjvCompany',2000009,200,0,0,1,0,0)
,(6000001,'hbjjvCompany',2000010,200,0,0,1,0,0)
,(6000002,'NCompany',2000011,200,0,0,1,0,0)
,(6000001,'hbjjvCompany',2000012,400,0,0,0,0,1)
,(6000003,'ghhaCo',2000014,200,0,1,0,0,0);
SELECT CoID
,CoName
,SUM(Price) AS SumPrice
,SUM(size1) AS CountSize1
,SUM(size2) AS CountSize2
,SUM(size3) AS CountSize3
,SUM(size4) AS CountSize4
,SUM(size5) AS CountSize5
FROM #YourTable
GROUP BY CoID,CoName;
--Clean up
DROP TABLE #YourTable;
The result
CoID CoName SumPrice s1 s2 s3 s4 s5
6000003 ghhaCo 200.0000 0 1 0 0 0
6000001 hbjjvCompany 1200.0000 2 1 3 0 1
6000002 NCompany 350.0000 1 1 1 0 0

Case statement not correctly matching expected values

I'm trying to generate some randomized data, and I've been using newid() to seed functions since it is called once for every row and is guaranteed to return a different result each time. However I'm frequently getting values that are somehow not equal to any integers in the expected range.
I've tried a few variations, including a highly upvoted one, but they all result in the same issue. I've put it into a script that shows the problem:
declare #test table (id uniqueidentifier)
insert into #test
select newid() from sys.objects
select
floor(rand(checksum(id)) * 4),
case isnull(floor(rand(checksum(id)) * 4), -1)
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999
end,
floor(rand(checksum(newid())) * 4),
case isnull(floor(rand(checksum(newid())) * 4), -1)
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999
end
from #test
I expect the results to always be in the range 0 to 3 for all four columns. When the unique identifiers are retrieved from a table, the results are always correct (first two columns.) Similarly, when they're output on the fly they're also correct (third column.) But when they're compared on the fly to integers in a case statement, it often returns a value outside the expected range.
Here's an example, these are the first 20 rows when I ran it just now. As you can see there are '999' instances in the last column that shouldn't be there:
0 0 3 1
3 3 3 1
0 0 3 3
3 3 2 999
1 1 2 999
3 3 2 1
2 2 0 999
0 0 0 0
3 3 2 0
1 1 3 999
3 3 0 999
2 2 2 2
1 1 3 0
2 2 3 0
3 3 1 999
0 0 1 999
3 3 1 1
0 0 0 3
3 3 0 999
0 0 1 0
At first I thought maybe the type coercion was different than I expected, and the result of rand() * int was a float not an int. So I wrapped it all in floor to force it to be an int. Then I thought perhaps there's an odd null value creeping in, but with my case statement a null would be returned as -1, and there are none.
I've run this one two different SQL Server 2012 SP1 instances, both give the same sort of results.
In the fourth column, isnull(floor(rand(checksum(newid())) * 4), -1) is being evaluated up to five times for each row. Once for each branch of the case. On each call the values can be different. So it can return 2, not match 1, 3 not match 2, 1 not match 3, 3 not match 4 fall to the else and return 999.
This can be seen if you get the execution plan, and look at the XML, there is a line [whitespace added.]:
<ScalarOperator ScalarString="
CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(0.000000000000000e+000) THEN (0)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(1.000000000000000e+000) THEN (1)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(2.000000000000000e+000) THEN (2)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(3.000000000000000e+000) THEN (3)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(-1.000000000000000e+000) THEN (-1)
ELSE (999)
END
END
END
END
END
">
Placing the expression in a CTE seems to keep the recomputes from happening:
; WITH T AS (SELECT isnull(floor(rand(checksum(newid())) * 4), -1) AS C FROM #Test)
SELECT CASE C
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999 END
FROM T

Resources