2012-07-13 5 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가 더 효율적일 수 있다고 생각했습니다. 필자는 쿼리 유형에 따라 계층 구조에 대한 재귀 적 CTE를 성공적으로 수행 했으므로이 다른 방식으로 달성 할 수있는 쿼리를 성공적으로 마무리 할 수 ​​없었습니다.

어떤 도움이나 간단한 방법으로 감사하겠습니다.

+0

흠, 당신은 일반적으로 윈도우/OLAP 함수 ('LAG() '를 사용한다고 생각할지라도, 재귀 적으로 그렇게 할 수 있어야한다. 주로 스택 오버플로 유형의 예외에 대해 걱정할 것입니다. 또한,'SET @OnHandQty = 0'은 나를 '틀린'것으로 생각합니다. 실제로 컴퓨터가 알고있는 것 이상을 가지고 있다면 어떻게 될까요? 최소한 일시적으로 네거티브를 허용해야합니다. 그리고 그 중첩 된 'IF'는 불필요합니다. 그래도 나는 이것이 정상적으로 끝날 수 있다고 생각합니다 ... 어떤 경우이든 커서가 필요 없습니다. –

+0

나는 OnHand가 더 많은 경우 부정적인 케이스가 나중에 처리 될 것이고 원래의 개발자와 이야기를 할 수 없습니다. OnHand를 더 많이받은 경우 이미 해당 제품을 발송했다는 의미입니다. 조금 혼란스러워. OnHand = 0이 총 비용에 더 많은 것을 더하는 것을 막을 수 있도록 실제로는 실제로 거기에 중단이 없었습니다. 나는 더 나은 방법이 있어야한다고 생각합니다. – Cericme

+0

재귀 솔루션이 성능을 향상시켜야합니까? – danihp

답변

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) 

Welp이 내가 함께 올라오고 결국 해결책이었다. 나는 제프 MODEN하여이 문서에 저를 가리키는 위해 #sqlhelp의 해시 태그를보고 좋은 사람을 생각하고 싶다 :

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

내가 한 그것은을받지 않았기 때문에 테이블에 ROWNUMBER를 사용하지 끝 첫 번째 사례 집합이 올바르게 나타납니다. 이 구조를 사용하여 데이터 세트를 17 분에서 가장 빨리, 12 초에 내 느린 개발 상자로 가져 왔습니다. 나는 생산이 더 낮아질 것이라 확신한다.

출력을 테스트 한 결과 같은 제품의 2 개 항목이 가격이 같고 업데이트 시간이 동일하지 않은 경우를 제외하고 이전 방식과 똑같은 결과를 얻었습니다. 한 가지 방법은 다른 순서와 다른 순서를 선택할 수 있습니다. 변이가 1 페니 이상인 곳에 한 번만 일어난 15,624 개의 아이템 중 하나입니다.

답장을 보내 주신 모든 분들께 감사드립니다. 나는 궁극적으로 다른 방향으로 갔지만 나는 너없이 그것을 찾지 못했을 것이다.

0

시도해 볼 수있는 한 가지 방법이 있습니다. 틀림없이 이것은 실제 세계를 다루어야하는 유형이 아니지만 커서에서 멀리 떨어져 있습니다. 나는 임시 테이블 @temp_calculate을 가지고 에 의하여 주문되는 ID를 추가했다. 또한 출력에 원하는 필드를 @HandQty 및 @TotalCost와 마찬가지로 IndividualulaCost라는 새 테이블에 추가하고이 하나의 쿼리를 실행하여 UPDATE @HandQty 및 IndividualulaCost에 사용할 수 있습니다. 한 번 더 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 

RETURNS :

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

당신이 OVER 절과 ROWS UNBOUNDED PRECEDINGRANK 기능을 사용할 수 SQL SERVER 2012를 사용한 경우. 거기에 갈 때까지 이것은 슬라이딩 집계를 다루는 한 가지 방법입니다.

+0

이것을 보면 Triangular Join 문제가 발생할까봐 걱정됩니다. http://www.sqlservercentral.com/articles/T-SQL/61539/ – Cericme

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

+0

나는 합계를 얻기 위해 이것을 수정했다. 불행하게도 나는 이것을 조정할 때 OnHandQty를 유지하기 위해 그것을 얻을 수 없었다. – Cericme