2012-07-13 116 views
0

我在SQL 2005中,我試圖將此遊標轉換爲不是遊標的東西,以確定這是否是最有效的方法。SQL中的庫存價格計算

 --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可能更有效率。我只成功完成了任何針對Heirarchy的Recursive CTE,其中包括以下類型的查詢,並且我還沒有能夠成功地將我的頭部包裹在一個可以以另一種方式實現的查詢中。

任何幫助或一個簡單的多數民衆贊成如何必須將不勝感激。

+0

嗯,你應該能夠遞歸做到這一點,但我覺得你通常會使用一個窗口/ OLAP功能('LAG()',我想,但我不認爲2005年有它)。我主要擔心堆棧溢出類型的異常。另外,SET @OnHandQty = 0讓我覺得「錯誤」 - 如果你的身體比計算機知道的多,會發生什麼?你應該至少暫時允許手頭的負面信息。而且嵌套的「IF」應該是不必要的。雖然,我認爲這可以照常進行......無論如何,你都不需要光標。 – 2012-07-13 20:03:46

+0

我繼承這一點,不能跟原來的開發商,早已不復存在,負案實際上是處理了一下後,如果我們有更多的現有量那麼我們收到對其採取在另一個地方的責任。如果我們有更多的接收,然後OnHand,這意味着我們已經將該產品運出。有點混亂。它實際上並沒有在那裏休息,所以OnHand = 0阻止它增加更多的總成本。我只是覺得應該有更好的方法。 – Cericme 2012-07-13 20:29:06

+0

您確定遞歸解決方案應該可以提高性能嗎? – danihp 2012-07-13 20:57:02

回答

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包括hashtag指着我這篇文章傑夫MODEN:

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

我做最終不得不使用ROWNUMBER在桌子上,因爲它沒有得到第一組案件正確。使用這個構造,我把檢索數據集的時間從17分鐘縮短到12秒,這在我的速度極慢的開發箱中是最好的。我相信生產會降低更多。

我測試過的輸出,我也得到同樣的結果如舊的方式,除了當2項相同的產品有不同的價格和更新的時間是完全一樣的。一種方法可能會選擇另一種順序。它只有一次發生一次的變量是=一分錢的15,624項。

感謝大家誰在這裏找到答案。我最終採取了不同的方式,但是如果沒有你,我不會找到它的。

0

以下是您可以嘗試的一件事。誠然,這不是我必須處理現實世界的事情,但我遠離遊標。我把你的臨時表@temp_calculate並添加了一個ID訂購UPDATEDATE。您還可以將您希望輸入的字段添加到臨時表中 - @HandQty和@TotalCost以及一個名爲IndividulaCost的新字段 - 並運行這一個查詢並將其用於UPDATE @HandQty和IndividulaCost。之後再運行一個UPDATE,採用此處使用的相同概念來獲取和更新總成本。 (實際上,您可能可以在插入到臨時表時使用其中的一部分並消除一個步驟。)

我不認爲它很棒,但我確實相信它比遊標更好。玩它,看看你的想法。

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你可以使用RANK功能與OVER條款和ROWS UNBOUNDED PRECEDING。直到你到達那裏,這是處理Sliding Aggregations的一種方法。

+0

看着這個,我擔心它會碰到三角形連接問題。 http://www.sqlservercentral.com/articles/T-SQL/61539/ – Cericme 2012-07-16 13:24:26

1

這是一個遞歸的CTE解決方案。行號列必須存在以使其工作。所以我導出了一個包含行號列的新臨時表(@ temp_calculate2)。理想情況下,行號列會出現在@temp_calculate中,但我對您是否可以修改@temp_calculate的結構知之甚少。

事實證明,有四種基本方法可以計算SQL Server 2005及更高版本中的運行總數:通過連接,子查詢,遞歸CTE和遊標。我跑過blog entry by Jerry Nixon,證明前三名。結果非常驚人。與連接和子查詢解決方案相比,遞歸CTE幾乎快得難以置信。

不幸的是,他沒有包含遊標解決方案。我創建了一個,並使用他的示例數據在我的電腦上運行它。光標解決方案只比遞歸CTE稍慢 - 413ms與273ms。

與遞歸CTE相比,我不知道遊標解決方案使用多少內存。我用SQL Profiler來獲取這些數據不夠好,但我很好奇,看看這兩種方法如何比較內存使用情況。

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); 
+0

我沒想過要爲遞歸引入一個ID。 – Cericme 2012-07-16 13:25:26

+0

我沒有得到這個修改,以獲得總數。不幸的是,我無法在保留OnHandQty的時候調整它。 – Cericme 2012-07-16 19:30:06