Tsql запрос для периодов на покупку / продажу? - PullRequest
1 голос
/ 16 октября 2011

Агент покупает продукты на фабрике и продает их нормальным людям.

Например,

Он покупает Яблоко (покупает) (01.01.2010)

на следующий день он снова покупает яблоко (покупка) (01.02.2010)

позже он покупает апельсин (покупка) (2/1 /2010)

позже он продает яблоко (продажа) (01.03.2010)

позже он продает яблоко (продажа) (20 /1/2010)

<= <strong>здесь цикл покупки / продажи для apple завершен.

продает оранжевый (22/1/2010)

мне нужен результат запроса, который сообщает мне для каждого цикла - это:>

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

уведомление:

он может купить другое яблоко и не продавать его - это НЕ БУДЕТ в списке.

только тогда, когда стек ПЕРВЫЙ БУДЕТ ЗАВЕРШЕН

1 Ответ

3 голосов
/ 16 октября 2011

Редактировать: Я добавил еще один фильтр (предложение HAVING) для отображения только тех циклов, которые completed.

В этом решении используется рекурсивный CTE:

1) для вычисления промежуточного итога (RunningTotal) для каждого элемента и

2) для генерации идентификатора группы (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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...