SQL Server 2016 - Calculating remaining stock quantity - sql-server

I have a table like this:
As you can see, I have a column called remaining_stock that is correct for the first line of each item but NOT for the subsequent lines. The first line is straight forward as you can simply subtract the order-quantity from initial_stock..
What I want to achieve is to get a remaining_stock column that looks like this:
I though of using row_number and then joining back to the same table using the row numbers.. but that doesn't quite work either. can someone point me in the right direction please?
select 1 as line, 123 as item, 5 as order_quantity,10 as intial_stock
union all
select 2 as line, 123 as item, 3 as order_quantity,10 as intial_stock
union all
select 3 as line, 123 as item, 1 as order_quantity,10 as intial_stock
union all
select 4 as line, 234 as item, 5 as order_quantity,15 as intial_stock
union all
select 5 as line, 234 as item, 3 as order_quantity,15 as intial_stock
union all
select 6 as line, 234 as item, 1 as order_quantity,15 as intial_stock

Small matter using the window function Sum() over
Example
Select *
,Remaining_Stock = intial_stock - sum(Order_Quantity) over (Partition By Item Order by Line)
from YourTable
Returns
line item order_quantity intial_stock Remaining_Stock
1 123 5 10 5
2 123 3 10 2
3 123 1 10 1
4 234 5 15 10
5 234 3 15 7
6 234 1 15 6

Related

How to calculate moving avarage within array

I have a table with two columns, id and for each id an array with values. I want to calculate the moving avarage within the id over the moving 3 elements.
id
v
1
{210,200,180,120,150,200}
2
{230,180,140,100,170,210}
create table series(id int, v _int4);
insert into series
values(1,array[210,200,180,120,150,200])
,(2,array[230,180,140,100,170,210]);
If i use the following query it works for one id
select id, nr, "elemV", avg("elemV") over (order by nr rows between 3 PRECEDING AND CURRENT ROW) "movingAvgOver3elements"
from series t, unnest("v") with ordinality as a("elemV", nr)
where id = 1 ;
id
nr
elemV
movingAvgOverLast4Elements
1
1
210
210.0000000000000000
1
2
200
205.0000000000000000
1
3
180
196.6666666666666667
1
4
120
177.5000000000000000
1
5
150
162.5000000000000000
1
6
200
162.5000000000000000
How does it works for all id's within the id ?
The problem is in that case the 4 elements moving over the id 1 to 2
select id, nr, "elemV", avg("elemV") over (order by id, nr rows between 3 PRECEDING AND CURRENT ROW) "movingAvgOver3elements"
from series t, unnest("v") with ordinality as a("elemV", nr)
id
nr
elemV
movingAvgOver3elements
1
1
210
210.0000000000000000
1
2
200
205.0000000000000000
1
3
180
196.6666666666666667
1
4
120
177.5000000000000000
1
5
150
162.5000000000000000
1
6
200
162.5000000000000000
2
1
230
175.0000000000000000
2
2
180
190.0000000000000000
2
3
140
187.5000000000000000
2
4
100
162.5000000000000000
2
5
170
147.5000000000000000
2
6
210
155.0000000000000000
If I understand correctly, I think you just need to add a PARTITION BY clause to make each id a separate frame. With that clause in place, you can also remove the sort by id, although that's optional.
select id, nr, "elemV", avg("elemV") over (PARTITION BY id order by nr rows between 3 PRECEDING AND CURRENT ROW) "movingAvgOver3elements"
from series t, unnest("v") with ordinality as a("elemV", nr);

T-SQL select rows where [col] = MIN([col])

I have a data set produced from a UNION query that aggregates data from 2 sources.
I want to select that data based on whether or not data was found in only of those sources,or both.
The data relevant parts of the set looks like this, there are a number of other columns:
row
preference
group
position
1
1
111
1
2
1
111
2
3
1
111
3
4
1
135
1
5
1
135
2
6
1
135
3
7
2
111
1
8
2
135
1
The [preference] column combined with the [group] column is what I'm trying to filter on, I want to return all the rows that have the same [preference] as the MIN([preference]) for each [group]
The desired output given the data above would be rows 1 -> 6
The [preference] column indicates the original source of the data in the UNION query so a legitimate data set could look like:
row
preference
group
position
1
1
111
1
2
1
111
2
3
1
111
3
4
2
111
1
5
2
135
1
In which case the desired output would be rows 1,2,3, & 5
What I can't work out is how to do (not real code):
SELECT * WHERE [preference] = MIN([preference]) PARTITION BY [group]
One way to do this is using RANK:
SELECT row
, preference
, [group]
, position
FROM (
SELECT row
, preference
, [group]
, position
, RANK() OVER (PARTITION BY [group] ORDER BY preference) AS seq
FROM t) t2
WHERE seq = 1
Demo here
Should by doable via simple inner join:
SELECT t1.*
FROM t AS t1
INNER JOIN (SELECT [group], MIN(preference) AS preference
FROM t
GROUP BY [group]
) t2 ON t1.[group] = t2.[group]
AND t1.preference = t2.preference

Repeat a sum as a column in SQL

I have a table
WO# Seg# Part# QTY TotSale FlatSale
1 1 1 5 35 159
1 1 2 2 100 159
1 2 3 3 50 50
I need to calculate the FlatSale/SUM(TotSale) for each Seq# & WO# but do not group Seg# into one row.
I need this
WO# Seg# Part# QTY TotSale FlatSale Calc
1 1 1 5 35 159 1.177
1 1 2 2 100 159 1.177
1 2 3 3 50 50 1
With my code I am able to only do the division on each individual line like this:
select *, FlatSale/TotSale as Calc from table
WO# Seg# Part# QTY TotSale FlatSale Calc
1 1 1 5 35 159 4.54
1 1 2 2 100 159 1.59
1 2 3 3 50 50 1
I wouldn't mind leaving my Calc column and adding another column if that's the easiest way to do it.
Maybe something like this:
SELECT
[WO#],[Seg#],[Part#],[QTY],[TotSale],[FlatSale]
((FlatSale*100)/(SUM([TotSale]) OVER(PARTITION BY [Seg#] ORDER BY [Seg#])))/100 AS Calc
FROM SomeTable
You can use a lateral join. Here's something quick (not tested as I don't have access to mssql at the moment):
select *, x.CalcPrice as Calc
from table t
outer apply (
select t.FlatSale/SUM(ix.TotSale) CalcPrice
from table ix where ix.[WO#] = t.[WO#]
and ix.[Seg#] = t.[Seg#]) x
Something like that.

Increment column values based on two columns in Oracle database

I have a table Test , that has below structure:
Id CID RO Other Columns
1 111 2
2 111 1
3 111 6
4 111 6
5 111 8
6 111 5
7 101 4
8 101 4
9 101 3
Resultant order in RO should be like below:
-> For One CID and ascending order of RO should get order (RO) replaced with 1,2,3,4 and so on
Final Order in RO column:
(RO column's value got replaced)
Id CID RO (New) RO Other Columns
1 111 2 2
2 111 1 1
3 111 6 4
4 111 6 5
5 111 8 6
6 111 5 3
7 101 4 2
8 101 4 3
9 101 3 1
There are hundreds of cids like that in table. Please let me know if this can be achieved in single query using some Oracle function or some procedure needs to be written. Any lead or example would be helpful.
Thanks
The NEW_RO column can be calculated with the analytic function ROW_NUMBER():
select ... ,
row_number() over (partition by cid order by ro) as new_ro [, ...]
In your data, there are ties for RO within the same CID. Do you care, in that case, which row gets what NEW_RO value? If, for example, in the case of same RO you also want to (further) order by ID, you can change the above to
select ... ,
row_number() over (partition by cid order by ro, id) as new_ro [, ...]
EDIT: I missed the fact that you need to UPDATE the RO values with the NEW_RO values. Analytic functions can't be used in an UPDATE statement (not directly anyway); the MERGE statement is the perfect alternative for this:
merge into test
using ( select id,
row_number() over (partition by cid order by ro, id) as new_ro
from test
) s
on (test.id = s.id)
when matched then update set ro = s.new_ro
;
Addressing the follow up question in the comment on #mathguy's answer. If you've got a query that is producing the new values you want, and want to quickly write an update, I like to use merge:
MERGE INTO your_table target
USING (your_query_here) source
ON (Target.ID = Source.ID)
WHEN MATCHED THEN
UPDATE SET Target.column = Source.new_value
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606
MERGE can do more than that, but I've found it handy in this "correct the data" situation.

SQL Server Rank Function without taking into account some flagged values

Here is my problem: I have a list of flagged values, I want to see where those values would be in the case they weren't flagged. But I don't want the other flagged values to influence the order.
Note: Flagged values are the ones with CurrentPlace 10000
ID Value CurrentPlace
------------------------
1 2 1
2 8 3
3 3 2
4 4 10000
5 5 10000
6 10 10000
Using:
select *
from
(select
id, value,
rank() over (order by Value asc) as Rank
from
tbl1) r
where
r.ID in (select id from tbl1 where CurrentPlace = 10000)
Desired output:
ID Value Rank
------------------
4 4 3
5 5 3
6 10 4
But I'm getting this instead:
ID Value Rank
------------------
4 4 3
5 5 4
6 10 6
Any help will be appreciated
Thank you guys
I've solved with
SELECT ID, Value, Rank
FROM tbl1 a
CROSS APPLY
(SELECT isnull(max(currentPlace),0) + 1 AS Rank FROM tbl1 WHERE value < a.value and currentPlace <> 10000) b
WHERE a.CurrentPlace = 10000
Please feel free to comment this out.

Resources