代理商从工厂购买产品并将其出售给普通人。
e.g。
他购买苹果(购买)(1/1/2010)
第二天他又买了苹果(买)(2/1/2010)
后来他购买橙色(购买)(2/1/2010)
后来他出售苹果(卖出)(2010年3月1日)
后来他出售 apple (卖出)(20/1/2010)
< = 此处 苹果的买入/卖出周期已经结束。
卖橙色(2010年1月22日)
我需要查询结果,告诉我每个周期 - 这个:>
item | numberOfItems | timeFromBeginingToVanish
------------------------------------------------------
apple 2 19 days // ( 20/1 - 1/1)
orange 1 20 days //(22/1 - 2/1)
通知:
他可以买另一个苹果而不是卖给他 - 这个WONT在列表中。
仅在首次完成筹码时才
答案 0 :(得分: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