2010-10-22 9 views
4

Pour une application de commande en ligne de produits alimentaires, j'ai déterminé le nombre d'ingrédients dont nous avons besoin (StockItems), mais j'ai besoin d'aide pour convertir à ce que nous devons commander en fonction de quelles tailles ils viennent (que nous appelons SupplierItems - soit StockItems + PackSizes). Si nous prenons des pommes à titre d'exemple, nous devons commander 46 (ce bit a déjà été élaboré). Mais les pommes ne viennent que dans des boîtes de 20 et des boîtes de 5. Vous voulez commander des pommes dans les plus grandes cases si possible et ensuite sur-commander si vous ne pouvez pas commander le montant exact. Si vous avez besoin de 46 pommes, vous commanderez 2 boîtes de 20 et 2 boîtes de 5, ce qui vous donnera 50 pommes - le meilleur que vous obtiendrez avec ces emballages.Comment convertir les produits en tailles de produit avec la plus grande taille

Le SQL ci-dessous crée toutes les tables nécessaires et remplit les données. La table StockItemsRequired contient les 46 pommes dont nous avons besoin. J'ai rempli la table SupplierItemsRequired avec les 2 boîtes de 20 et 2 boîtes de 5, mais c'est la partie que j'ai besoin de travailler à partir des autres tables.

Ma question est: quel est le SQL pour remplir la table SupplierItemsRequired avec le bon SupplierItems qui doivent être commandés - basé sur les règles ci-dessus. Aucune solution avec des curseurs ou des boucles, s'il vous plaît - Je suis à la recherche d'une solution basée sur un ensemble (Je suis sûr que cela peut être fait!).

J'utilise 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

Quelques liens connexes: 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 –

+0

Êtes-vous prêt à limiter le nombre de PackSizes par article à un nombre arbitraire - disons 3 ou 5 PackSizes par article du fournisseur? –

+0

Salut Larry. Théoriquement, non. Mais pratiquement, il ne devrait pas y avoir plus de 3 PackSizes pour un StockItem. Alors oui, nous pourrions limiter le nombre de PackSizes à 3. –

Répondre

1

sans tenir compte de la manière flagrante aucune exigence de boucle est Peso's algorithm peaufiné ici pour adapter cette tâche. Soyez intéressé de voir comment cela se compare si quelqu'un prend le défi basé ensemble.

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

Merci, Martin. S'il n'y a pas de solution basée sur un ensemble à ce problème (ce qui pourrait être probable après avoir suivi les liens que vous avez quittés), je vais devoir boucler (et vous ne faites que passer en boucle un item, donc pas moins de 100) . –

+0

@Craig - Je regardais [plus tôt] (http://www.sqlmag.com/content.aspx?topic=solutions-for-t-sql-challenge-combinations&catpath=sql-server) peut-être quelque chose pourrait être utile. La solution [Steve Kass] (http://stackoverflow.com/users/139164/steve-kass) semble particulièrement intéressante mais quelque chose semble avoir mal tourné avec le code qui a été posté. –

Questions connexes