Отличный способ получить максимальное значение промежуточного итога в TSQL - PullRequest
2 голосов
/ 22 января 2010

У нас есть таблица транзакций, которая имеет следующую структуру:

TranxID    int (PK and Identity field)
ItemID     int
TranxDate  datetime
TranxAmt   money

TranxAmt может быть положительным или отрицательным, поэтому промежуточная сумма этого поля (для любого ItemID) будет увеличиваться и уменьшаться с течением времени. Получить текущую сумму, очевидно, просто, но мне нужен эффективный способ получить наибольшее значение промежуточной суммы и TranxDate, когда это произошло. Обратите внимание, что TranxDate не является уникальным, и из-за некоторой обратной даты поле идентификатора не обязательно должно быть в той же последовательности, что и TranxDate для данного элемента. В настоящее время мы делаем что-то вроде этого (@tblTranx - это табличная переменная, содержащая только транзакции для данного элемента):

SELECT Top 1 @HighestTotal = z.TotalToDate, @DateHighest = z.TranxDate
FROM
    (SELECT a.TranxDate, a.TranxID, Sum(b.TranxAmt) AS TotalToDate
    FROM @tblTranx AS a
    INNER JOIN @tblTranx AS b ON a.TranxDate >= b.TranxDate
    GROUP BY a.TranxDate, a.TranxID) AS z
ORDER BY z.TotalToDate DESC

(Группировка TranxID устраняет проблему, вызванную дублированием значений дат)

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

Вопрос в том, можно ли сделать это лучше, чтобы мы могли обрабатывать эти значения на лету (для нескольких элементов одновременно), не попадая в ловушку RBAR (некоторые ItemID имеют сотни записей). Если это так, может ли это быть адаптировано для получения максимальных значений подмножеств транзакций (на основе TransactionTypeID, не включенного выше). В настоящее время я делаю это с SQL Server 2000, но SQL Server 2008 скоро вступит во владение, поэтому можно использовать любые приемы SQL Server.

1 Ответ

3 голосов
/ 22 января 2010

SQL Server отстой в подсчете промежуточных итогов.

Вот решение для вашего запроса (который группируется по датам):

WITH    q AS
        (
        SELECT  TranxDate, SUM(TranxAmt) AS TranxSum
        FROM    t_transaction
        GROUP BY
                TranxDate
        ),
        m (TranxDate, TranxSum) AS
        (
        SELECT  MIN(TranxDate), SUM(TranxAmt)
        FROM    (
                SELECT  TOP 1 WITH TIES *
                FROM    t_transaction
                ORDER BY
                        TranxDate
                ) q
        UNION ALL
        SELECT  DATEADD(day, 1, m.TranxDate),
                m.TranxSum + q.TranxSum
        FROM    m
        CROSS APPLY
                (
                SELECT  TranxSum
                FROM    q
                WHERE   q.TranxDate = DATEADD(day, 1, m.TranxDate) 
                ) q
        WHERE   m.TranxDate <= GETDATE()
        )
SELECT  TOP 1 *
FROM    m
ORDER BY
        TranxSum DESC
OPTION (MAXRECURSION 0)

Вам нужно , чтобы индекс имел значение TranxDate для быстрой работы.

...