2011-10-15 47 views
1

代理商從工廠購買產品並將其銷售給正常人。Tsql通過買入/賣出查詢期間?

例如

他買蘋果(買入)(1/1/2010)

第二天,他又買蘋果(購買)(2/1/2010)

後來他買橙色(買入)(2/1/2010)

後來他賣蘋果(賣出)(3/1/2010)

後來他賣蘋果(賣出)(20/1/2010)

< = 這裏買/賣爲蘋果的週期結束。

銷售橙色(22/1/2010)

我需要查詢結果告訴我,每個週期 - 這一點:>

item | numberOfItems | timeFromBeginingToVanish 
------------------------------------------------------ 
apple    2     19 days     // (20/1 - 1/1) 
orange   1      20 days     //(22/1 - 2/1) 

通知:

他可以購買另一個蘋果,而不是賣給他 - 這個WONT在列表中。

僅當堆棧是先完成

+0

*「這不會出現在列表中」 * - 正是將不會出現在列表中,第三個蘋果或整個'apple'行? –

回答

3

編輯:我已經加入另一個濾波器(HAVING子句)只顯示那些completed週期。

此解決方案使用遞歸CTE:

1)來計算運行總數(RunningTotal)爲每個項目和

2)來產生用於每一個項目週期的組ID(PseudoDenseRank)。

CREATE TABLE [Transaction] 
(
    TransactionId INT IDENTITY(10,10) PRIMARY KEY 
    ,Item VARCHAR(100) NOT NULL 
    ,TransactionType CHAR(1) NOT NULL 
    ,Qty INT NOT NULL 
    ,TransactionDate DATE NOT NULL 
    ,CHECK(TransactionType IN ('B', 'S')) --Buy, Sell 
); 

INSERT [Transaction] 
SELECT 'apple', 'B', 1, '2010-01-01' 
UNION ALL 
SELECT 'apple', 'B', 1, '2010-01-02' 
UNION ALL 
SELECT 'orange','B', 1, '2010-01-03' 
UNION ALL 
SELECT 'apple', 'S', 1, '2010-01-03' 
UNION ALL 
SELECT 'apple', 'S', 1, '2010-01-20' 
UNION ALL 
SELECT 'orange','S', 1, '2010-01-22' 

UNION ALL 
SELECT 'apple', 'B', 2, '2010-02-01' 
UNION ALL 
SELECT 'orange','B', 3, '2010-02-02' 
UNION ALL 
SELECT 'apple', 'S', 1, '2010-02-03' 
UNION ALL 
SELECT 'apple', 'S', 1, '2010-02-10' 
UNION ALL 
SELECT 'orange','S', 1, '2010-02-10' 
UNION ALL 
SELECT 'orange','S', 1, '2010-02-11'; 

DECLARE @Results TABLE 
(
    TransactionId INT NOT NULL 
    ,Item VARCHAR(100) NOT NULL 
    ,TransactionType CHAR(1) NOT NULL 
    ,Qty INT NOT NULL 
    ,TransactionDate DATE NOT NULL 
    ,RowNum INT NOT NULL 
    ,PRIMARY KEY (Item, RowNum) 
); 

INSERT @Results 
SELECT * 
     ,ROW_NUMBER() OVER(PARTITION BY t.Item ORDER BY t.TransactionDate ASC, t.TransactionType ASC, t.TransactionId ASC) RowNum 
FROM [Transaction] t; 

WITH CteRecursive 
AS 
(
    SELECT q.Item 
      ,q.RowNum 
      ,CASE WHEN q.TransactionType = 'B' THEN q.Qty END QtyBuy 
      ,CASE WHEN q.TransactionType = 'S' THEN q.Qty END QtySell 
      ,q.TransactionDate 
      ,CASE WHEN q.TransactionType = 'B' THEN q.Qty WHEN q.TransactionType = 'S' THEN -q.Qty END AS RunningTotal 
      ,1 AS PseudoDenseRank 
    FROM @Results q 
    WHERE q.RowNum = 1 
    UNION ALL 
    SELECT prev.Item 
      ,crt.RowNum 
      ,CASE WHEN crt.TransactionType = 'B' THEN crt.Qty END QtyBuy 
      ,CASE WHEN crt.TransactionType = 'S' THEN crt.Qty END QtySell 
      ,crt.TransactionDate 
      ,prev.RunningTotal + CASE WHEN crt.TransactionType = 'B' THEN crt.Qty WHEN crt.TransactionType = 'S' THEN -crt.Qty END 
      ,CASE WHEN prev.RunningTotal = 0 THEN prev.PseudoDenseRank + 1 ELSE prev.PseudoDenseRank END 
    FROM CteRecursive prev 
    INNER JOIN @Results crt ON prev.Item = crt.Item 
    AND  prev.RowNum + 1 = crt.RowNum 
) 
SELECT q.Item 
     ,q.PseudoDenseRank  AS CycleNumber 
     ,SUM(q.QtyBuy)   AS QtyBuyTotal 
     ,SUM(q.QtySell)   AS QtySellTotal 
     ,CASE WHEN ISNULL(SUM(q.QtyBuy), 0) - ISNULL(SUM(q.QtySell),0) = 0 THEN 'Complete' ELSE 'Incomplete' END AS CycleStatus 
     ,MIN(q.TransactionDate) AS CycleStartDate 
     ,MAX(q.TransactionDate) AS CycleEndDate 
     ,CONVERT(VARCHAR(25), MIN(q.TransactionDate), 112) + ' - ' + CONVERT(VARCHAR(25), MAX(q.TransactionDate), 112) AS CycleInterval 
FROM CteRecursive q 
GROUP BY q.Item, q.PseudoDenseRank 
HAVING ISNULL(SUM(q.QtyBuy), 0) - ISNULL(SUM(q.QtySell),0) = 0 
ORDER BY q.Item, q.PseudoDenseRank; 

DROP TABLE [Transaction]; 

結果:

Item CycleNumber QtyBuyTotal QtySellTotal CycleStatus CycleStartDate CycleEndDate CycleInterval 
------ ----------- ----------- ------------ ----------- -------------- ------------ ----------------------- 
apple 1   2   2   Complete 2010-01-01  2010-01-20 2010-01-01 - 2010-01-20 
apple 2   2   2   Complete 2010-02-01  2010-02-10 2010-02-01 - 2010-02-10 
orange 1   1   1   Complete 2010-01-03  2010-01-22 2010-01-03 - 2010-01-22 
--The next row is eliminated by the filter from HAVING 
orange 2   3   2   Incomplete 2010-02-02  2010-02-11 2010-02-02 - 2010-02-11 
+0

工作和這麼多額外的信息...我沒有任何文字。 :) –

+0

如果有3行「蘋果」,「B」,「1」和2行「蘋果」,「S」,1「都有相同的週期數,該解決方案將過濾掉整個週期。然而,在回答我關於這種情況的問題時,OP回答說只應該過濾第三個「B」項。 (我的理解是,第三項應該被忽略,就好像它不存在一樣)。 –

+0

@Andriy M,如果有3行'apple','B',1行和2行'apple','S', 1都具有相同的週期數,該解決方案將過濾掉整個週期。 :正確。讓我澄清:如果他+ 1 + 1,然後-1-1然後+1:所以我們有與2個蘋果開始了循環就把他們(堆棧指針爲0) - 這個我想在查詢中看到。但後來,他只買了一個蘋果(不賣),這樣,他開始了一個週期,但並沒有完成它 - 所以這行蘋果不會在查詢結果中加以考慮。 –