2010-10-22 79 views
4

對於食品在線訂購應用程序,我已經計算出需要多少ingridients(我們稱之爲StockItems),但需要幫助轉換根據他們進來的尺寸(我們稱之爲供應商項目 - 即StockItems + PackSizes)來定購。如何使用最大包裝尺寸將產品轉換成包裝尺寸產品

如果我們以蘋果爲例,我們需要訂購46(該位已經制定出來)。但蘋果只有20盒和5盒。如果可能的話,你需要在最大的盒子裏點蘋果,然後如果你無法訂購確切的數量,那麼就會過度訂購。如果你需要46個蘋果,你可以訂購2盒20盒和2盒5盒,這樣可以給你50個蘋果 - 這些包裝的尺寸最好。

下面的SQL創建所有需要的表並填充它們將數據。 StockItemsRequired表包含我們需要的46個蘋果。我填寫了SupplierItemsRequired表格,其中有2箱20箱和2箱5箱,但這是我需要從其他表中計算出來的部分。

我的問題是:根據上面的規則,什麼是SQL填充SupplierItemsRequired表需要訂購正確的SupplierItems。沒有遊標或循環的解決方案,請 - 我正在尋找一套基於解決方案(我相信它可以完成!)。

我使用SQL Server 2008的

-- create tables 
create table StockItems (StockItemID int primary key identity(1,1), StockItemName varchar(50)) 
create table PackSizes (PackSizeID int primary key identity(1,1), PackSizeName varchar(50)) 
create table SupplierItems (SupplierItemID int primary key identity(1,1), StockItemID int, PackSizeID int) 
create table StockItemsRequired(StockItemID int, Quantity int) 
create table SupplierItemsRequired(SupplierItemID int, Quantity int) 

-- fill tables 
insert into StockItems (StockItemName) values ('Apples') 
insert into StockItems (StockItemName) values ('Pears') 
insert into StockItems (StockItemName) values ('Bananas') 
insert into PackSizes (PackSizeName) values ('Each') 
insert into PackSizes (PackSizeName) values ('Box of 20') 
insert into PackSizes (PackSizeName) values ('Box of 5') 
insert into SupplierItems (StockItemID, PackSizeID) values (1, 2) 
insert into SupplierItems (StockItemID, PackSizeID) values (1, 3) 
insert into StockItemsRequired (StockItemID, Quantity) values (1, 46) 
insert into SupplierItemsRequired (SupplierItemID, Quantity) values (1, 2) 
insert into SupplierItemsRequired (SupplierItemID, Quantity) values (2, 2) 


-- SELECT definition data 
select * from StockItems -- ingredients 
select * from PackSizes -- sizes they come in 
select si.SupplierItemID, st.StockItemName, p.PackSizeName -- how you buy these items 
from SupplierItems si 
inner join StockItems st on si.StockItemID = st.StockItemID 
inner join PackSizes p on si.PackSizeID = p.PackSizeID 

-- SELECT how many of the ingridients you need (StockItemsRequired) 
select st.StockItemID, st.StockItemName, r.Quantity 
from StockItemsRequired r 
inner join StockItems st on r.StockItemID = st.StockItemID 

-- SELECT how you need to buy these items (SupplierItemsRequired) 
select si.SupplierItemID, st.StockItemName, p.PackSizeName, r.Quantity 
from SupplierItemsRequired r 
inner join SupplierItems si on r.SupplierItemID = si.SupplierItemID 
inner join StockItems st on si.StockItemID = st.StockItemID 
inner join PackSizes p on si.PackSizeID = p.PackSizeID 
+1

一些相關鏈接:http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=80857 http://sqlblog.com/blogs/hugo_kornelis/archive/2008/10/27/bin -packing-part-4-the-set-based-disaster.aspx – 2010-10-22 23:44:00

+0

你是否願意將每個項目的PackSizes數量限制爲某個任意數量 - 比如每個供應商項目包含3或5個PackSizes? – 2010-10-23 01:03:34

+0

嗨拉里。理論上,不。但實際上,StockItem不應超過3 PackSizes。所以是的,我們可以將PackSizes的數量限制爲3. – 2010-10-23 11:20:51

回答

1

公然無視這裏的無環要求真實Peso's algorithm調整,以適應這個任務。有興趣瞭解一下如果任何一個人接受基於集合的挑戰,這將如何進行比較。

DECLARE @WantedValue INT; 

SET @WantedValue = 101; 

DECLARE @packsizes TABLE 
(
size INT 
) 

INSERT INTO @packsizes 
SELECT 40 UNION ALL 
SELECT 30 UNION ALL 
SELECT 27 UNION ALL 
SELECT 15 

-- Stage the source data 
DECLARE @Data TABLE 
    (
     RecID INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED, 
     MaxItems INT, 
     CurrentItems INT DEFAULT 0, 
     FaceValue INT, 
     BestOver INT DEFAULT 1 
    ); 

-- Aggregate the source data 
INSERT  @Data 
     (
      MaxItems, 
      FaceValue 
     ) 
SELECT  CEILING(@WantedValue/size), 
     size 
FROM  @packsizes 
order by size desc 


-- Declare some control variables 
DECLARE @CurrentSum INT, 
    @BestOver INT, 
    @RecID INT 

-- Delete all unworkable FaceValues 
DELETE 
FROM @Data 
WHERE FaceValue > (SELECT MIN(FaceValue) FROM @Data WHERE FaceValue >= @WantedValue) 


-- Update BestOver to a proper value 
UPDATE @Data 
SET BestOver = MaxItems 

-- Initialize the control mechanism 
SELECT @RecID = MIN(RecID), 
    @BestOver = SUM(BestOver * FaceValue) 
FROM @Data 

-- Do the loop! 
WHILE @RecID IS NOT NULL 
    BEGIN 
     -- Reset all "bits" not incremented 
     UPDATE @Data 
     SET CurrentItems = 0 
     WHERE RecID < @RecID 

     -- Increment the current "bit" 
     UPDATE @Data 
     SET CurrentItems = CurrentItems + 1 
     WHERE RecID = @RecID 

     -- Get the current sum 
     SELECT @CurrentSum = SUM(CurrentItems * FaceValue) 
     FROM @Data 
     WHERE CurrentItems > 0 

     -- Stop here if the current sum is equal to the sum we want 
     IF @CurrentSum = @WantedValue 
      BREAK 
     ELSE 
      -- Update the current BestOver if previous BestOver is more 
      IF @CurrentSum > @WantedValue AND @CurrentSum < @BestOver 
       BEGIN 
        UPDATE @Data 
        SET BestOver = CurrentItems 

        SET @BestOver = @CurrentSum 
       END 

     -- Find the next proper "bit" to increment 
     SELECT @RecID = MIN(RecID) 
     FROM @Data 
     WHERE CurrentItems < MaxItems 
    END 

-- Now we have to investigate which type of sum to return 
IF @RecID IS NULL 
     SELECT BestOver AS Items, 
      FaceValue, 
      SUM(BestOver*FaceValue) AS SubTotal 
     FROM @Data 
     WHERE BestOver > 0 
     GROUP BY ROLLUP ((BestOver, FaceValue)) 
ELSE 
    -- We have an exact match 
    SELECT CurrentItems AS Items, 
     FaceValue, 
     SUM(CurrentItems*FaceValue) AS SubTotal 
    FROM @Data 
    WHERE CurrentItems > 0 
    GROUP BY ROLLUP ((CurrentItems, FaceValue)) 
+0

謝謝,Martin。如果沒有基於集合的解決方案來解決這個問題(這可能是在你遵循這些鏈接之後),我將需要循環(並且你只是循環通過item no命令,所以不少於100) 。 – 2010-10-23 11:24:25

+0

@Craig - 我正在看[此前](http://www.sqlmag.com/content.aspx?topic=solutions-for-t-sql-challenge-combinations&catpath=sql-server)也許有可能使用。 [Steve Kass's](http://stackoverflow.com/users/139164/steve-kass)解決方案似乎特別有趣,但似乎發佈的代碼出錯了。 – 2010-10-23 11:28:00

相關問題