T SQL Cumulative Subtraction - sql

I'm using MS SQL Server.
I have the below table:
SKU Shop WeekNum ShopPrioirty Replen OpeningStock
111 100 1 1 10 5000
111 200 1 2 10 NULL
111 300 1 3 5 NULL
111 400 1 4 8 NULL
222 100 2 1 20 6000
222 200 2 2 15 NULL
222 300 2 3 12 NULL
222 400 2 4 10 NULL
This is the desired result:
SKU Shop WeekNum ShopPrioirty Replen OpeningStock
111 100 1 1 10 5000
111 200 1 2 10 4990
111 300 1 3 5 4980
111 400 1 4 8 4975
222 100 2 1 20 6000
222 200 2 2 15 5980
222 300 2 3 12 5965
222 400 2 4 10 5953
For a given week, a SKU exists in multiple shops and is assigned a priority. At Priority 1 the opening stock is assigned.
However, I need to update the Opening Stock (where it is currently NULL) to equal the previous Opening Stock Minus the Previous Replen.
Before I attempt the update, I tried to just do a SELECT
SELECT SKU
,Shop
,WeekNum
,StorePriority
,Replen
,OpeningStock
,OpeningStock - Replen OVER (ORDER BY SKU,Shop,WeekNum ROWS UNBOUNDED PRECEDING) AS Opening
FROM [table1] t
But I receive the error:Incorrect syntax near the keyword 'OVER'.
Is a running sum the correct way to go?
Would it be best to create a key made up from the SKU\Shop\WeekNum\Priority?
Thanks.

I've made a couple of assumptions here on your PARTITION BY and ORDER BY clauses, but this gets you the result you're after. As you only have a value for OpeningStock in the first row for a SKU, then I use FIRST_VALUE to get the First Value, and then take away all prior values of Replen:
WITH VTE AS(
SELECT *
FROM (VALUES(111,100,1,1,10,5000),
(111,200,1,2,10,NULL),
(111,300,1,3,5 ,NULL),
(111,400,1,4,8 ,NULL),
(222,100,2,1,20,6000),
(222,200,2,2,15,NULL),
(222,300,2,3,12,NULL),
(222,400,2,4,10,NULL))V(SKU,Shop,WeekNum,ShopPrioirty,Replen,OpeningStock))
SELECT V.SKU,
V.Shop,
V.WeekNum,
V.ShopPrioirty,
V.Replen,
V.OpeningStock,
FIRST_VALUE(V.OpeningStock) OVER (PARTITION BY V.SKU ORDER BY V.ShopPrioirty,V.WeekNum ROWS UNBOUNDED PRECEDING) -
ISNULL(SUM(V.Replen) OVER (PARTITION BY V.SKU ORDER BY V.ShopPrioirty,V.WeekNum ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS CurrentStock
FROM VTE V;

Related

Can't use case & aggregation correctly

I have the following table
Cash_table
ID Cash Rates Amount
1 50 3 16
2 100 4 25
3 130 10 7
3 130 10 6
4 13 7 1.8
5 30 8 2.5
5 30 10 1
6 10 5 2
What I want as a result is to cumulate all the entries that have a Count(id)>1 like this:
ID New_Cash New_Rates New_Amount
1 50 3 16
2 100 4 25
3 130 10+10 130/(10+10)
4 13 7 1.8
5 30 8+10 30/(8+10)
6 10 5 2
So I only want to change the rows where Count(id)>1 and leave the rest like it was.
For the rows with count(id)>1 I want to sum up the rates and take the cash and divide it by the sum of the rates. The Rates alone aren't a problem since I can sum them up and group by id and get the desired result.
The problem is with the New_Amount column:
I am trying to do it with a case statement but it isn't working:
select id,
cash as new_cash,
sum(rates) as new_rates,
(case count(id)
when 1 then amount
else cash/sum(nvl(rates,null))
end) as new_amount
from Cash_table
group by id
As the cash value is always the same for an ID, you can group by that as well:
select id,
cash as new_cash,
sum(rates) as new_rates,
case count(id)
when 1 then max(amount)
else cash/sum(rates)
end as new_amount
from cash_table
group by id, cash
order by id
ID NEW_CASH NEW_RATES NEW_AMOUNT
---------- ---------- ---------- ----------
1 50 3 16
2 100 4 25
3 130 20 6.5
4 13 7 1.8
5 30 18 1.66666667
6 10 5 2
The first branch of the case expression needs an aggregate because you aren't grouping by amount; and the sum(nvl(rates,null)) can just be sum(rates). If you're expecting any null rates then you need to decide how you want the amount to be handled, but nvl(rates,null) isn't doing anything.
You can do the same thing without a case expression if you prefer, manipulating all the values - which might be more expensive:
select id,
cash as new_cash,
sum(rates) as new_rates,
sum(amount * rates)/sum(rates) as new_amount
from cash_table
group by id, cash
order by id

SQL. How to combine two records with the same ID into one line based on value's in a column

SQL Server 2012: how to combine two records with the same ID (TransportOrder) into one line based on value's in a column (PalletType)?
Example: order 678 has two lines with 1 europallet en 3 BetweenEuropallet. So de order takes only 1 TransportEuropalletPlace.
The output should be in the case of order 678, one line, telling there is total 4 europallets (sum 1 + 3 from two lines) and 1 TransportEuropalletPlace (SUM 1 + 0)
How to achieve this with SQL query?
Original output:
TransportOrder PalletType Quantity TransportEuropalletPlace
--------------------------------------------------------------------------
123 Minipallet 1 0.5
345 Europallet 1 1
678 Europallet 1 1
678 BetweenEuropallet 3 0
900 Europallet 2 2
Output needed for order 678:
TransportOrder PalletType Quantity TransportEuropalletPlace
--------------------------------------------------------------------------
123 Minipallet 1 0.5
345 Europallet 1 1
678 Europallet 4 1
900 Europallet 2 2
This should get you started:
SELECT
TransportOrder,
SUM(ISNULL(Europallet,0) + ISNULL(BetweenEuropallet, 0)) as 'Pallets'
FROM Table_Name
GROUP BY TransportOrder
You should group by the column TransportOrder, as in:
select
transportorder,
max(pallettype) as pallettype,
sum(quantity) as quantity,
sum(transporteuropalletplace) as transporteuropalletplace
from my_table
group by transportorder

Simple data, Complex query on SQL Server

I need to make a query over an SQL Server table but I don't know exactly how.
Consider this table (the real table is much more complex, Ord1 and Ord2 are dates that could be null, but i simplified it to this case):
Data of MyTable
ID MaqID Ord1 Ord2
------------------------
1 144 4 3
2 144 2 1
3 12 2 3
4 144 3 5
5 12 3 1
6 144 4 2
7 12 2 4
8 144 2 3
9 12 1 5
10 12 3 2
I need records for specific MaqID in Specific Order. I get that with this Query:
SELECT * FROM myTable WHERE MaqID=144 ORDER BY MaqID, Order1 DESC, Order2
Wich give me:
ID MaqID Ord1 Ord2
------------------------
6 144 4 2
1 144 4 3
4 144 3 5
2 144 2 1
8 144 2 3
Now, I need a single query that, for each MaqID, return the first ID for each subquery following above order. The result should be:
Expected result
MaqID ID
-----------
144 6
12 5
I have already try distinct conbination of TOP a MAX, but TOP result only one result and i need one for each MaqID, and for Max I have not field to maximize.
To sumarize: I need the first ID for each MaqID from a subquery in a specific order
Any ideas? Thanks!
You can do this using row_number():
select t.*
from (select t.*,
row_number() over (partition by macid Order1 DESC, Order2) as seqnum
from mytable t
) t
where seqnum = 1;

Queries to fetch items based on top Ranks

I have a table which rank the items which i have.
I need a queries which will pick up only the top 2 ranks for a given item, the rank may not be in sequential order.
I need to fetch the item with least two ranks, there will same rank for two items as well.
Here is the snap shot of my table.
Item Id Supp Id Rank
1 2 2
1 1 7
1 7 5
1 9 11
2 67 4
2 9 14
2 10 14
2 34 4
2 25 3
2 60 3
2 79 5
my requirement is if I enter 2 i should get the result as below
Item Id Supp_id Rank
2 25 3
2 60 3
2 67 4
2 34 4
I am using oracle 10g version.
As one of the approaches it can be done as follows. Here we are using dense_rank() over() analytic function to assign a rank for a row in a ordered by rank group of rows .
select t.item_id
, t.supp_id
, t.rank
from (select item_id
, supp_id
, rank
, dense_rank() over(partition by item_id
order by rank) as rn
from t1
where item_id = 2
) t
where t.rn <= 2
Result:
ITEM_ID SUPP_ID RANK
---------- ---------- ----------
2 25 3
2 60 3
2 67 4
2 34 4
SQLFiddle Demo

TSQL - divide rows into groups based on one field

This is modified version of my earlier question: TSQL equally divide resultset to groups and update them
I have my database with 2 tables like so:
Orders table has data like below:
OrderID OperatorID GroupID OrderDesc Status Cash ...
--------------------------------------------------------------------------
1 1 1 small_order 1 300
2 1 1 another_order 1 0
3 1 2 xxxxxxxxxxx 2 1000
5 2 2 yyyyyyyyyyy 2 150
9 5 1 xxxxxxxxxxx 1 50
10 NULL 2 xxxxxxxxxxx 1 150
11 NULL 3 xxxxxxxxxxx 1 -50
12 4 1 xxxxxxxxxxx 1 200
Operators table:
OperatorID Name GroupID Active
---------------------------------------
1 John 1 1
2 Kate 1 1
4 Jack 2 1
5 Will 1 0
6 Sam 3 0
I'm able to equally divide my recordset into equally groups using below query:
SELECT o.*, op.operatorName AS NewOperator, op.operatorID AS NewOperatorId
FROM (SELECT o.*, (ROW_NUMBER() over (ORDER BY newid()) % numoperators) + 1 AS randseqnum
FROM Orders o CROSS JOIN
(SELECT COUNT(*) AS numoperators FROM operators WHERE operators.active=1) op
WHERE o.status in (1,3)
) o JOIN
(SELECT op.*, ROW_NUMBER() over (ORDER BY newid()) AS seqnum
FROM Operators op WHERE op.active=1
) op
ON o.randseqnum = op.seqnum ORDER BY o.orderID
Demo available at: http://sqlfiddle.com/#!3/ff47b/1
Using script from above I can divide Orders to (almost) equal groups but based on number or Orders for Operator, but I need to modify it so that it will assign Operators to Orders based on sum or Cash for orders.
For example:
If I have 6 Orders with Cash values: 300, 0, 50, 150, -50, 200 they sum gives 650.
My script should assign to 3 Operators random 2 Orders with random sum of Cash for Orders.
What I would like to get is to assign for example 300,-50 to operator1, 200, 0 to second and 150, 50 to third.
Hope this sound clear :)
Here is example output that I expect to get:
ORDERID OPERATORID GROUPID DESCRIPTION STATUS CASH NEWOPERATORID
------------------------------------------------------------------------
1 1 1 small_order 1 300 2
2 1 1 another_order 1 0 1
9 5 1 xxxxxxxxxxx 1 50 4
10 (null) 2 xxxxxxxxxxx 1 150 4
11 (null) 3 xxxxxxxxxxx 1 -50 2
12 4 1 xxxxxxxxxxx 1 200 1
How can I (if I can at all) assign Operators to my Orders so that sum or Cash will be closest to average
If I'm understanding this right, could you get the result you want by ordering the Cash column by the biggest, then the smallest, then the next biggest, then the next smallest, etc. Like this:
ROW_NUMBER() over (order by CASE WHEN CashOrder % 2 = 1 then Cash else -Cash END) as splitCash
where you've provided CashOrder lower in the query with
ROW_NUMBER() over (ORDER by CASH) as CashOrder
Then you specify each of your operators depending on this split value, ie (for three operators):
splitCash%3 +1