0

私はSQL2005を使用しており、このカーソルをカーソルではないものに変換して、これが最も効率的な方法であるかどうかを判断しようとしています。

        --Create cursor to determint total cost
DECLARE CostCursor CURSOR FAST_FORWARD
        FOR SELECT  ReceiptQty
                   ,Price
            FROM    @temp_calculate
            ORDER BY UpdateDate DESC
OPEN CostCursor 
FETCH Next FROM CostCursor INTO @ReceiptQty,@Price
WHILE @@FETCH_STATUS = 0
      BEGIN
            IF @OnHandQty >= @ReceiptQty
               BEGIN
                           --SELECT @ReceiptQty,@Price, 1,@OnHandQty
                     SET @Cost = @ReceiptQty * @Price
                     SET @OnHandQty = @OnHandQty - @ReceiptQty
                     SET @TotalCost = @TotalCost + @Cost
               END
            ELSE
               BEGIN
                     IF @OnHandQty < @ReceiptQty
                        BEGIN
                              --SELECT @ReceiptQty,@Price, 2,@OnHandQty
                              SET @Cost = @OnHandQty * @Price
                              SET @OnHandQty = 0
                              SET @TotalCost = @TotalCost + @Cost
                              BREAK;
                        END
               END
            FETCH Next FROM CostCursor INTO @ReceiptQty,@Price
      END
CLOSE CostCursor
DEALLOCATE CostCursor

システムは、最新の受領済み在庫と価格を調べて使用し、手持ちの支払い額を決定する必要があります。

Ex. 1st Iteration: @OnHandQty = 8 RecievedQty = 5 Price = 1 UpdateDate = 1/20 Results:  @HandQty = 3 @TotalCost = $5
2nd Iteration: @OnHandQty = 3 RecievedQty = 6 Price = 2 UpdateDate = 1/10 Results:  @HandQty = 0 @TotalCost = $11

最終結果は、私が手元に持っている在庫に11ドルを支払ったことを示しています。私がこれをC#または他のオブジェクト指向言語で行っていた場合、これは私に再帰を叫びます。再帰CTEの方が効率的だと思いました。私は、次のタイプのクエリに続く階層クエリの再帰CTEを正常に実行しただけであり、これを別の方法で実現するクエリに頭を悩ませることはできませんでした。

どんな助けでも、それがどうあるべきかという簡単なことをいただければ幸いです。

4

3 に答える 3

1

これが再帰CTEソリューションです。それを機能させるには、行番号列が存在する必要があります。そこで、行番号列を含む新しい一時テーブル(@ temp_calculate2)を導出しました。理想的には、行番号の列が@temp_calculateに存在しますが、@temp_calculateの構造を変更できるかどうかについての状況については十分にわかりません。

SQL Server 2005以降で現在の合計を計算するには、結合、サブクエリ、再帰CTE、およびカーソルの4つの基本的な方法があります。最初の3つを示すJerryNixonのブログエントリに出くわしました。結果は非常に素晴らしいです。再帰CTEは、結合およびサブクエリソリューションと比較してほとんど信じられないほど高速です。

残念ながら、彼はカーソルソリューションを含めていませんでした。私はそれを作成し、彼のサンプルデータを使用して自分のコンピューターで実行しました。カーソルソリューションは、再帰CTEよりもわずかに遅いだけです-413ms対273ms。

再帰CTEと比較して、カーソルソリューションが使用するメモリの量がわかりません。私はSQLプロファイラーでそのデータを取得するのに十分ではありませんが、メモリ使用量に関して2つのアプローチがどのように比較されるかを知りたいと思います。

SET NOCOUNT OFF;

DECLARE @temp_calculate TABLE
(
  ReceiptQty INT,
  Price FLOAT,
  UpdateDate DATETIME
);
INSERT INTO @temp_calculate (ReceiptQty, Price, UpdateDate) VALUES (5, 1.0, '2012-1-20');
INSERT INTO @temp_calculate (ReceiptQty, Price, UpdateDate) VALUES (6, 2.0, '2012-1-10');
INSERT INTO @temp_calculate (ReceiptQty, Price, UpdateDate) VALUES (4, 3.0, '2012-1-08');

DECLARE @temp_calculate2 TABLE
(
  RowNumber INT PRIMARY KEY,
  ReceiptQty INT,
  Price FLOAT
);
INSERT INTO @temp_calculate2
  SELECT
      RowNumber = ROW_NUMBER() OVER(ORDER BY UpdateDate DESC),
      ReceiptQty,
      Price
    FROM
      @temp_calculate;

;WITH LineItemCosts (RowNumber, ReceiptQty, Price, RemainingQty, LineItemCost)
AS
(
  SELECT
      RowNumber,
      ReceiptQty,
      Price,
      8, -- OnHandQty
      ReceiptQty * Price
    FROM
      @temp_calculate2
    WHERE
      RowNumber = 1

  UNION ALL

  SELECT
      T2.RowNumber,
      T2.ReceiptQty,
      T2.Price,
      LIC.RemainingQty - LIC.ReceiptQty,
      (LIC.RemainingQty - LIC.ReceiptQty) * T2.Price
    FROM
      LineItemCosts AS LIC
      INNER JOIN @temp_calculate2 AS T2 ON LIC.RowNumber + 1 = T2.RowNumber
)
/* Swap these SELECT statements to get a view of
   all of the data generated by the CTE. */
--SELECT * FROM LineItemCosts;
SELECT
    TotalCost = SUM(LineItemCost)
  FROM
    LineItemCosts
  WHERE
    LineItemCost > 0
  OPTION
    (MAXRECURSION 10000);
于 2012-07-14T19:54:22.510 に答える
0

これがあなたが試すことができる一つのことです。確かに、これは私が現実の世界で扱わなければならないタイプのことではありませんが、私はカーソルから離れています。私はあなたの臨時雇用者のテーブルを取り、によって注文され@temp_calculateたものを追加しました。また、出力に必要なフィールド(@HandQtyと@TotalCost、およびIndividulaCostという新しいフィールド)を追加し、この1つのクエリを実行して、@HandQtyとIndividulaCostに使用することもできます。ここで使用したのと同じ概念を使用して、合計コストを取得および更新して、もう一度実行します。(実際、一時テーブルへの挿入でこれの一部を使用して、ステップを排除できる場合があります。)IDUPDATEDATEUPDATEUPDATE

素晴らしいとは思いませんが、カーソルよりはましだと思います。それで遊んで、あなたがどう思うか見てください。

DECLARE @OnHandQty int
set @OnHandQty = 8
SELECT a.ID,
RECEIPTQty + TOTALOFFSET AS CURRENTOFFSET,
TOTALOFFSET,
CASE WHEN @OnHandQty - (RECEIPTQty + TOTALOFFSET) > 0 THEN RECEIPTQTY * PRICE
     ELSE (@OnHandQty  - TOTALOFFSET) * Price END AS CALCPRICE,
CASE WHEN @OnHandQty - RECEIPTQTY - TOTALOFFSET > 0 THEN @OnHandQty - RECEIPTQTY -   TOTALOFFSET
     ELSE 0 END AS HandQuantity
FROM SO_temp_calculate a
CROSS APPLY (   SELECT ISNULL(SUM(ReceiptQty), 0) AS TOTALOFFSET
                     FROM SO_temp_calculate B where a.id > b.id
                  ) X

戻り値:

ID  CURRENTOFFSET   TOTALOFFSET CALCPRICE   HandQuantity
----------------------------------------------------------------
1          5           0           5           3
2         11           5           6           0

使用している場合は、句と。を使用して関数をSQL SERVER 2012使用できます。そこに行くまで、これはスライディングアグリゲーションに対処する1つの方法です。RANKOVERROWS UNBOUNDED PRECEDING

于 2012-07-13T22:45:27.040 に答える
0
CREATE CLUSTERED INDEX IDX_C_RawData_ProductID_UpdateDate ON #RawData (ProductID ASC , UpdateDate DESC , RowNumber ASC)

    DECLARE @TotalCost Decimal(30,5)
    DECLARE @OnHandQty Decimal(18,5)
    DECLARE @PreviousProductID Int

    UPDATE  #RawData
    SET     @TotalCost = TotalCost = CASE
                                          WHEN RowNumber > 1
                                          AND @OnHandQty >= ReceiptQuantity THEN @TotalCost + (ReceiptQuantity * Price)
                                          WHEN RowNumber > 1
                                          AND @OnHandQty < ReceiptQuantity THEN @TotalCost + (@OnHandQty * Price)
                                          WHEN RowNumber = 1
                                          AND OnHand >= ReceiptQuantity THEN (ReceiptQuantity * Price)
                                          WHEN RowNumber = 1
                                          AND OnHand < ReceiptQuantity THEN (OnHand * Price)
                                     END
           ,@OnHandQty = OnHandQty = CASE
                                          WHEN RowNumber > 1
                                          AND @OnHandQty >= ReceiptQuantity THEN @OnHandQty - ReceiptQuantity
                                          WHEN RowNumber > 1
                                          AND @OnHandQty < ReceiptQuantity THEN 0
                                          WHEN RowNumber = 1
                                          AND OnHand >= ReceiptQuantity THEN (OnHand - ReceiptQuantity)
                                          WHEN RowNumber = 1
                                          AND OnHand < ReceiptQuantity THEN 0
                                     END/*,
            @PreviousProductID = ProductID*/
    FROM    #RawData WITH (TABLOCKX)
    OPTION (MAXDOP 1)

ウェルプ、これは私が思いついた解決策でした。ジェフ・モデンによるこの記事を私に教えてくれた#sqlhelpハッシュタグを見ている素晴らしい人々を考えるのが好きです:

http://www.sqlservercentral.com/articles/T-SQL/68467/

最初のケースのセットが正しく取得されなかったため、テーブルで行番号を使用する必要がありました。この構成を使用して、データセットの取得を、私が実行できた最善の方法である17分から、非常に遅い開発ボックスでの12秒に短縮しました。生産はそれをさらに下げると確信しています。

出力をテストしましたが、同じ製品の2つのアイテムの価格が異なり、更新時間がまったく同じである場合を除いて、以前の方法とまったく同じ結果が得られました。一方の方法は、もう一方の方法とは異なる順序を選択する場合があります。バラエティがペニー以上の場合に一度だけ発生した15,624アイテムのそれ。

ここに答えてくれたみんなに感謝します。私は最終的に別の道を進みましたが、あなたなしではそれを見つけることができなかったでしょう。

于 2012-07-18T20:58:36.507 に答える