I have a query below to allocated stock qty to match order qty based on order type. The problem is the stock qty is not updated.
DECLARE #tblOrder TABLE
(DealerCode NVARCHAR(50),
PartCode NVARCHAR(50),
OrderQty INT,
OrderType NVARCHAR(50)
)
INSERT INTO #tblOrder
( DealerCode,
PartCode,
OrderQty,
OrderType )
VALUES ('D1','A',19,'Urgent'),
('D2','B',10,'Normal'),
('D3','C',11,'HotLine'),
('D1','D',20,'Normal'),
('D2','E',12,'Normal'),
('D1','D',40,'Normal');
DECLARE #tblStock TABLE
(PartCode NVARCHAR(50),
StockQty INT)
INSERT INTO #tblStock
( PartCode,
StockQty)
VALUES ('A',20),
('B',15),
('C',9),
('D',30),
('E',0)
;WITH ordertemp AS (
select ord.dealercode,
ord.partcode,
ord.orderqty,
ord.ordertype,
RANK() OVER (ORDER BY case ord.ordertype when 'HotLine' then 1 when 'Urgent' then 2 else 3 end, ord.partcode, ord.dealercode) 'StockPriority',
sto.stockqty 'InitialStock'
from #tblorder ord
left outer join #tblstock sto
on ord.partcode = sto.partcode )
SELECT
Orders.dealercode,
Orders.partcode,
CASE WHEN Backlog.PriorQty > Orders.InitialStock THEN 0
ELSE Orders.InitialStock - Backlog.PriorQty END 'Stock',
Orders.orderqty,
CASE WHEN Backlog.PriorQty + Orders.OrderQty < Orders.InitialStock THEN Orders.OrderQty
WHEN Backlog.PriorQty > Orders.InitialStock THEN 0
ELSE Orders.InitialStock - Backlog.PriorQty END 'Allocated',
Orders.ordertype
FROM
ordertemp Orders
INNER JOIN
(
SELECT A.stockpriority, A.partcode, ISNULL(SUM(B.orderqty),0) 'PriorQty'
from ordertemp A
LEFT OUTER JOIN ordertemp B
on A.partcode = B.partcode
and A.stockpriority > B.stockpriority
group by A.stockpriority, A.partcode ) Backlog
ON Orders.stockpriority = Backlog.stockpriority
ORDER BY Orders.StockPriority
I got result:
dealercode partcode stock orderqty allocated ordertype
D3 C 9 11 9 HotLine
D1 A 20 19 19 Urgent
D2 B 15 10 10 Normal
D1 D 30 20 20 Normal
D1 D 30 40 30 Normal
D2 E 0 12 0 Normal
Actually, the stock (30) of partcode 'D' must be remain 10 after allocated 20.
Any idea please?
The Reason you get stock 30 is because your are using RANK for prioritization. RANK will allocate the same number to two order records if they have the same stock priority as defined by your condition ORDER BY case ord.ordertype when 'HotLine' then 1 when 'Urgent' then 2 else 3 end, ord.partcode, ord.dealercode. If you check the output of ordertemp you will see
dealercode partcode orderqty ordertype StockPriority InitialStock
D3 C 11 HotLine 1 9
D1 A 19 Urgent 2 20
D2 B 10 Normal 3 15
D1 D 20 Normal 4 30
D1 D 40 Normal 4 30
D2 E 12 Normal 6 0
Notice that the StockPriority of partcode D is 4 for both rows 4 and 5.
You can fix this by using ROW_NUMBER() instead of RANK() . You can also add orderqty in ROW_NUMBER() to specify if you want higher priority for smaller/larger orders if they have the same ordertype, partcode and dealercode.
Query with ROW_NUMBER()
DECLARE #tblOrder TABLE
(DealerCode NVARCHAR(50),
PartCode NVARCHAR(50),
OrderQty INT,
OrderType NVARCHAR(50)
)
INSERT INTO #tblOrder
( DealerCode,
PartCode,
OrderQty,
OrderType )
VALUES ('D1','A',19,'Urgent'),
('D2','B',10,'Normal'),
('D3','C',11,'HotLine'),
('D1','D',20,'Normal'),
('D2','E',12,'Normal'),
('D1','D',40,'Normal');
DECLARE #tblStock TABLE
(PartCode NVARCHAR(50),
StockQty INT)
INSERT INTO #tblStock
( PartCode,
StockQty)
VALUES ('A',20),
('B',15),
('C',9),
('D',30),
('E',0)
;WITH ordertemp AS (
select ord.dealercode,
ord.partcode,
ord.orderqty,
ord.ordertype,
ROW_NUMBER() OVER (ORDER BY case ord.ordertype when 'HotLine' then 1 when 'Urgent' then 2 else 3 end, ord.partcode, ord.dealercode) 'StockPriority',
sto.stockqty 'InitialStock'
from #tblorder ord
left outer join #tblstock sto
on ord.partcode = sto.partcode )
SELECT
Orders.dealercode,
Orders.partcode,
CASE WHEN Backlog.PriorQty > Orders.InitialStock THEN 0
ELSE Orders.InitialStock - Backlog.PriorQty END 'Stock',
Orders.orderqty,
CASE WHEN Backlog.PriorQty + Orders.OrderQty < Orders.InitialStock THEN Orders.OrderQty
WHEN Backlog.PriorQty > Orders.InitialStock THEN 0
ELSE Orders.InitialStock - Backlog.PriorQty END 'Allocated',
Orders.ordertype
FROM
ordertemp Orders
INNER JOIN
(
SELECT A.stockpriority, A.partcode, ISNULL(SUM(B.orderqty),0) 'PriorQty'
from ordertemp A
LEFT OUTER JOIN ordertemp B
on A.partcode = B.partcode
and A.stockpriority > B.stockpriority
group by A.stockpriority, A.partcode ) Backlog
ON Orders.stockpriority = Backlog.stockpriority
ORDER BY Orders.StockPriority
He's quite right above - when you first posted I had made the assumption you'd only have one entry per supplier per part / priority rather than duplicates :)
Note if you added orderquantity you would still face a problem if you had 2 entries with the same quantity in each.
Related
I have 2 tables in SQL Server and I want to compare them. I want to take 'NEEDED_AMOUNT' and 'min. 'ID'. I tried the following:
SELECT S_ID, NEEDED_AMOUNT, ID
FROM (
select T1.S_ID
, T2.NEEDED_AMOUNT
, T1.ID
from T1
INNER JOIN T2 MSD ON T1.S_ID = T2.S_ID
) TABLE1
GROUP BY S_ID, NEEDED_AMOUNT, ID
To explain this for example: in T1 table I have S_ID as '1' and its amount '20' and '30'. Also in T2 I have request for S_ID and I need '40' amount. So in T1 table how can I reach 40? I must take first row '20' amount and I split second row '30' to '20'. Below you can see what I want the output.
So here are the tables.
I can call this table T1 (ID is primary key and auto inc.):
ID AMOUNT S_ID
1 20 1
2 30 1
3 10 2
4 20 3
5 5 3
and I can call this table T2:
S_ID NEEDED_AMOUNT DATE
1 40 01.01.2020
2 5 02.01.2020
3 20 03.01.2020
So my output will be like this:
S_ID NEEDED_AMOUNT ID
1 20 1
1 20 2
2 5 3
3 20 4
Thanks for any opinion
I would use recursive approach for this :
with cte as (
select id, amount, s_id, needed_amount,
(case when amount = needed_amount then 1 else cnt end) as cnt
from (select t1.*, t2.needed_amount,
row_number() over (partition by t1.s_id order by t1.id) as seq,
count(*) over (partition by t1.s_id) as cnt
from t1 inner join
t2
on t2.s_id = t1.s_id
) t
where seq = 1
), cte1 as (
select c.needed_amount / c.cnt as amount, c.s_id, 1 as start, c.cnt
from cte c
union all
select amount, s_id, start + 1, cnt
from cte1 c1
where start < cnt
)
select s_id, amount, row_number() over (order by s_id) as id
from cte1;
I have a table in MS SQL Server, where are some null values in column "value"
Group ID Value
A 1 10
A 2
A 3
A 4 40
B 1
B 2 20
B 3 30
B 4
I want to update null values by not null in the same group with with the first higher ID, or if there is not any higher in same group, first lower. So the result should look like this.
Group ID Value
A 1 10
A 2 40
A 3 40
A 4 40
B 1 20
B 2 20
B 3 30
B 4 30
Thanks!
You can use windowed version of SUM function in order to determine islands of NULL valued records along with the record having the higher ID in the same group:
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp
FROM mytable
Output:
Group ID Value grp
-----------------------
A 4 40 1
A 3 30 2
A 2 NULL 2
A 1 NULL 2
B 4 40 1
B 3 NULL 1
B 2 20 2
B 1 10 3
You can now wrap the above query in a CTE and use another CTE to do the update:
;WITH CTE AS (
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp
FROM mytable
), ToUpdate AS (
SELECT [Group], ID, Value,
MAX(Value) OVER (PARTITION BY [Group], grp) AS group_value
FROM CTE
)
UPDATE ToUpdate
SET Value = group_value
WHERE Value IS NULL
Demo here
Edit:
The above query doesn't handle the edge case where the very last record within a Group slice is NULL. To handle this case as well you can use the following query:
;WITH CTE AS (
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID) AS grp2
FROM mytable
), ToUpdate AS (
SELECT [Group], ID, Value,
MAX(Value) OVER (PARTITION BY [Group], grp) AS group_value,
MAX(Value) OVER (PARTITION BY [Group], grp2) AS group_value2
FROM CTE
)
UPDATE ToUpdate
SET Value = COALESCE(group_value, group_value2)
WHERE Value IS NULL
Demo here
Please try this-
DATA GENERATION
DECLARE #T TABLE
(
GroupCd CHAR(1),
Id INT,
Value INT
)
INSERT INTO #T
VALUES('A',1,10),
('A',2,NULL),
('A',3,NULL),
('A',4,40),
('B',1,NULL),
('B',2,20),
('B',3,30),
('B',4,NULL)
SOLUTION
UPDATE a
SET a.Value = b.Value
FROM #T a
INNER JOIN
(
SELECT a.GroupCd,a.Id,Coalesce(a.Value,z.Value,z1.Value) Value
FROM #T a
OUTER APPLY
(
SELECT TOP 1 Value
FROM #T b
WHERE a.GroupCd = b.GroupCd
AND b.Value IS NOT NULL AND a.Id < b.Id
ORDER BY Id
)z
OUTER APPLY
(
SELECT TOP 1 Value
FROM #T b
WHERE a.GroupCd = b.GroupCd
AND b.Value IS NOT NULL AND a.Id > b.Id
ORDER BY Id DESC
)z1
)b ON a.GroupCd = b.GroupCd AND a.Id = b.Id
SELECT * FROM #T
OUTPUT
GroupCd Id Value
------- ----------- -----------
A 1 10
A 2 40
A 3 40
A 4 40
B 1 20
B 2 20
B 3 30
B 4 30
(8 rows affected)
You Can try This simple Method
DECLARE #T TABLE
(
GroupCd CHAR(1),
Id INT,
Value INT
)
INSERT INTO #T
VALUES('A',1,NULL),
('A',2,NULL),
('A',3,30),
('A',4,40),
('B',1,10),
('B',2,20),
('B',3,NULL),
('B',4,40)
SELECT
*,
NewVal = COALESCE(Value,(SELECT TOP 1 Value FROM #T WHERE GroupCd = T.GroupCd AND Id > T.Id AND Value IS NOT NULL ORDER BY Id ASC))
FROM #T T
My Result
update MY_TABLE set [value] = [newValue] from (
select [Group] [newGroup],
[Value] [newValue]
from (
select [Group], [Value],
row_number() over (partition by [group] order by [Id] desc) [rn]
from MY_TABLE
where [Value] is not null
) [a] where [rn] = 1
) where [Group] = [newGroup] and [Value] is null
Using SQL Server, I have...
AccDocumentItems Table:
SLId DL1Id DL2Id Debit Credit CurId ExchangeRate Cnt
------------------------------------------------------------------------------
S1 D1 D4 2000 0 1 1000 2
S1 D1 D4 0 6000 1 1500 4
S1 D1 D4 6000 0 1 1200 5
S2 D2 D4 4000 0 2 1000 4
S2 D2 D4 0 2000 2 1000 2
S2 D2 D4 3000 0 2 1500 2
I am trying to write a query in sql that group by CurId then substract Cnt column that has debit greater than 0 from Cnt Column that has Debit 0,for example row 1 to 3 has CurId 1 and row 1 has Debit greater than zero,then we have (2-4+5)=3 and row 4 to 6 has CurId 2 then we have (4-2+2)=4 finally I want sum of them 3*Last ExchangeRate in ExchangeRates Table, and 4*Last ExchangeRate in ExchangeRates Table, then sum of (3*Last ExchangeRate+4*Last ExchangeRate).
ExchangeRates Table:
CurrencyId EffectiveDate ExchangeRate
------------------------------------------------
1 2016-1-1 1000
2 2016-1-2 1200
2 2016-1-3 2000
1 2016-1-4 1500
result=(3*Last ExchangeRate+4*Last ExchangeRate)-((sum of Debit)-(sum
of Credit))
in this example Last ExchangeRate for CurrencyId 1 is 1500, and Last ExchangeRate for CurrencyId 2 is 2000 finally I want this result
result=(3*1500+4*2000)-(15000-8000)
This solution is based on your calculations and works only with 2 CurId's.
declare #cur_1 int = 1
declare #cur_2 int = 2
;with AccDocumentItems as (
select
*
from
(values
('S1','D1','D4',2000,0,1,1000,2)
,('S1','D1','D4',0,6000,1,1500,4)
,('S1','D1','D4',6000,0,1,1200,5)
,('S2','D2','D4',4000,0,2,1000,4)
,('S2','D2','D4',0,2000,2,1000,2)
,('S2','D2','D4',3000,0,2,1500,2)
) t (SLId, DL1Id, DL2Id, Debit, Credit, CurId, ExRate, Cnt)
)
, ExchangeRates as (
select
*
from
(values
(1,'2016-1-1',1000)
,(2,'2016-1-2',1200)
,(2,'2016-1-3',2000)
,(1,'2016-1-4',1500)
) t (CurrencyId, EffectiveDate, ParityRate)
)
select
sum(t.cnt * q.ParityRate) - sum(diff)
from
(
select
CurId, diff = sum(Debit) - sum(Credit), cnt = sum(cnt * case when Debit = 0 then -1 else 1 end)
from
AccDocumentItems
where
CurId in (#cur_1, #cur_2)
group by CurId
) t
join (
select
top 1 with ties *
from
ExchangeRates
order by row_number() over (partition by CurrencyId order by EffectiveDate desc)
) q on t.CurId = q.CurrencyId
This solution works for All CurId's.
Declare #Result int
Declare #DeCr int
Declare #Cur int
Set #DeCr = (
Select SUM(Debit)-SUM(Credit)
From dbo.AccDocumentItems
)
Set #Cur =
(
Select Sum(ParityRate * Zarib)
From
(
Select ExchangeRates.CurrencyId as cuur,ParityRate
From
(
Select Max(EffectiveDate) As MaxDate,CurrencyId
From dbo.ExchangeRates
Group by CurrencyId
)As Table1
left join
dbo.ExchangeRates
ON table1.currencyid = ExchangeRates.CurrencyId
And Table1.MaxDate = EffectiveDate
) As Table3
inner join
(
Select Curid
, sum(cnt*signs) As Zarib
From
(
Select CurId
, Cnt
, Case
When Debit = 0 Then -1
Else 1
End As Signs
From dbo.AccDocumentItems
) As Table2
Group by Curid
) As Table4
On Table3.cuur = Table4.Curid
)
Select #Cur - #DeCr
Declare #Result int
Declare #DeCr int
Declare #Cur int
Set #DeCr = (
Select SUM(Debit)-SUM(Credit)
From dbo.AccDocumentItems
)
Set #Cur =
(
Select Sum(ParityRate * Zarib)
From
(
Select ExchangeRates.CurrencyId as cuur,ParityRate
From
(
Select Max(EffectiveDate) As MaxDate,CurrencyId
From dbo.ExchangeRates
Group by CurrencyId
)As Table1
left join
dbo.ExchangeRates
ON table1.currencyid = ExchangeRates.CurrencyId
And Table1.MaxDate = EffectiveDate
) As Table3
inner join
(
Select Curid
, sum(cnt*signs) As Zarib
From
(
Select CurId
, Cnt
, Case
When Debit = 0 Then -1
Else 1
End As Signs
From dbo.AccDocumentItems
) As Table2
Group by Curid
) As Table4
On Table3.cuur = Table4.Curid
)
set #result = #Cur - #DeCr
Select #result
select all the departments having students with even roll number
Dept No Roll No Student Name
1 1 lee
1 2 scott
2 2 scott
2 4 smith
1 4 smith
This should result in DEpt no 2 as it has only students with roll number divisible by 2
Another(imo easy and lightweight) way is using NOT EXISTS and DISTINCT:
SELECT DISTINCT [Dept No]
FROM dbo.TableName t
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.TableName t2
WHERE t.[Dept No] = t2.[Dept No]
AND t2.[Roll No] % 2 = 1
)
Demo
If there is no odd number all must be even.
You can use GROUP BY with HAVING like this.
Query
SELECT [Dept No]
FROM departments
GROUP BY [Dept No]
HAVING SUM(CASE WHEN [Roll No] % 2 = 0 THEN 1 ELSE 0 END) > 1
AND SUM(CASE WHEN [Roll No] % 2 = 1 THEN 1 ELSE 0 END) = 0
Explanation
The query returns the departments if there a rollno which is even using SUM(CASE WHEN [Roll No] % 2 = 0 THEN 1 ELSE 0 END) > 1. If there is any rollno with odd roll no, SUM(CASE WHEN [Roll No] % 2 = 1 THEN 1 ELSE 0 END) will return non zero sum and that department will be excluded.
declare #t table (Dept int,Rno int,Student varchar(10))
insert into #t (Dept,Rno,Student)values (1,1,'lee'),(1,2,'scott'),(2,2,'scott'),(2,4,'smith'),(1,4,'smith')
SELECT Dept,Rno,Student
FROM (SELECT ROW_NUMBER () OVER (ORDER BY Rno DESC) row_number, Dept,Rno,Student
FROM #t) a WHERE (row_number%2) = 0
I have a table
CREATE TBALE #tableA (PID int, Amount decimal (10,2), CorrectAmount decimal(10,2))
INSERT INTO #TableA (PID, Amount)
VALUES (1,100), (2,100), (3,100), (4,100), (5,100)
Logic to populate correctAmount:
Now I always need the sum of CorrectAmount as 350 (hard coded)
Keep the 'correct amount' as 'Amount' until its 350
Start with the top record until it hits 350.
Expected result:
#tableA:
PID Amount CorrectAmount
-------------------------
1 100 100
2 100 100
3 100 100
4 100 50
5 100 0
;WITH Joined as(
SELECT
t1.PID,
t1.Amount,
RunningTotal = SUM(t2.Amount),
PrevRunningTotal = SUM(t2.Amount) - t1.Amount,
rn = ROW_NUMBER() OVER(ORDER BY SUM(t2.Amount))
FROM #tableA t1
INNER JOIN #tableA t2
ON t2.PID <= t1.PID
GROUP BY
t1.PID, t1.Amount
HAVING
SUM(t2.Amount) > 350
)
UPDATE t
SET t.CorrectAmount =
CASE
WHEN j.PID IS NULL THEN t.Amount
ELSE
CASE
WHEN j.rn = 1 THEN 350 - j.PrevRunningTotal
ELSE 0
END
END
FROM #tableA t
LEFT JOIN Joined j ON j.PID = t.PID