0

인벤토리 계산을위한 가중 평균 비용을 계산할 때 의도 한대로 작동하는 재귀 쿼리가 있습니다. 내 문제는 다른 쿼리로 그룹화 된 동일한 쿼리에서 여러 가중치 평균이 필요하다는 것입니다. 각 키 열에 대해 여러 번 계산하여 문제를 해결할 수 있음을 알고 있습니다. 그러나 쿼리 성능 고려 사항 때문에 한 번 건너 뜁니다. 가끔 1M + 행이 있습니다.재귀 쿼리 (SQL Server)에서 그룹화 된 결과

데이터를 단순화하고 가중치 평균을 간단한 합계로 대체하여 문제를 더 쉽게 따르도록했습니다.

재귀 cte를 사용하여 어떻게 결과를 얻을 수 있습니까? 가중 평균 비용을 계산하려면 재귀 쿼리를 사용해야한다는 것을 기억하십시오. 나는 (ID는 정렬 순서입니다. ID와 키가 함께 독특합니다.) SQL 서버 2016

예 데이터입니다

Id Key1 Key2 Key3 Value 
1 1  1  1  10 
2 1  1  1  10 
3 1  2  1  10 
4 2  2  1  10 
5 1  2  1  10 
6 1  1  2  10 
7 1  1  1  10 
8 3  3  1  10 

예상 결과

Id Key1 Key2 Key3 Value Key1Sum Key2Sum Key3Sum 
1 1  1  1  10  10  10  10 
2 1  1  1  10  20  20  20 
3 1  2  1  10  30  10  30 
4 2  2  1  10  10  20  40 
5 1  2  1  10  40  30  50 
6 1  1  2  10  50  30  10 
7 1  1  1  10  60  40  60 
8 3  3  1  10  10  10  70 

편집

필자는 비판을 잘 받아야 할 때 질문을하는 방법을 훨씬 잘 이해해야합니다.

다음은 예제이며 재귀 쿼리가 필요한 이유입니다. 위 예제에서 Key1에 대한 결과를 얻었지만 동일한 쿼리에서 Key2와 Key3에 대한 결과가 필요합니다. 같은 쿼리를 세 번 반복 할 수 있다는 것을 알고 있지만 바람직하지 않습니다.

DECLARE @InventoryItem AS TABLE (
    IntentoryItemId INT NULL, 
    InventoryOrder INT, 
    Key1 INT NULL, 
    Key2 INT NULL, 
    Key3 INT NULL, 
    Quantity NUMERIC(22,9) NOT NULL, 
    Price NUMERIC(16,9) NOT NULL 
); 

INSERT INTO @InventoryItem (
    IntentoryItemId, 
    InventoryOrder, 
    Key1, 
    Key2, 
    Key3, 
    Quantity, 
    Price 
) 
VALUES 
(1, NULL, 1, 1, 1, 10, 1), 
(2, NULL, 1, 1, 1, 10, 2), 
(3, NULL, 1, 2, 1, 10, 2), 
(4, NULL, 2, 2, 1, 10, 1), 
(5, NULL, 1, 2, 1, 10, 5), 
(6, NULL, 1, 1, 2, 10, 3), 
(7, NULL, 1, 1, 1, 10, 3), 
(8, NULL, 3, 3, 1, 10, 1); 


--The steps below will give me the cost "grouped" by Key1 
WITH Key1RowNumber AS (
    SELECT 
     IntentoryItemId, 
     ROW_NUMBER() OVER (PARTITION BY Key1 ORDER BY IntentoryItemId) AS RowNumber 
    FROM @InventoryItem 
) 

UPDATE @InventoryItem 
    SET InventoryOrder = Key1RowNumber.RowNumber 
FROM @InventoryItem InventoryItem 
INNER JOIN Key1RowNumber 
ON Key1RowNumber.IntentoryItemId = InventoryItem.IntentoryItemId; 

WITH cte AS (
    SELECT 
     IntentoryItemId, 
     InventoryOrder, 
     Key1, 
     Quantity, 
     Price, 
     CONVERT(NUMERIC(22,9), InventoryItem.Quantity) AS CurrentQuantity, 
     CONVERT(NUMERIC(22,9), (InventoryItem.Quantity * InventoryItem.Price)/NULLIF(InventoryItem.Quantity, 0)) AS AvgPrice 
    FROM @InventoryItem InventoryItem 
    WHERE InventoryItem.InventoryOrder = 1 
    UNION ALL 
    SELECT 
     Sub.IntentoryItemId, 
     Sub.InventoryOrder, 
     Sub.Key1, 
     Sub.Quantity, 
     Sub.Price, 
     CONVERT(NUMERIC(22,9), Main.CurrentQuantity + Sub.Quantity) AS CurrentQuantity, 
     CONVERT(NUMERIC(22,9), 
       ((Main.CurrentQuantity) * Main.AvgPrice + Sub.Quantity * Sub.price) 
        /
       NULLIF((Main.CurrentQuantity) + Sub.Quantity, 0) 
     ) AS AvgPrice 
    FROM CTE Main 
    INNER JOIN @InventoryItem Sub 
    ON Main.Key1 = Sub.Key1 
    AND Sub.InventoryOrder = main.InventoryOrder + 1 
) 

SELECT cte.IntentoryItemId, cte.AvgPrice 
FROM cte 
ORDER BY IntentoryItemId 
+0

당신은 무엇을 시도?즉, 어디에서 길을 잃은 것입니까? [최소한의 완전하고 검증 가능한 예제를 만드는 방법] (https://stackoverflow.com/help/mcve)을 검토하고 질문을 수정하십시오. – jhenderson2099

+0

SQL Server 2012 이상을 사용하는 경우 재귀보다 창 함수를 사용하면 성능이 향상 될 수 있습니다. –

+0

내 최신 답변을 확인하십시오. – KumarHarsh

답변

0

왜 1M + 행을 계산하기를 원할 것입니다하는 방법 ?

둘째로 나는 당신의 DB 디자인이 잘못됐다고 생각하니? key1 ,key2,key3unpivoted이어야하며 Keys이라는 열과 각 키 그룹을 식별하는 1 개의 열이 있어야합니다.

아래 예는 분명합니다.

내 쿼리를 최적화 할 수 있다면 행 개수를 제한하려고 시도하는 많은 행을 계산할 수 있습니다.

또한 가능한 경우 평균 가격의 계산 된 열을 유지하는 것이 좋습니다. 테이블이 채워지면 계산하고 저장할 수 있습니다.

출력이 올바른지 먼저 알려주십시오.

DECLARE @InventoryItem AS TABLE (
    IntentoryItemId INT NULL, 
    InventoryOrder INT, 
    Key1 INT NULL, 
    Key2 INT NULL, 
    Key3 INT NULL, 
    Quantity NUMERIC(22,9) NOT NULL, 
    Price NUMERIC(16,9) NOT NULL 
); 

INSERT INTO @InventoryItem (
    IntentoryItemId, 
    InventoryOrder, 
    Key1, 
    Key2, 
    Key3, 
    Quantity, 
    Price 
) 
VALUES 
(1, NULL, 1, 1, 1, 10, 1), 
(2, NULL, 1, 1, 1, 10, 2), 
(3, NULL, 1, 2, 1, 10, 2), 
(4, NULL, 2, 2, 1, 10, 1), 
(5, NULL, 1, 2, 1, 10, 5), 
(6, NULL, 1, 1, 2, 10, 3), 
(7, NULL, 1, 1, 1, 10, 3), 
(8, NULL, 3, 3, 1, 10, 1); 
--select * from @InventoryItem 
--return  
;with cte as 
(
select * 
, ROW_NUMBER() OVER (PARTITION BY Key1 ORDER BY IntentoryItemId) AS rn1 
, ROW_NUMBER() OVER (PARTITION BY Key2 ORDER BY IntentoryItemId) AS rn2 
, ROW_NUMBER() OVER (PARTITION BY Key3 ORDER BY IntentoryItemId) AS rn3 
from @InventoryItem 
) 
,cte1 AS (
     SELECT 
     IntentoryItemId, 

     Key1 keys, 
     Quantity, 
     Price 
     ,rn1 
     ,rn1 rn 
     ,1 pk 
    FROM cte c 

    union ALL 

    SELECT 
     IntentoryItemId, 

     Key2 keys, 
     Quantity, 
     Price 
     ,rn1 
     ,rn2 rn 
     ,2 pk 
    FROM cte c 

    union ALL 

    SELECT 
     IntentoryItemId, 

     Key3 keys, 
     Quantity, 
     Price 
     ,rn1 
     ,rn3 rn 
     ,3 pk 
    FROM cte c 

) 

, cte2 AS (
    SELECT 
     IntentoryItemId, 
     rn, 
     Keys, 
     Quantity, 
     Price, 
     CONVERT(NUMERIC(22,9), InventoryItem.Quantity) AS CurrentQuantity, 
     CONVERT(NUMERIC(22,9), (InventoryItem.Quantity * InventoryItem.Price)) a, 
      CONVERT(NUMERIC(22,9), InventoryItem.Price) b, 

     CONVERT(NUMERIC(22,9), (InventoryItem.Quantity * InventoryItem.Price)/NULLIF(InventoryItem.Quantity, 0)) AS AvgPrice 
     ,pk 
    FROM cte1 InventoryItem 
    WHERE InventoryItem.rn = 1 
    UNION ALL 
    SELECT 
     Sub.IntentoryItemId, 
     sub.rn, 
     Sub.Keys, 
     Sub.Quantity, 
     Sub.Price, 
     CONVERT(NUMERIC(22,9), Main.CurrentQuantity + Sub.Quantity) AS CurrentQuantity, 
     CONVERT(NUMERIC(22,9),Main.CurrentQuantity * Main.AvgPrice), 
     CONVERT(NUMERIC(22,9),Sub.Quantity * Sub.price), 

     CONVERT(NUMERIC(22,9), 
       ((Main.CurrentQuantity * Main.AvgPrice) + (Sub.Quantity * Sub.price)) 
        /
       NULLIF(((Main.CurrentQuantity) + Sub.Quantity), 0) 
     ) AS AvgPrice 
     ,sub.pk 
    FROM CTE2 Main 
    INNER JOIN cte1 Sub 
    ON Main.Keys = Sub.Keys and main.pk=sub.pk 
    AND Sub.rn = main.rn + 1 
    --and Sub.InventoryOrder<=2 
) 
select * 
,(select AvgPrice from cte2 c1 where pk=2 and c1.IntentoryItemId=c.IntentoryItemId) AvgPrice2 
,(select AvgPrice from cte2 c1 where pk=2 and c1.IntentoryItemId=c.IntentoryItemId) AvgPrice3 
from cte2 c 

where pk=1 
ORDER BY pk,rn 

(2012+ SQL의 경우) 대체 솔루션 제이슨에게 많은 감사,

SELECT * 
,CONVERT(NUMERIC(22,9),avg((Quantity * Price)/NULLIF(Quantity, 0)) 
OVER(PARTITION BY Key1 ORDER by IntentoryItemId ROWS UNBOUNDED PRECEDING))AvgKey1Price 
,CONVERT(NUMERIC(22,9),avg((Quantity * Price)/NULLIF(Quantity, 0)) 
OVER(PARTITION BY Key2 ORDER by IntentoryItemId ROWS UNBOUNDED PRECEDING))AvgKey2Price 
,CONVERT(NUMERIC(22,9),avg((Quantity * Price)/NULLIF(Quantity, 0)) 
OVER(PARTITION BY Key3 ORDER by IntentoryItemId ROWS UNBOUNDED PRECEDING))AvgKey3Price 
from @InventoryItem 
order by IntentoryItemId 
+0

1M +는 내 관리자가 계산 된 데이터를 다른 사람에게 유리하게 유지하려고하지 않기 때문에 왜냐하면 당신이 말했듯이 데이터를 모두 동시에 계산하는 대신에 준비를 바꾸는 것이 가장 좋습니다. 재귀 작업이 상당히 비싸기 때문에 적은 반복 작업으로이 작업을 수행 할 수 있다는 꿈이있었습니다. – Senno

0

여기 ... SQL 서버 2012 & 이후에 할

IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL 
DROP TABLE #TestData; 

CREATE TABLE #TestData (
    Id INT, 
    Key1 INT, 
    Key2 INT, 
    Key3 INT, 
    [Value] INT 
    ); 
INSERT #TestData(Id, Key1, Key2, Key3, Value) VALUES 
    (1, 1, 1, 1, 10), 
    (2, 1, 1, 1, 10), 
    (3, 1, 2, 1, 10), 
    (4, 2, 2, 1, 10), 
    (5, 1, 2, 1, 10), 
    (6, 1, 1, 2, 10), 
    (7, 1, 1, 1, 10), 
    (8, 3, 3, 1, 10); 

--============================================================= 

SELECT 
    td.Id, td.Key1, td.Key2, td.Key3, td.Value, 
    Key1Sum = SUM(td.[Value]) OVER (PARTITION BY td.Key1 ORDER BY td.Id ROWS UNBOUNDED PRECEDING), 
    Key2Sum = SUM(td.[Value]) OVER (PARTITION BY td.Key2 ORDER BY td.Id ROWS UNBOUNDED PRECEDING), 
    Key3Sum = SUM(td.[Value]) OVER (PARTITION BY td.Key3 ORDER BY td.Id ROWS UNBOUNDED PRECEDING) 
FROM 
    #TestData td 
ORDER BY 
    td.Id; 

결과 ...

Id   Key1  Key2  Key3  Value  Key1Sum  Key2Sum  Key3Sum 
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- 
1   1   1   1   10   10   10   10 
2   1   1   1   10   20   20   20 
3   1   2   1   10   30   10   30 
4   2   2   1   10   10   20   40 
5   1   2   1   10   40   30   50 
6   1   1   2   10   50   30   10 
7   1   1   1   10   60   40   60 
8   3   3   1   10   10   10   70 
+0

고마워,하지만 원래의 질문을 단순화하고 내 문제가 무엇인지에 대한 오해가 생겼다. 이제는 재귀가 필요/사용되는 이유를 보여주는 예제로 질문을 업데이트했습니다. – Senno