Published on

November 25, 2003

Расчет накопительных итогов, промежуточных итогов и общего итога в SQL Server

Если вам когда-либо понадобилось показать подробные данные отдельных транзакций, сохраняя при этом накопительные итоги, промежуточные итоги и столбцы общего итога одновременно, то этот статья для вас. В этой статье мы рассмотрим различные методы расчета и суммирования информации по нескольким строкам без использования курсора. Для этого мы будем использовать основные операторы SELECT и SQL-клаузы и функции, такие как SUM и CASE.

Пример данных

Прежде чем мы приступим к примерам, давайте создадим набор тестовых данных, которые мы будем использовать. Мы создадим таблицу “Заказы” со следующей структурой:

create table Orders (
    OrderID int identity,
    OrderAmt Decimal(8,2),
    OrderDate SmallDatetime
)

Мы заполним эту таблицу следующим набором записей:

OrderIDOrderAmtOrderDate
110.502003-10-11 08:00:00
211.502003-10-11 10:00:00
31.252003-10-11 12:00:00
4100.572003-10-12 09:00:00
519.992003-10-12 11:00:00
647.142003-10-13 10:00:00
710.082003-10-13 12:00:00
87.502003-10-13 19:00:00
99.502003-10-13 21:00:00

Накопительный итог для каждой записи

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

SELECT OrderId, OrderDate, O.OrderAmt,
    (SELECT SUM(OrderAmt) FROM Orders WHERE OrderID <= O.OrderID) AS 'Накопительный итог'
FROM Orders O

Выполнение этого запроса для нашей таблицы Заказы даст нам следующие результаты:

OrderIDOrderDateOrderAmtНакопительный итог
12003-10-11 08:00:0010.5010.50
22003-10-11 10:00:0011.5022.00
32003-10-11 12:00:001.2523.25
42003-10-12 09:00:00100.57123.82
52003-10-12 11:00:0019.99143.81
62003-10-13 10:00:0047.14190.95
72003-10-13 12:00:0010.08201.03
82003-10-13 19:00:007.50208.53
92003-10-13 21:00:009.50218.03

Как видите, столбец “Накопительный итог” отображает накопительный итог вместе с другой информацией о столбцах, связанных с каждой записью таблицы Заказы. Накопительный итог рассчитывается путем суммирования OrderAmt для всех заказов, где OrderID меньше или равен OrderID текущей отображаемой записи.

Накопительный итог для каждой даты заказа

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

SELECT O.OrderId, CONVERT(char(10), O.OrderDate, 101) AS 'Дата заказа', O.OrderAmt,
    (SELECT SUM(OrderAmt) FROM Orders WHERE OrderID <= O.OrderID AND CONVERT(char(10), OrderDate, 101) = CONVERT(char(10), O.OrderDate, 101)) AS 'Накопительный итог'
FROM Orders O
ORDER BY OrderID

Результаты, возвращаемые этим запросом с использованием нашей таблицы Заказы, будут следующими:

OrderIDДата заказаOrderAmtНакопительный итог
110/11/200310.5010.50
210/11/200311.5022.00
310/11/20031.2523.25
410/12/2003100.57100.57
510/12/200319.99120.56
610/13/200347.1447.14
710/13/200310.0857.22
810/13/20037.5064.72
910/13/20039.5074.22

Обратите внимание, что “Накопительный итог” начинается с значением 10.50, а затем становится 22.00 и, наконец, 23.25 для OrderID 3, поскольку все эти записи имеют одну и ту же дату заказа (10/11/2003). Но когда отображается OrderID 4, накопительный итог сбрасывается, и начинается новый накопительный итог. Это происходит потому, что у OrderID 4 другая дата для его даты заказа, чем у OrderID 1, 2 и 3. Расчет этого накопительного итога для каждой уникальной даты достигается с помощью коррелированного подзапроса с дополнительным условием WHERE, которое гарантирует, что даты заказов на разных записях являются одним и тем же днем.

Накопительные итоги с промежуточными итогами и общим итогом

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

SELECT O.OrderID, CONVERT(char(10), O.OrderDate, 101) AS 'Дата заказа', O.OrderAmt,
    CASE
        WHEN OrderID = (SELECT TOP 1 OrderId FROM Orders WHERE CONVERT(char(10), OrderDate, 101) = CONVERT(char(10), O.OrderDate, 101) ORDER BY OrderID DESC)
        THEN (SELECT CAST(SUM(OrderAmt) AS char(10)) FROM Orders WHERE OrderID <= O.OrderID AND CONVERT(char(10), OrderDate, 101) = CONVERT(char(10), O.OrderDate, 101))
        ELSE ' '
    END AS 'Промежуточный итог',
    CASE
        WHEN OrderID = (SELECT TOP 1 OrderId FROM Orders ORDER BY OrderDate DESC)
        THEN (SELECT CAST(SUM(OrderAmt) AS char(10)) FROM Orders)
        ELSE ' '
    END AS 'Общий итог'
FROM Orders O
ORDER BY OrderID

Результаты, возвращаемые этим оператором SELECT, будут выглядеть следующим образом:

OrderIDДата заказаOrderAmtПромежуточный итогОбщий итог
110/11/200310.50
210/11/200311.50
310/11/20031.2523.25
410/12/2003100.57
510/12/200319.99120.56
610/13/200347.14
710/13/200310.08
810/13/20037.50
910/13/20039.5074.22218.03

В этом примере первый оператор CASE управляет печатью столбца “Промежуточный итог”. Промежуточный итог печатается только на последнем заказе дня, что определяется с помощью коррелированного подзапроса. Второй оператор CASE печатает “Общий итог”, который печатается только вместе с самым последним заказом. Каждый из этих операторов CASE использует клаузу TOP для определения того, какой OrderID является правильным для печати “Общего итога”.

Заключение

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

Click to rate this post!
[Total: 0 Average: 0]

Let's work together

Send us a message or book free introductory meeting with us using button below.