2012-04-30 2 views
1

まず、私はカーソルが一般的に悪であり、使用されるべきではないことをよく認識していると述べます。この特定の問題に対するセットベースの解決策を考え出すことはできませんでした。セットベースの操作を行うように私に教えてもらえれば、この特定の問題をどのようにコード化するかを教えてもらえればそれだけです。ネストされたカーソルを使用して「無効なオブジェクト名」を取得する必要があります

基本的に、私は購入する必要があるいくつかの在庫品を持っています。私は最も安い利用可能な価格に基づいて購入したいと思っています。ここで私はサプライヤーの価格と在庫レベルを知っています。また、可能な場合はパックサイズで購入したいというパックサイズの問題もあります。

私は既に#needordersに購入する必要がある物品のリストと、供給者の在庫レベルと価格を#orderedpricesに引き出しました。私はカーソルCUR_NEEDEDを反復処理し、二次カーソルCUR_AVAILABLE作成しています下:

DECLARE CUR_NEEDED CURSOR LOCAL SCROLL_LOCKS 
FOR 
SELECT  GoodID 
     , ConditionID 
     , QuantityToShip 
     , OrderStatusID 
     , RetailerID 
     , PackSize 
FROM  #needorders 
ORDER BY  GoodID 
     , ConditionID 
     , PurchaseDate DESC 
FOR UPDATE 

OPEN CUR_NEEDED 
FETCH NEXT FROM CUR_NEEDED INTO @GoodID, @ConditionID, @QuantityToShip, @OrderStatusID, @RetailerID, @PackSize 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
     DECLARE CUR_AVAILABLE CURSOR LOCAL SCROLL_LOCKS 
     FOR 
     SELECT  SupplierStocklistItemID 
       , SupplierID 
       , StockLevel 
       , SupplierCurrencyID 
       , CostPrice 
     FROM  #orderedprices 
     WHERE  #orderedprices.GoodID = @GoodID 
     AND  #orderedprices.ConditionID = @ConditionID 
     AND  #orderedprices.StockLevel > 0 
     ORDER BY #orderedprices.PriceRank 
     FOR UPDATE 

     OPEN CUR_AVAILABLE 
     FETCH NEXT FROM CUR_AVAILABLE INTO @SupplierStocklistItemID, @SupplierID, @StockLevel, @SupplierCurrencyID, @CostPrice 

     WHILE @@FETCH_STATUS = 0 
      BEGIN 
       /* 
       Buy as many @PackSize as we need to cover how many we require, unless the supplier 
       only has a certain number, in which case buy that number. 
       E.g., need 14, pack size 5, 2 suppliers 
       Supplier A has 11 
       Supplier B has 40 
       Buy 9 from Supplier A, with our remaining need being 3. 
       Buy 5 from supplier B, with our remaining need being -2 
       */ 
       --feed rows into #supplierpurchasesbase while @StockLevel > 0 

       --Figure out how many we need to buy, based upon PackSize 
       IF @QuantityToShip % @PackSize > 0 
        BEGIN 
         SET @Buy = @QuantityToShip - @QuantityToShip % @PackSize + @PackSize 
        END 
       ELSE 
        BEGIN 
         SET @Buy = @QuantityToShip 
        END 

       IF @StockLevel < @Buy 
        BEGIN 
         --PRINT 'Supplier only has ' + CAST(@StockLevel AS VARCHAR) + ' for us to buy.' 
         SET @Buy = @StockLevel 
        END 

       INSERT INTO #supplierpurchasesbase (
         GoodID 
        , ConditionID 
        , SupplierStocklistItemID 
        , Quantity 
        , SupplierID 
        , SupplierCurrencyID 
        , CostPrice 
        , RetailerID) 
       SELECT @GoodID 
        , @ConditionID 
        , @SupplierStocklistItemID 
        , @Buy 
        , @SupplierID 
        , @SupplierCurrencyID 
        , @CostPrice 
        , @RetailerID 

       --update @QuantityToShip & the row in CUR_AVAILABLE 
       IF @StockLevel <= @Buy 
        BEGIN 
         UPDATE CUR_AVAILABLE 
         SET StockLevel = @StockLevel - @Buy 
         WHERE CURRENT OF CUR_AVAILABLE 

         SET @QuantityToShip = 0 
        END 
       ELSE 
        BEGIN 
         UPDATE CUR_AVAILABLE 
         SET StockLevel = 0 
         WHERE CURRENT OF CUR_AVAILABLE 

         SET @QuantityToShip = @QuantityToShip - @Buy 
        END 

       --update the stocklevel so we don't see the thing again if we've used it up. 

       IF @QuantityToShip = 0 --Don't need any more 
        BEGIN 
         UPDATE CUR_NEEDED 
         SET OrderStatusID = @StatusPendingPO 
         WHERE CURRENT OF CUR_NEEDED 

         BREAK 
        END 
       ELSE --Need more, move next, if we can 
        FETCH NEXT FROM CUR_AVAILABLE INTO @SupplierStocklistItemID, @SupplierID, @StockLevel, @SupplierCurrencyID, @CostPrice 
      END 
     CLOSE  CUR_AVAILABLE 
     DEALLOCATE CUR_AVAILABLE 

     FETCH NEXT FROM CUR_NEEDED INTO @GoodID, @ConditionID, @QuantityToShip, @OrderStatusID, @RetailerID, @PackSize 
    END 
CLOSE  CUR_NEEDED 
DEALLOCATE CUR_NEEDED 

私はに実行している問題は、私は、私はエラー

無効なオブジェクト名「を取得してい得るということですがCUR_AVAILABLE '

CURRENT OF CUR_AVAILABLEを更新しようとしています。

CUR_AVAILABLEカーソルを@CUR_AVAILABLEとして定義しようとしましたが、別のエラーが発生しました。私はCUR_NEEDEDWHILEループの外側のCUR_AVAILABLEカーソルを定義しようとしましたが、カーソルを閉じる/割り当てを解除しないようにしました。これは動作していないようです。

ここで私が間違っていると思うアイデアは、(セットベースの解決策がない限り、セットを使用している以外は)

答えて

0

問題は二重だった:更新構文があってはならない。むしろ

UPDATE CUR_AVAILABLE 
SET StockLevel = @StockLevel - @Buy 
WHERE CURRENT OF CUR_AVAILABLE 

は、構文は次のようになります。

UPDATE #orderedprices 
SET StockLevel = @StockLevel - @Buy 
WHERE CURRENT OF CUR_AVAILABLE 

また、更新可能にするために、一時テーブルをするために必要な

ALTER TABLE #orderedprices ADD CONSTRAINT PRIMARY KEY CLUSTERED (RowCtr) 

レッスンは知っていますが、確かに私にはかなりの悲しみがありました解決策を見つける!

2

次のクエリは再帰的なCTEを使用するため、真にセットベースのソリューションとはみなされません。それにもかかわらず、私はまだそれはあなたの2つのカーソルよりも良好に機能するために(または非常に少なくとも、試してみる価値であることを)期待:

WITH buys (
    GoodID, 
    ConditionID, 
    SupplierStocklistItemID, 
    Quantity, 
    SupplierID, 
    SupplierCurrencyID, 
    CostPrice, 
    RetailerID, 
    PriceRank, 
    RemainingNeed, 
    PackSize 
) 
AS (
    SELECT 
    GoodID, 
    ConditionID, 
    SupplierStocklistItemID = 0, 
    Quantity    = 0, 
    SupplierID    = 0, 
    SupplierCurrencyID  = 0, 
    CostPrice    = CAST(0.00 AS decimal(10,2)), 
    RetailerID, 
    PriceRank    = 0, 
    RemainingNeed   = QuantityToShip, 
    PackSize 
    FROM #needorders 
    UNION ALL 
    SELECT 
    p.GoodID, 
    p.ConditionID, 
    p.SupplierStockListItemID, 
    Quantity = y.CurrentBuy, 
    p.SupplierID, 
    p.SupplierCurrencyID, 
    p.CostPrice, 
    b.RetailerID, 
    p.PriceRank, 
    RemainingNeed = b.RemainingNeed - y.CurrentBuy, 
    b.PackSize 
    FROM #orderedprices p 
    INNER JOIN buys b ON p.GoodID = b.GoodID 
    AND p.ConditionID = b.ConditionID 
    AND p.PriceRank = b.PriceRank + 1 
    CROSS APPLY (
    SELECT RemainingNeedAdjusted = 
     (b.RemainingNeed + b.PackSize - 1)/b.PackSize * b.PackSize 
) x 
    CROSS APPLY (
    SELECT CurrentBuy = CASE 
     WHEN x.RemainingNeedAdjusted > p.StockLevel 
     THEN p.StockLevel 
     ELSE x.RemainingNeedAdjusted 
    END 
) y 
    WHERE p.StockLevel > 0 
    AND b.RemainingNeed > 0 
) 
SELECT 
    GoodID, 
    ConditionID, 
    SupplierStocklistItemID, 
    Quantity, 
    SupplierID, 
    SupplierCurrencyID, 
    CostPrice, 
    RetailerID 
FROM buys 
WHERE PriceRank > 0 
ORDER BY 
    GoodID, 
    ConditionID, 
    PriceRank 

基本的には、CTEは、クエリがに挿入されたものに行がほぼ同じ構成します#supplierpurchasesbaseがありますが、内部変数の種類として機能する補助列が追加されています。ただし、最終的なSELECTによって引っ張られることはありません。

アンカー部分は、補助列の初期値と共に#needorderedテーブルに基づいて0個のレコードセットを形成します。再帰部分には、購入する量を計算し、次の反復のための「残りの必要性」量を更新し、次の反復が必要かどうかをチェックするすべてのロジックが含まれます。

特定の前提がありました。実際の状況と一致しない場合、あなたの周りにあなたの道を見つけることができれば幸いです。例えば、数量、パックサイズは整数であると仮定され、整数の除算を使用するため、ロジックの一部はそれに依存します。また、PriceRankは、1から始まる整数のシーケンスであり、(GoodID, ConditionID)に一意であると仮定されています。

最小限のテストセットアップと同様に、このスクリプトは、on SQL Fiddleという名前でテスト、修正、テストされています。

+0

ありがとう、アンドリー!私はこれを打つつもりで、もし私が2つのカーソルの機能を複製することができれば、私はあなたの答えにフラグを立てるでしょう - カーソルを扱う方が望ましいでしょう(私は多分3過去15年間、私はこのような人に仕事をさせるような問題に遭遇しました)! –

関連する問題