March 1, 2006 at 4:51 am
Hi
Hope someone out there can help.
I have a table as follows
Orderref Int, Date DateTime, Quantity Decimal(14,3), Material NVarChar(32), Scheduled Amount
With the following Data
1, 2006/3/1, 100.000, MATERIAL1,0
2, 2006/3/2, 200.000, MATERIAL1,0
3, 2006/3/3, 150.000, MATERIAL1,0
4, 2006/3/1, 100.000, MATERIAL2,0
5, 2006/3/3, 100.000, MATERIAL2,0
I need to set up a stored procedure that will accept a Material and Quantity and update the scheduled Amount up to and including the quantity passed. Priority should be given to those orders with the earliest dates.
e.g If the values MATERIAL1 and a quantity of 350 were passed the records should then show
1, 2006/3/1, 100.000, MATERIAL1,100
2, 2006/3/2, 200.000, MATERIAL1,200
3, 2006/3/3, 150.000, MATERIAL1,50
4, 2006/3/1, 100.000, MATERIAL2,0
5, 2006/3/3, 100.000, MATERIAL2,0
I know this could be done using a cursor but is there a set based alternative ?
Thanks in advance
March 1, 2006 at 6:02 am
I wonder, why people always confuse CURSORS and LOOPS or whether they even know the difference...
One solution:
BEGIN TRAN
DECLARE @OrderID INT,
@OrderAmount FLOAT,
@OrderAmountScheduled FLOAT
WHILE @myAmounttoSchedule > 0
BEGIN
SELECT
@OrderID = OrderID,
@OrderAmount = OrderAmount,
@OrderAmountScheduled = OrderAmountScheduled
FROM myTable
WHERE
OrderID = (SELECT Min(OrderID)
FROM myTable
WHERE mymaterial = myMaterialToSchdeule
AND myScheduledAmount < myOrderAmount
AND OrderDate = SELECT MIN(OrderDate)
FROM myTable
WHERE mymaterial = myMaterialToSchdeule
AND myScheduledAmount < myOrderAmount)
IF OrderID IS NULL -- not enough Orders to accept amount to schedule
BEGIN
ROLLBACK TRAN
RETURN
END
IF @myAmounttoSchedule > @OrderAmount - @OrderAmountScheduled
BEGIN
SET @myAmounttoSchedule = @myAmounttoSchedule - (@OrderAmount - @OrderAmountScheduled)
SET @OrderAmountScheduled = @OrderAmount
END
ELSE IF @myAmounttoSchedule <= @OrderAmount - @OrderAmountScheduled
BEGIN
SET @OrderAmountScheduled = @OrderAmountScheduled + @myAmounttoSchedule
SET @myAmounttoSchedule = 0
END
UPDATE myTable
SET scheduledAmount = @OrderAmountScheduled
WHERE OrderID = @OrderID
END
COMMIT TRAN
RETURN
PS: you can of course work without variables (CASE(d) update statements), but for clarity I would not do so.
Comment on the select statement
SELECT
...
FROM myTable
WHERE
OrderID = (SELECT Min(OrderID)
FROM myTable
WHERE mymaterial = myMaterialToSchdeule
AND myScheduledAmount < myOrderAmount
AND OrderDate = SELECT MIN(OderDate)
FROM myTable
WHERE mymaterial = myMaterialToSchdeule
AND myScheduledAmount < myOrderAmount)
This can of course be done "more simple" in questionable manners, e.g.
"SELECT TOP 1 ... ORDER BY..." OR "SET ROWCOUNT 1 ... then issue the select statement" etc.
_/_/_/ paramind _/_/_/
March 1, 2006 at 7:16 am
a possible solution
DECLARE @Material NVarChar(32), @Qty int
SET @Material = 'MATERIAL1'
SET @Qty = 350
UPDATE a
SET a.[Scheduled Amount] = CASE WHEN (@Qty - b.Summ) > a.Quantity THEN a.Quantity ELSE (@Qty - b.Summ) END
FROM
a
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)) AS [Summ]
FROM
s1
LEFT OUTER JOIN
s2
ON s2.Orderref <> s1.Orderref
AND s2.Material = s1.Material
AND s2.[Date] < s1.[Date]
WHERE s1.Material = @Material
GROUP BY s1.Orderref
) b
ON b.Orderref = a.Orderref
WHERE a.Material = @Material
AND (@Qty - b.Summ) > 0
Far away is close at hand in the images of elsewhere.
Anon.
March 1, 2006 at 7:30 am
I'm afraid you missed the goal
or do you think, i'm payed per line of code?
_/_/_/ paramind _/_/_/
March 1, 2006 at 7:53 am
Well, I prefer David's method. A little tweak to seems (to me) to meet all the requirements (including the priority one)...
SET @Material = 'MATERIAL1'
SET @Qty = 350
UPDATE a
SET a.[Scheduled Amount] = CASE WHEN (@Qty - b.Summ) > a.Quantity THEN a.Quantity ELSE (@Qty - b.Summ) END
FROM
a
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)) AS [Summ]
FROM (SELECT TOP 100 PERCENT * FROM
ORDER BY Date) s1
LEFT OUTER JOIN
s2
ON s2.Orderref <> s1.Orderref
AND s2.Material = s1.Material
AND s2.[Date] < s1.[Date]
WHERE s1.Material = @Material
GROUP BY s1.Orderref
) b
ON b.Orderref = a.Orderref
WHERE a.Material = @Material
AND (@Qty - b.Summ) > 0
Ryan Randall
Solutions are easy. Understanding the problem, now, that's the hard part.
March 1, 2006 at 7:57 am
This means, you didn't understand the goal either
_/_/_/ paramind _/_/_/
March 1, 2006 at 8:06 am
Thanks to you all
I've done some simple tests on both solutions and think both seem to meet the 'goal' bearing in mind the 'tweek' Ryan identified.
Whats your thoughts on the relative performances for larger data tables ?
March 1, 2006 at 8:10 am
I'm really not trying to define YOUR VERY OWN goals, but be very, very careful with the second solution Drop me a message, when you're stuck
_/_/_/ paramind _/_/_/
March 1, 2006 at 8:18 am
paramind
What are your concerns regarding the other solution ?
Andrew
March 1, 2006 at 8:22 am
A little tweak to seems (to me) to meet all the requirements (including the priority one)... |
My solution caters for the priority requirement.
The only problem would be if there was duplicate dates. However if ascending Orderref correlates with ascending date then...
UPDATE a
SET a.[Scheduled Amount] = CASE WHEN (@Qty - b.Summ) > a.Quantity THEN a.Quantity ELSE (@Qty - b.Summ) END
FROM
a
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)) AS [Summ]
FROM
s1
LEFT OUTER JOIN
s2
ON s2.Orderref < s1.Orderref
AND s2.Material = s1.Material
AND s2.[Date] <= s1.[Date]
WHERE s1.Material = @Material
GROUP BY s1.Orderref
) b
ON b.Orderref = a.Orderref
WHERE a.Material = @Material
AND (@Qty - b.Summ) > 0
As for performance, try it and check Execution plans, it might work better by extracting by Material in a temp table, extracting the summary subquery from that temp table into another and then joining them. With correct indexes it may prove faster.
Far away is close at hand in the images of elsewhere.
Anon.
March 1, 2006 at 8:31 am
Sorry David
Looking at it again and I can see it now.
Thanks
March 1, 2006 at 8:37 am
No problem
I had to look twice myself just to make sure
Far away is close at hand in the images of elsewhere.
Anon.
March 1, 2006 at 8:39 am
Just have a look at what will happen when you restart with this situation being the starting point: (again 350 to schedule)
1, 2006/3/1, 100.000, MATERIAL1,50
2, 2006/3/3, 200.000, MATERIAL1,0
3, 2006/3/3, 150.000, MATERIAL1,0
4, 2006/3/3, 100.000, MATERIAL2,0
5, 2006/3/3, 100.000, MATERIAL2,0
_/_/_/ paramind _/_/_/
March 1, 2006 at 8:57 am
I see paramind - that's what you're thinking. In that case, I guess changing...
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)) AS [Summ]
...to...
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)-ISNULL(s2.[Scheduled Amount],0)) AS [Summ]
...might do the trick.
David: sorry about before - I got mixed up with my testing.
Ryan Randall
Solutions are easy. Understanding the problem, now, that's the hard part.
March 1, 2006 at 9:06 am
UPDATE a
SET a.[Scheduled Amount] = a.[Scheduled Amount] + CASE WHEN (@Qty - b.Summ) > (a.Quantity - a.[Scheduled Amount]) THEN (a.Quantity - a.[Scheduled Amount]) ELSE (@Qty - b.Summ - a.[Scheduled Amount]) END
FROM @a a
INNER JOIN (SELECT s1.Orderref, SUM(ISNULL(s2.Quantity,0)-ISNULL(s2.[Scheduled Amount],0)) AS [Summ]
FROM @a s1
LEFT OUTER JOIN @a s2
ON s2.Orderref < s1.Orderref
AND s2.Material = s1.Material
AND s2.[Date] <= s1.[Date]
WHERE s1.Material = @Material
GROUP BY s1.Orderref
) b
ON b.Orderref = a.Orderref
WHERE a.Material = @Material
AND (@Qty - b.Summ) > 0
Far away is close at hand in the images of elsewhere.
Anon.
Viewing 15 posts - 1 through 15 (of 18 total)
You must be logged in to reply to this topic. Login to reply