Есть ли лучший способ определить границы временного интервала образца серии? - PullRequest
4 голосов
/ 22 апреля 2011

У меня есть таблица платежей с положительными и отрицательными значениями (т. Е. Захватывает и кредиты). Мне нужно определить точки, где мы получили чистую положительную сумму, начиная с последней чистой положительной суммы. Например, если клиент осуществляет эти платежи и получает эти кредиты:

01/01  $100 <-
02/01 -$100
03/01 -$100
04/01  $100
05/01  $100
06/01  $100 <-

... тогда точки будут 01/01 и 06/01: с 02/01 по 04/01 они имеют отрицательный баланс, а с 05/01 - нулевой.

Мой текущий подход начинается с создания списка дат окончания для всех дат с использованием захвата, затем вычисляется дата начала для каждой из них и, наконец, вычисляется чистый захват для этих периодов:

Start      End        NetCaptures
1900/01/01 2011/01/01  $100
2011/01/02 2011/04/01 -$100
2011/04/02 2011/05/01  $100
2011/05/02 2011/06/01  $100

Затем я отбрасываю записи с NetCaptures, равным 0 долл. США или меньше, пересчитываю даты начала, пересчитываю нетто-захваты и повторяю, пока нет записей для удаления, оставляя это.

Start      End        NetCaptures
1900/01/01 2011/01/01  $100
2011/01/02 2011/06/01  $100

Есть ли лучший способ сделать это? Какое умное использование выражений анализа? Это приближается к RBAR. На практике он работает приемлемо быстро (десять минут для записей по 500 тыс., По сравнению с 1,5 до того, как я начал вести учет кредитов таким образом).

* РЕЗУЛЬТАТ *

Несмотря на то, что Microsoft поддерживает функцию изящного скользящего итога, используя эту мысль, я получил код, подобный следующему: вычислить все захваты, подсчитать промежуточный итог до каждого и отбросить те, где есть более ранняя запись с равным или больше промежуточного итога.

CREATE TABLE #Sequences
    (
    OrderID INT NOT NULL,
    Sequence    INT NOT NULL,
    PRIMARY KEY (OrderID, Sequence),
    StartDate   DATE NOT NULL DEFAULT '1900-01-01',
    EndDate DATE NOT NULL,
    CapturesThisPeriod  DECIMAL(18, 2) NOT NULL DEFAULT 0.00,
    )
INSERT INTO #Sequences (OrderID, Sequence, EndDate)
    SELECT OrderID, ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY DateReceived), DateReceived
    FROM Receipts
    WHERE Amount > 0.00

/* Calculate the start date for each period */
UPDATE S
SET StartDate = DATEADD(D, 1, Prev.EndDate)
FROM
    #Sequences AS S
    INNER JOIN #Sequences AS Prev ON S.OrderID = Prev.OrderID AND Prev.Sequence = S.Sequence - 1

/* Calculate the cumulative total for each period */
UPDATE M
SET CumulativeReceipts = R.Receipts
FROM
    #Sequences AS M
    INNER JOIN      
        (
        SELECT
            M.OrderID, M.Sequence, SUM(R.Amount) AS Receipts
        FROM
            #Sequences AS M
            INNER JOIN Receipts AS R ON M.OrderID = R.OrderID AND R.DateReceived <= M.EndDate
        GROUP BY
            M.OrderID, M.Sequence
        ) AS R ON M.OrderID = R.OrderID AND M.Sequence = R.Sequence

/* Delete sequences with do not represent net positive receipts */
DELETE FROM M
FROM #Sequences AS M
WHERE EXISTS (SELECT * FROM #Sequences AS Prev WHERE M.OrderID = Prev.OrderID AND Prev.Sequence < M.Sequence AND Prev.CumulativeReceipts >= M.CumulativeReceipts)

/* Recalculate sequence numbers and dates */
UPDATE S SET Sequence = NewSequence FROM (SELECT Sequence, ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY Sequence) AS NewSequence FROM #Sequences) AS S
UPDATE S
SET StartDate = DATEADD(D, 1, Prev.EndDate)
FROM
    #Sequences AS S
    INNER JOIN #Sequences AS Prev ON S.OrderID = Prev.OrderID AND Prev.Sequence = S.Sequence - 1
    END

/* Calculate net captures per period, and continue with analysis */

1 Ответ

1 голос
/ 22 апреля 2011

Поиск "бегущей суммы";например, http://explainextended.com/2010/01/22/sql-server-running-totals

...