通过买/卖查询句点?

时间:2011-10-15 20:48:59

标签: sql-server tsql

代理商从工厂购买产品并将其出售给普通人。

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在列表中。

仅在首次完成筹码时才

1 个答案:

答案 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