Вычисление агрегатов
Вычисление итоговых и промежуточных сумм
Чтобы получить детализированные и сводные суммы значений данных в таблице, можно использовать и другие агрегатные функции.
Вычисление итоговых сумм
Итоговые суммы необходимы для ответа на вопросы "Сколько денег мы выручили от продаж?" или "Сколько единиц данного товара мы продали?". Подсчитать записи вы могли бы и при помощи пользовательского приложения. Однако нужную информацию можно получить более эффективным способом, выполнив прямой запрос к базе данных.
Применяем функцию SUM
Функция SUM делает именно то, что вы ожидаете: она возвращает сумму значений в столбце. Эти значения имеют числовые типы данных. Кроме того, функция возвратит ошибку, если обнаружит значение NULL при попытке вычислить итоговую сумму. Давайте рассмотрим, какими способами можно использовать функцию SUM для получения различной полезной информации. (Сценарии из этого раздела можно найти в примерах в папке \SqlScripts в файле SumExamplesFromText.sql )
Генерируем итоговые суммы
- Чтобы подсчитать итоговую сумму значений в столбце LineTotal таблицы SalesOrderDetail в базе данных Adventure Works, введите и выполните следующий запрос:
SELECT SUM(LineTotal) AS [Grand Total] FROM Sales.SalesOrderDetail
- Сделаем результат, возвращаемый этим сценарием, более полезным: пусть он отображает итоговое количество продаж по продуктам; для этого выполним следующий сценарий:
SELECT ProductID, SUM(LineTotal) AS [Product Total] FROM Sales.SalesOrderDetail GROUP BY ProductID
- Уже лучше, но название столбца ProductID может быть недостаточно понятно для пользователей. Еще усовершенствуем запрос посредством выполнения соединения с таблицей товаров, чтобы вместо идентификатора товара отображалось соответствующее название товара; для этого выполним следующий запрос:
SELECT Production.Product.Name, SUM(Sales.SalesOrderDetail.LineTotal) AS [Product Total] FROM Sales.SalesOrderDetail INNER JOIN Production.Product ON Sales.SalesOrderDetail.ProductID = Production.Product.ProductID GROUP BY Production.Product.Name ORDER BY Production.Product.Name
- Теперь результаты более понятны и полезны для пользователей. Вероятно, мы могли бы предоставить пользователям некоторую информацию о категории и подкатегории каждого товара. С этой целью выполним следующий сценарий:
SELECT C.Name AS Category, S.Name AS SubCategory, P.Name AS Product, SUM(O.LineTotal) AS [Product Total] FROM Sales.SalesOrderDetail AS O INNER JOIN Production.Product AS P ON O.ProductID = P.ProductID INNER JOIN Production.ProductSubcategory AS S ON P.ProductSubcategoryID = S.ProductSubcategoryID INNER JOIN Production.ProductCategory AS C ON S.ProductCategoryID = C.ProductCategoryID GROUP BY P.Name, C.Name, S.Name ORDER BY Category, SubCategory, Product
Получаем следующие результаты.
Вот теперь мы предоставляем пользователям очень полезный набор информации.
Если элементов слишком много, то анализ результатов может быть затруднительным. Кроме того, возможно, пользователю потребуется итоговая сумма по полям SubCategory и Category. Чтобы выполнить эту задачу, можно использовать функцию ROLLUP.
Используем функцию ROLLUP для вычисления детализированных сумм
T-SQL предоставляет операторы для предложения GROUP BY, которые позволяют получить не только детализированную, но и сводную информацию для каждого из полей, которые указаны в аргументе предложения GROUP BY. (Сценарии из этого раздела можно найти в примерах в папке \SqlScripts в файле RollupExamplesFromText.sql )
Генерируем детализированные суммы
- Измените предыдущую инструкцию SELECT так, чтобы она использовала только информацию столбцов Category и SubCategory.
Это уменьшит количество возвращаемых строк и несколько упростит понимание данных.
Мы также добавим в предложение GROUP BY оператор WITH ROLLUP, чтобы вывести промежуточную сумму для столбцов Category и SubCategory.
SELECT C.Name AS Category, S.Name AS SubCategory, SUM(O.LineTotal) AS Sales FROM Sales.SalesOrderDetail AS O INNER JOIN Production.Product AS P ON O.ProductID = P.ProductID INNER JOIN Production.ProductSubcategory AS S ON P.ProductSubcategoryID = S.ProductSubcategoryID INNER JOIN Production.ProductCategory AS C ON S.ProductCategoryID = C.ProductCategoryID GROUP BY C.Name, S.Name WITH ROLLUP ORDER BY Category, SubCategory
Из этого результирующего набора видно, что общая итоговая сумма продаж составляет 109,846,381.40 долларов, общая итоговая сумма продаж для категории Accessories составляет 1,272,072.88 долларов, общая итоговая сумма продаж для подкатегории Bike Racks -237,096.16 долларов и т. д. Однако вывод общих итоговых сумм таким образом - не лучший способ представления информации.
- Чтобы получить более удобный структурированный результирующий набор, можно воспользоваться особой агрегатной функцией, GROUPING. Эта функция помогает обозначить различия между строками, содержащими итоговые значения, и строками, содержащие промежуточные значения. Добавьте функцию GROUPING в свой сценарий для полей Category и SubCategory, чтобы итоговые строки в результате были нагляднее; для этого выполните следующий сценарий:
SELECT C.Name AS Category, S.Name AS SubCategory, SUM(O.LineTotal) AS Sales, GROUPING(C.Name) AS IsCategoryGroup, GROUPING(S.Name) AS IsSubCategoryGroup FROM Sales.SalesOrderDetail AS O INNER JOIN Production.Product AS P ON O.ProductID = P.ProductID INNER JOIN Production.ProductSubcategory AS S ON P.ProductSubcategoryID = S.ProductSubcategoryID INNER JOIN Production.ProductCategory AS C ON S.ProductCategoryID = C.ProductCategoryID GROUP BY C.Name, S.Name WITH ROLLUP ORDER BY Category, SubCategory
- Теперь изменим порядок строк в результирующем наборе, упорядочив по этим сгруппированным значениям. В результате информация будет отображаться в более подходящем для отчета стиле:
SELECT C.Name AS Category, S.Name AS SubCategory, SUM(O.LineTotal) AS Sales, GROUPING(C.Name) AS IsCategoryGroup, GROUPING (S.Name) AS IsSubCategoryGroup FROM Sales.SalesOrderDetail AS O INNER JOIN Production.Product AS P ON O.ProductID = P.ProductID INNER JOIN Production.ProductSubcategory AS S ON P.ProductSubcategoryID = S.ProductSubcategoryID INNER JOIN Production.ProductCategory AS C ON S.ProductCategoryID = C.ProductCategoryID GROUP BY C.Name, S.Name WITH ROLLUP ORDER BY IsCategoryGroup, Category, IsSubCategoryGroup, SubCategory
Результирующий набор для этого сценария показан в табл. 1.5. Обратите внимание на то, что некоторые строки были опущены для экономии места.
- Наконец, можно изменить отображение значения NULL до более приемлемого для отчета вида при помощи добавления в запрос инструкции CASE:
SELECT CASE GROUPING(C.Name) WHEN 1 THEN "Category Total" ELSE C.Name END AS Category, CASE GROUPING(S.Name) WHEN 1 THEN "Subcategory Total" ELSE S.Name END AS SubCategory, SUM(O.LineTotal) AS Sales, GROUPING(C.Name) AS IsCategoryGroup, GROUPING(S.Name) AS IsSubCategoryGroup FROM Sales.SalesOrderDetail AS O INNER JOIN Production.Product AS P ON O.ProductID = P.ProductID INNER JOIN Production.ProductSubcategory AS S ON P.ProductSubcategoryID = S.ProductSubcategoryID INNER JOIN Production.ProductCategory AS C ON S.ProductCategoryID = C.ProductCategoryID GROUP BY C.Name, S.Name WITH ROLLUP ORDER BY IsCategoryGroup, Category, IsSubCategoryGroup, SubCategory
Можно выбрать, отображать ли значения GROUPING, включая или не включая их в предложение SELECT нашего сценария. Скрытые значения GROUPING могут использоваться в сценарии в других предложениях, например, в предложениях ORDER BY. Если вы получаете значения GROUPING в приложении, то их можно использовать для добавления форматирования итоговых строк в отчете или на экране.