Производители промышленных реляционных СУБД стремятся расширить возможности аналитической обработки данных в своих диалектах SQL. Обычно расширение таких возможностей SQL выполняется в следующих направлениях:
Предложения CUBE и ROLLUP делают выполнение запросов и построение отчетов проще в среде ХД. Предложение ROLLUP создает промежуточные суммы (subtotals) в соответствие с возрастающим уровнем агрегации, от наиболее детализированных уровней представления данных к более обобщенным суммам. Предложение CUBE является расширением подобным предложению ROLLUP, позволяющим в одной команде вычислить все возможные комбинации промежуточных сумм. Предложение CUBE может генерировать информацию, необходимую для перекрестных отчетов (cross-tabulation reports) в одном запросе.
Аналитические функции увеличивают потенциал SQL в области статистической обработки данных результирующих множеств запросов. Функции ранжирования включают в себя вычисление куммулятивных распределений, процентных рангов (percent rank) и N-мерных рангов (N-tiles). Вычисления в плавающих окнах (moving window) позволяют работать с кумулятивными агрегатами (moving and cumulative aggregations), такие как суммы и средние величины.
Другие расширения SQL включают в себя семейство функций для вычисления регрессии и CASE выражения. Функции вычисления регрессии включают в себя полный набор вычислений для линейной регрессии. CASE выражения обеспечивают реализацию логики if - then.
Одной из ключевых концепций систем поддержки принятия решений (DSS) и информационных систем руководителя (EIS) является многомерный анализ - анализ объекта во всех необходимых комбинациях измерений. Термин «Измерение» (dimension) используется для обозначения любой категории, используемой для спецификации запроса. Примерами измерений в ХД чаще всего выступают «Время», «География», «Товар», «Подразделение», и «Канал распределения». События или объекты, связанные с конкретными значениями измерений, принято называть фактами. Примерами фактов могут служить «Продажи», «Прибыль», «Количество клиентов», «Объем продукции».
Типичными примерами вопросов в многомерном анализе являются такие, которые будем называть многомерными запросами:
Во всех перечисленных вопросах используются несколько измерений. Во многих многомерных запросах требуется агрегировать данные по времени, географии или финансам и сравнивать полученные наборы данных.
Можно выбирать разворачивать (делать сечения) данные (slices of data) из куба. Например, такая операция соответствует получению перекрестного отчета, показанному в Табл. 8.1. Например, региональный менеджер может изучать данные, сравнивая сечения куба по различным рынкам. Менеджер по товарам может сравнивать сечения куба по различным продуктам.
Ответы на многомерные запросы часто требуют доступа к большому количеству данных, агрегации этих данных по уровням иерархии измерений, вычислении частичных сумм по измерениям. Таким образом, аналитические задачи требуют эффективной и удобной агрегации данных.
Возможности агрегирования данных используются не только в многомерном анализе. Обработка транзакций, например, в финансовых или производственных системах (ERP), также генерирует большое число отчетов. Эффективность таких систем возрастает, когда создание отчетов не очень ограничивает нагрузку на систему. В практике финансовых и ERP систем, очень много отчетов генерируется в ночное время, когда число пользователей таких систем значительно снижается.
Для иллюстрации расширений SQL в настоящей лекции используется гипотетическое ХД организации, которая продает и сдает напрокат видеокассеты. В ХД сохраняется информация о действиях организации в нескольких регионах, отлеживаются продажи и прибыль с продаж. Данные сохраняются в трех измерениях – «Время» (Time), «Отдел продаж» (Department) и «Регион» (Region). Временной период данных составляет период от 2000 года до 2019 года, В компании имеется два типа отделов продаж – «Отдел розничных продаж» (Video Sales) и «Отдел видеопроката» (Video Rentals). Регион включает три направления «Центральный» (Central), «Восточный» (East) и «Западный» (West). Таблица фактов «Продажи» (Sales) содержит данные о продажах и прокате видеопродукции компании за 2000-2019 гг.
Таблицы схемы «звезда» для анализа движения товаров.
| Имя поля | Описание | |
|---|---|---|
| Таблица измерения «Время» (Time) | ||
| Time | Год | |
| Таблица измерения «Регион» (Region) | ||
| Region | Наименование региона | |
| Country | Страна | |
| Таблица измерения «Отделы продаж» (Department) | ||
| Department | Отдел продаж | |
| Manager | Руководитель отдела | |
| Location | Месторасположение | |
| Таблица фактов «Продажи» (Sales) | ||
| sales_id | Идентификатор продажи | |
| Time | Год | |
| Region | Наименование региона | |
| Department | Отдел продаж | |
| Profit | Прибыль | |
В Табл. 8.1 ниже приведен типичный отчет, который руководство компании может запросить для анализа деятельности компании за определенный период времени.
| 2018 | |||
|---|---|---|---|
| Регион | Отдел продаж | ||
| Прибыль от проката | Прибыль от продажи | Итоговая прибыль | |
| Центральный | 82,000 | 85,000 | 167,000 |
| Восточный | 101,000 | 137,000 | 238,000 |
| Западный | 96,000 | 97,000 | 193,000 |
| Итого | 279,000 | 319,000 | 598,000 |
Обратим внимание на то, что в этом небольшом отчете генерируется пять частичных сумм и итоговые суммы. Частичные суммы являются скрытыми числами, которые должны быть вычислены для отчета в запросе, который использует агрегатную функцию SUM() и предложение GROUP BY.
Рассмотрим теперь подробнее расширения оператора SELECT, которые упрощают конструирование запросов для построения отчетов, аналогичных приведенному в Табл. 8.1.
Предложение (в оригинальной документации Oracle – функция) ROLLUP позволяет в команде SELECT вычислять многоуровневые частичные суммы для специфицированных групп измерений. Также может вычисляться итоговая сумма. Предложение ROLLUP является простым расширением предложения GROUP BY, поэтому синтаксис для его использования прост. Использование предложения ROLLUP очень эффективно.
Синтаксис:
SELECT ... GROUP BY ROLLUP (grouping_column_reference_list)
Действия ROLLUP является следующими: создаются частичные суммы для каждого из раскрываемых уровней от наиболее низкого уровня иерархии к более высокому уровню, и вычисляется итоговая сумма в соответствие с указанным списком колонок в предложении ROLLUP. Предложение ROLLUP рассматривает свои аргументы как упорядоченный список колонок группировки. Сначала, вычисляется стандартное агрегатное значение, указанное в предложении GROUP BY. Затем создаются частичные суммы для уровней атрибутов из списка группировки GROUP BY в порядке возрастания их значений, справа налево по списку колонок группировки. И окончательно, создается итоговая сумма (grand total).
Предложение ROLLUP создает частичные суммы для n+1 уровней, где n есть число колонок группировки. Например, если в запросе указан ROLLUP на колонки группировки измерений «Время» (Time), «Регион» (Region) и «Отдел продаж» (Department) (n=3), то результирующее множество (result set) будет включать в себя строки для 4-х уровней агрегации.
Рассмотрим примеры. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2017-18 гг. Предложение SELECT для приведенной схемы ХД может выглядеть следующим образом.
SELECT TRUNC(Time, ‘YYYY’) Y1, Region, Department, SUM(Profit) AS Profit FROM sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY ROLLUP(Time, Region, Department);
Результат выполнения запроса (Агрегирование в ROLLUP для трех измерений):
| Y1 | Region | Department | Profit |
|---|---|---|---|
| 2017 | Центральный | VideoRental | 75,00 |
| 2017 | Центральный | VideoSales | 74,00 |
| 2017 | Центральный | NULL | 149,00 |
| 2017 | Восточный | VideoRental | 89,00 |
| 2017 | Восточный | VideoSales | 115,00 |
| 2017 | Восточный | NULL | 204,00 |
| 2017 | Западный | VideoRental | 87,00 |
| 2017 | Западный | VideoSales | 86,00 |
| 2017 | Западный | NULL | 173,00 |
| 2017 | NULL | NULL | 526,00 |
| 2018 | Центральный | VideoRental | 82,00 |
| 2018 | Центральный | VideoSales | 85,00 |
| 2018 | Центральный | NULL | 167,00 |
| 2018 | Восточный | VideoRental | 101,00 |
| 2018 | Восточный | VideoSales | 137,00 |
| 2018 | Восточный | NULL | 238,00 |
| 2018 | Западный | VideoRental | 96,00 |
| 2018 | Западный | VideoSales | 97,00 |
| 2018 | Западный | NULL | 193,00 |
| 2018 | NULL | NULL | 598,00 |
| NULL | NULL | NULL | 1124,00 |
Как видно из примера выше, запрос возвращает следующий набор строк:
Заметим, что NULL-значения показываются только для ясности. В действительности при выводе будут показаны пробелы.
NULL-значения, возвращаемые в результате выполнения предложений ROLLUP и CUBE, не всегда могут толковаться в общепринятом смысле, как неопределенные значения. NULL-значения могут указывать, что строка содержит частичную сумму. Например, первое NULL-значение в Выводе 1 появляется в колонке «Отдел продаж» (Department). Это NULL - значение означает, что строка есть частичная сумма для всех отделов продаж для центрального региона за 2017 год.
Можно использовать предложение ROLLUP только для вычисления некоторых частичных сумм. Такие команды с использованием ROLLUP используют синтаксис как показано ниже:
GROUP BY expr1, ROLLUP(expr2, expr3);
В этом случае, предложение ROLLUP создает частичные суммы для (2+1=3) уровней агрегации (aggregation levels), т.е. для уровней (expr1, expr2, expr3), (expr1, expr2) и (expr1). Итоговая сумма (grand total) не создается. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2017-18 гг. без итоговой суммы прибыли. Предложение SELECT для приведенной схемы ХД может выглядеть следующим образом.
SELECT TRUNC(Time, ‘YYYY’) Y1, Region, Department, SUM(Profit) AS Profit FROM sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY Time, ROLLUP (Region, Department);
Результат выполнения запроса (Использование предложения ROLLUP для вывода частичных сумм):
| Time | Region | Department | Profit |
|---|---|---|---|
| 2017 | Центральный | VideoRental | 75,00 |
| 2017 | Центральный | VideoSales | 74,00 |
| 2017 | Центральный | NULL | 149,00 |
| 2017 | Восточный | VideoRental | 89,00 |
| 2017 | Восточный | VideoSales | 115,00 |
| 2017 | Восточный | NULL | 204,00 |
| 2017 | Западный | VideoRental | 87,00 |
| 2017 | Западный | VideoSales | 86,00 |
| 2017 | Западный | NULL | 173,00 |
| 2017 | NULL | NULL | 526,00 |
| 2018 | Центральный | VideoRental | 82,00 |
| 2018 | Центральный | VideoSales | 85,00 |
| 2018 | Центральный | NULL | 167,00 |
| 2018 | Восточный | VideoRental | 101,00 |
| 2018 | Восточный | VideoSales | 137,00 |
| 2018 | Восточный | NULL | 238,00 |
| 2018 | Западный | VideoRental | 96,00 |
| 2018 | Западный | VideoSales | 97,00 |
| 2018 | Западный | NULL | 193,00 |
| 2018 | NULL | NULL | 598,00 |
Как видно, запрос возвращает следующее множество строк:
Можно вычислить частичные суммы без использования предложения ROLLUP следующим образом:
SELECT TRUNC(Time,’YYYY’), Region, Department, SUM(Profit) FROM Sales GROUP BY Time, Region, Department UNION ALL SELECT TRUNC(Time,’YYYY’), Region, '' , SUM(Profit) FROM Sales GROUP BY Time, Region UNION ALL SELECT TRUNC(Time,’YYYY’), '', '', SUM(Profit) FROM Sales GROUP BY Time UNION ALL SELECT '', '', '', SUM(Profit) FROM Sales;
Как видно из примера выше, для этого требуется для n измерений n+1 SELECT с UNION ALL.
Таким образом, предложение ROLLUP формирует промежуточные частичные суммы, начиная с самого нижнего уровня иерархии, вычисляет итоговую сумму. Уровни иерархии задаются в предложении GROUP BY.
ROLLUP предложение целесообразно использовать для задач, в которых вычисляются промежуточные или частичные суммы:
Обратимся к учебной БД «Отдел кадров» и представлению EMP_DETAILS_VIEW. Запрос
SELECT region_name, country_name, count(*) FROM emp_details_view GROUP BY region_name, country_name ORDER BY region_name, country_name;
Вычисляет итоги по регионам и странам:
REGION_NAME COUNTER_NAME COUNT(*) Americas Canada 2 Americas United States of America 68 Europe Germany 1 Europe United Kingdom 35
Добавим теперь BY ROLLUP в предложение GROUP BY.
SELECT region_name, country_name, count(*) FROM emp_details_view GROUP BY ROLLUP(region_name, country_name) ORDER BY region_name, country_name;
и в результате получим:
REGION_NAME COUNTER_NAME COUNT(*) Americas Canada 2 Americas United States of America 68 Americas NULL 70 Europe Germany 1 Europe United Kingdom 35 Europe NULL 36 NULL NULL 106
Как можно увидеть, в результирующем множестве появились три новых строки (были сгенерированы): две строки с количеством по регионам (уровень региона) и строка с количеством по странам. Таким образом, была выполнена детализация по странам и регионам.
Частичные суммы, генерируемые предложением ROLLUP, представляют только часть возможных комбинаций частичных сумм в измерениях. Например, в перекрестном отчете (см. Табл. 15.1) итоги работы отделов продаж по регионам (279,000 и 319,000) не могут быть вычислены в предложении ROLLUP (Time, Region, Department). Для этого нужно изменить порядок колонок группировки в предложении ROLLUP: ROLLUP (Time, Department, Region). Простой способ генерации полного набора частичных сумм для перекрестных отчетов состоит в использовании расширения CUBE предложения GROUP BY.
Предложение CUBE позволяет команде SELECT вычислить частичные суммы для всех возможных комбинаций групп измерений. Оно также вычисляет итоговую сумму. Подобно ROLLUP, предложение CUBE является расширением предложения GROUP BY.
Синтаксис:
SELECT ... GROUP BY CUBE (grouping_column_reference_list)
Из примера ниже видно, что CUBE берет указанный набор колонок группировки и создает частичные суммы для всех возможных комбинаций значений этих колонок. С точки зрения многомерного анализа, предложение CUBE генерирует все частичные суммы, которые могут быть вычислены для куба данных с указанными измерениями. Если указывается CUBE(Time, Region, Department), то результирующее множество запроса будет включать все значения, которые включаются в аналогичную конструкцию ROLLUP плюс набор дополнительных комбинаций.
Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2017-18 гг. Предложение SELECT для приведенной схемы ХД может выглядеть следующим образом.
SELECT TRUNC(Time, ‘YYYY’) Y1, Region, Department, SUM(Profit) AS Profit FROM sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY CUBE(Time, Region, Department);
Результат выполнения запроса (Выполнение CUBE с агрегацией по трем измерениям)
| Y1 | Region | Department | Profit |
|---|---|---|---|
| 2017 | Центральный | VideoRental | 75,00 |
| 2017 | Центральный | VideoSales | 74,00 |
| 2017 | Центральный | NULL | 149,00 |
| 2017 | Восточный | VideoRental | 89,00 |
| 2017 | Восточный | VideoSales | 115,00 |
| 2017 | Восточный | NULL | 204,00 |
| 2017 | Западный | VideoRental | 87,00 |
| 2017 | Западный | VideoSales | 86,00 |
| 2017 | Западный | NULL | 173,00 |
| 2017 | NULL | NULL | 526,00 |
| 2018 | Центральный | VideoRental | 82,00 |
| 2018 | Центральный | VideoSales | 85,00 |
| 2018 | Центральный | NULL | 167,00 |
| 2018 | Восточный | VideoRental | 101,00 |
| 2018 | Восточный | VideoSales | 137,00 |
| 2018 | Восточный | NULL | 238,00 |
| 2018 | Западный | VideoRental | 96,00 |
| 2018 | Западный | VideoSales | 97,00 |
| 2018 | Западный | NULL | 193,00 |
| 2018 | NULL | VideoRental | 279,00 |
| 2018 | NULL | VideoSales | 319,00 |
| 2018 | NULL | NULL | 598,00 |
| NULL | Центральный | VideoRental | 157,00 |
| NULL | Центральный | VideoSales | 159,00 |
| NULL | Центральный | NULL | 316,00 |
| NULL | Восточный | VideoRental | 190,00 |
| NULL | Восточный | VideoSales | 252,00 |
| NULL | Восточный | NULL | 442,00 |
| NULL | Западный | VideoRental | 183,00 |
| NULL | Западный | VideoSales | 183,00 |
| NULL | Западный | NULL | 366,00 |
| NULL | NULL | VideoRental | 530,00 |
| NULL | NULL | VideoSales | 594,00 |
| NULL | NULL | NULL | 1124,00 |
Использование CUBE для вычисления частичных сумм аналогично использованию предложения ROLLUP для вычисления частичных, в котором можно ограничить использование некоторых измерения. В этом случае вычисления всех возможных комбинаций ограничивается указанными в списке группировки измерениями.
Синтаксис:
GROUP BY expr1, CUBE(expr2, expr3);
В результате выполнения этой команды будет вычислено 4 частичные суммы:
Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2017-18 гг без вывода частичных сумм. Предложение SELECT для приведенной схемы ХД может выглядеть следующим образом.
SELECT TRUNC(Time, ‘YYYY’) Y1, Region, Department, SUM(Profit) AS Profit FROM sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY Time CUBE(Region, Department);
Результат выполнения запроса (Использование CUBE для вычислений частичных сумм)
| Y1 | Region | Department | Profit |
|---|---|---|---|
| 2017 | Центральный | VideoRental | 75,00 |
| 2017 | Центральный | VideoSales | 74,00 |
| 2017 | Центральный | NULL | 149,00 |
| 2017 | Восточный | VideoRental | 89,00 |
| 2017 | Восточный | VideoSales | 115,00 |
| 2017 | Восточный | NULL | 204,00 |
| 2017 | Западный | VideoRental | 87,00 |
| 2017 | Западный | VideoSales | 86,00 |
| 2017 | Западный | NULL | 173,00 |
| 2017 | NULL | VideoRental | 251,00 |
| 2017 | NULL | VideoSales | 275,00 |
| 2017 | NULL | NULL | 526,00 |
| 2018 | Центральный | VideoRental | 82,00 |
| 2018 | Центральный | VideoSales | 85,00 |
| 2018 | Центральный | NULL | 167,00 |
| 2018 | Восточный | VideoRental | 101,00 |
| 2018 | Восточный | VideoSales | 137,00 |
| 2018 | Восточный | NULL | 238,00 |
| 2018 | Западный | VideoRental | 96,00 |
| 2018 | Западный | VideoSales | 97,00 |
| 2018 | Западный | NULL | 193,00 |
| 2018 | NULL | VideoRental | 279,00 |
| 2018 | NULL | VideoSales | 319,00 |
| 2018 | NULL | NULL | 598,00 |
Без использования предложения CUBE для n-мерного куба, потребуется 2n команд SELECT с UNION ALL.
Предложение CUBE целесообразно использовать при решении задач создания перекрестных отчетов.
Квалификатор DISTINCT имеет ошибочную семантику в предложениях ROLLUP и CUBE. Не рекомендуется использовать квалификатор DISTINCT в комбинации с этими предложениями.
Обратимся к учебной БД «Отдел кадров» и представлению EMP_DETAILS_VIEW. В результирующем множестве запроса
SELECT region_name, first_name, count(*) FROM emp_details_view WHERE first_name LIKE 'Da%' GROUP BY ROLLUP(region_name, first_name);
REGION_NAME LAST_NAME COUNT(*) Europe David 2 Europe Danielle 1 Europe NULL 3 Americas David 1 Americas Daniel 1 Americas NULL 2 NULL NULL 5
можно видеть итоги по регионам, но не видны детали по сотруднику David. Заменим ROLLUP на CUBE, чтобы увидеть эти детали:
SELECT region_name, first_name, count(*) FROM emp_details_view WHERE first_name LIKE 'Da%' GROUP BY CUBE(region_name, first_name);
И получим результирующее множество:
REGION_NAME LAST_NAME COUNT(*) SUMMARY TOTAL 5 SUMMARY David 3 SUMMARY Daniel 1 SUMMARY Danielle 1 Europe TOTAL 3 Europe David 2 Europe Danielle 1 Americas TOTAL 2 Americas David 1 Americas Daniel 1
Две проблемы возникают при использовании ROLLUP и CUBE. Первая, как можно определить, какие строки результирующего множества являются частичными суммами, и как найти точный уровень агрегации данной частичной суммы? Часто необходимо использовать частичные суммы для вычислений процентных отношений между суммами, поэтому необходимо иметь простой способ находить частичные суммы. Вторая, что произойдет, если результат запроса содержит и NULL-значение хранимых строк, и псевдо NULL – значения, созданные ROLLUP или CUBE? Как различить их в результирующем множестве?
Для решения этой задачи предназначена функция GROUPING. Это статистическая функция, выдающая дополнительный столбец, который содержит значение 1, если строка добавлена с помощью оператора CUBE или ROLLUP, или значение 0 в ином случае.
Синтаксис (указывается в списке предложения SELECT):
SELECT ... [GROUPING(column_name)...] ...
GROUP BY ... {CUBE | ROLLUP} (column_name)
Приведем пример использования функции GROUPING для создания колонок-масок в результирующем множестве.
SELECT SELECT TRUNC(Time, ‘YYYY’) Y1, Region, Department, SUM(Profit) AS Profit, GROUPING (Time) as T, GROUPING (Region) as R, GROUPING (Department) as D FROM Sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY ROLLUP (Time, Region, Department);
Результат выполнения запроса (Использование функции GROUPING ):
| Y1 | Region | Department | Profit | Т | R | D |
|---|---|---|---|---|---|---|
| 2017 | Центральный | VideoRental | 75,00 | 0 | 0 | 0 |
| 2017 | Центральный | VideoSales | 74,00 | 0 | 0 | 0 |
| 2017 | Центральный | NULL | 149,00 | 0 | 0 | 1 |
| 2017 | Восточный | VideoRental | 89,00 | 0 | 0 | 0 |
| 2017 | Восточный | VideoSales | 115,00 | 0 | 0 | 0 |
| 2017 | Восточный | NULL | 204,00 | 0 | 0 | 1 |
| 2017 | Западный | VideoRental | 87,00 | 0 | 0 | 0 |
| 2017 | Западный | VideoSales | 86,00 | 0 | 0 | 0 |
| 2017 | Западный | NULL | 173,00 | 0 | 0 | 1 |
| 2017 | NULL | NULL | 526,00 | 0 | 1 | 1 |
| 2018 | Центральный | VideoRental | 82,00 | 0 | 0 | 0 |
| 2018 | Центральный | VideoSales | 85,00 | 0 | 0 | 0 |
| 2018 | Центральный | NULL | 167,00 | 0 | 0 | 1 |
| 2018 | Восточный | VideoRental | 101,00 | 0 | 0 | 0 |
| 2018 | Восточный | VideoSales | 137,00 | 0 | 0 | 0 |
| 2018 | Восточный | NULL | 238,00 | 0 | 0 | 1 |
| 2018 | Западный | VideoRental | 96,00 | 0 | 0 | 0 |
| 2018 | Западный | VideoSales | 97,00 | 0 | 0 | 0 |
| 2018 | Западный | NULL | 193,00 | 0 | 0 | 1 |
| 2018 | NULL | NULL | 598,000 | 0 | 1 | 1 |
| NULL | NULL | NULL | 1124,00 | 1 | 1 | 1 |
Как видно из примера, маска «0 0 0» - агрегированная строка из таблицы, «0 0 1» - первый уровень агрегации, «0 1 1» - второй уровень агрегации, «1 1 1» - итоговая сумма.
Предположим, что оператор SELECT выдает следующее результирующее множество, созданное выражением CUBE.
Результат выполнения запроса:
| Time | Region | Profit |
|---|---|---|
| 2017 | Восточный | 200,00 |
| 2017 | NULL | 200,00 |
| NULL | Восточный | 200,00 |
| NULL | NULL | 190,00 |
| NULL | NULL | 190,00 |
| NULL | NULL | 190,00 |
| NULL | NULL | 390,00 |
В результирующем множестве 4 различных строки с NULL - значениями для колонок «Время» (Time) и «Регион» (Region). Некоторые из этих NULL значений должны представлять агрегаты CUBE, а некоторые агрегаты NULL значений из базы данных. Как различить в отчете агрегатные NULL значения, построенные предложением CUBE, от хранимых в БД от NULL – значений?
Использование GROUPING функции в комбинации с CASE выражением и функцией преобразования значений одних типов данных CAST ({expr | MULTISET (subquery) } AS type_name ) позволяет решить эту проблему. Выражение CASE выполняет оценку списка условий и возвращает один из нескольких возможных выражений результатов. Функция CAST выполняет разграничение NULL-значений в результирующем множестве.
Теперь можно преобразовать отчет из Таблицы 15.1 таким образом, чтобы выделить агрегаты предложения CUBE, как показано ниже.
SELECT CASE WHEN GROUPING(Time) = 1 THEN 'All Times' ELSE CAST(Time as CHAR(20)) END AS Time, CASE WHEN GROUPING(Region) = 1 THEN 'All Regions' ELSE Region END AS Region, SUM(Profit) AS Profit FROM Sales WHERE TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2017’, ‘YYYY’) AND TRUNC(Time, ‘YYYY’) = TO_DATE(’01.01.2018’, ‘YYYY’) GROUP BY CUBE(Time, Region);
Результат выполнения запроса (Разграничение агрегатных и хранимых NULL – значений):
| Time | Region | Profit |
|---|---|---|
| 2017 | Восточный | 200,00 |
| 2017 | All Regions | 200,00 |
| All Times | Восточный | 200,00 |
| NULL | NULL | 190,00 |
| NULL | All Regions | 190,00 |
| All Times | NULL | 190,00 |
| All Times | All Regions | 390,00 |
Первая колонка есть «Время» (Time), определенное выражением.
CASE WHEN GROUPING(Time) = 1 THEN 'All Times' ELSE CAST(Time as CHAR(20)) END AS Time
Значение Time определяется выражением CASE, содержащим функцию GROUPING. Функция GROUPING возвращает 1, если строка есть агрегат предложений ROLLUP или CUBE, иначе - 0. CASE работает с результатом функции GROUPING. Оно возвращает текст "All Times", если это 1, и значение колонки «Время» (time) из БД, если это 0. Значениями из базы данных будут либо фактическое значение такое, как 2007 или сохраняемое NULL - значение. Функция CAST используется для согласования типов, поскольку в результирующем множестве все колонки должны быть одного типа. Вторая колонка спецификации, показывающая значения колонки «Регион» (Region), обрабатывается аналогичным образом.
Функция GROUPING полезна не только для идентификации NULL-значений, она также может помочь отсортировать строки частичных сумм и отфильтровать результирующее множество. В примере ниже выбирается подмножество частичных сумм, созданное CUBE. Предложение HAVING ограничивает колонки, которые используются в функции GROUPING.
SELECT Time, Region, Department, SUM(Profit) AS Profit, GROUPING (Time) AS T, GROUPING (Region) AS R, GROUPING (Department) AS D FROM Sales GROUP BY CUBE (Time, Region, Department) HAVING (GROUPING(Department)=1 AND GROUPING(Region)=1 AND GROUPING(Time)=1) OR (GROUPING(Region)=1 AND (GROUPING(Department)=1) OR (GROUPING(Time)=1 AND GROUPING(department)=1);
Результат выполнения запроса (использование функции GROUPING для фильтрации результата в частичных суммах и итоговой сумме):
| Time | Region | Department | Profit |
|---|---|---|---|
| 2017 | NULL | NULL | 526,00 |
| 2018 | NULL | NULL | 598,00 |
| NULL | Центральный | NULL | 316,00 |
| NULL | Восточный | NULL | 442,00 |
| NULL | Западный | NULL | 366,00 |
| NULL | NULL | NULL | 1124,00 |
Обратимся к учебной БД «Отдел кадров» и представлению EMP_DETAILS_VIEW. В запрос, вычисляющий итоги с ROLLUP, добавим функцию GROUPING:
SELECT decode(grouping(region_name),0,region_name,'GRAND') AS region_name, decode(grouping(country_name),0,country_name,'TOTAL') AS country_name, count(*) FROM emp_details_view GROUP BY ROLLUP(region_name, country_name);
REGION_NAME COUNTER_NAME COUNT(*) Europe Germany 1 Europe United Kingdom 35 Europe TOTAL 36 Americas Canada 2 Americas United States of America 68 Americas TOTAL 70 GRAND TOTAL 106
Как можно увидеть, функция DECODE в сочетании с функцией GROUPING позволяет заменить неопределенные значения в сгенерированных дополнительных строках на слова, поясняющие смысл вычисленных итогов по регионам и странам.
Предложения ROLLUP и CUBE работают независимо от какой-либо иерархии данных в БД. Их вычисления основываются на колонках, которые появляются в списке оператора SELECT. Такой подход позволяет использовать CUBE и ROLLUP и для обработки иерархий. Простой способ управления иерархией измерения - использовать предложение ROLLUP и точно указать уровни иерархии для выделенной колонки. В примере ниже показано, как месяцы сворачиваются в кварталы, кварталы в год.
Пример команды SELECT.
SELECT Year, Quarter, Month, SUM(Profit) AS Profit FROM sales GROUP BY ROLLUP(Year, Quarter, Month) HAVING Year = 2008;
Результат выполнения запроса (ROLLUP с использованием уровней иерархии измерения «Время» (Time)):
| Year | Quarter | Month | Profit |
|---|---|---|---|
| 2018 | Первый | Январь | 55,00 |
| 2018 | Первый | Февраль | 64,00 |
| 2018 | Первый | Март | 71,00 |
| 2018 | Первый | NULL | 190,00 |
| 2018 | Второй | Апрель | 75,00 |
| 2018 | Второй | Май | 86,00 |
| 2018 | Второй | Июнь | 88,00 |
| 2018 | Второй | NULL | 249,00 |
| 2018 | Третий | Июль | 91,00 |
| 2018 | Третий | Август | 87,00 |
| 2018 | Третий | Сентябрь | 101,00 |
| 2018 | Третий | NULL | 279,00 |
| 2018 | Четвертый | Октябрь | 109,00 |
| 2018 | Четвертый | Ноябрь | 114,00 |
| 2018 | Четвертый | Декабрь | 133,00 |
| 2018 | Четвертый | NULL | 356,00 |
| 2018 | NULL | NULL | 1074,00 |
Обратим внимание на некоторые особенности использования предложений ROLLUP и CUBE.
Предложения CUBE и ROLLUP не ограничивают мощность колонки (Column Capacity) предложения GROUP BY. Предложение GROUP BY, с расширениями или без них может работать с 255 колонками. Однако число комбинаций, которое создает предложение CUBE, может быть очень велико. Для 20-ти колонок предложением CUBE будут создано 220 комбинаций в результирующем множестве.
Предложение HAVING команды SELECT не влияет на использование предложений ROLLUP и CUBE в том смысле, что оно применяется в целом к предложению GROUP BY. Предикат в предложении HAVING применяются как к строкам частичных сумм, так и строкам агрегатам результирующего множества.
Предложение ORDER BY команды SELECT не влияет на использование предложения ROLLUP и CUBE. Предикат в предложении ORDER BY применяются ко всем строкам результирующего множества.
В диалекте SQL Oracle 11g предложение MODEL задает выражение, которое позволяет представлять результирующее множество в виде многомерного куба и задавать выражения для расчета его произвольных ячеек.
Синтаксис (сокращенный):
MODEL [IGNORE NAV] [RETURN UPDATED ROWS]
[PARTITION BY (partition_column_1, ...)]
DIMENSION BY (dimension_column_1, ...)
MEASURES (measured_column_1, ...)
RULES [AUTOMATIC ORDER | ITERATE (value) [UNTIL (expression)]] ( rule_1, ... );
Определим обязательные позиции.
Фраза DIMENSION BY определяет колонки, с помощью которых для любой ячейки многомерного куба можно сопоставить единственную строку. Нельзя использовать алиасное имя колонки предложения SELECT.
Фраза MEASURES определяет, какие колонки результирующего множества будут использоваться для доступа к данным. Нельзя ссылаться на колонку, которая отсутствует в DIMENSION BY и MEASURES.
Фраза RULES задает правила обработки строк результирующего множества.
Предложение PARTITION BY задает разбиение результирующего множества на секции, строки которой обрабатываются отдельно от строк других секций по заданным правилам (см. Лекцию 4).
Таким образом, с помощью предложения MODEL можно задать многомерный массив на результирующем множестве и работать с ним как с электронной таблицей. Это предмет обсуждения отдельной лекции, а для подробного изучения предложения MODEL следует обратиться к документации [].
Приведем простой пример использования этого расширения команды SELECT. Обратимся к таблице EMPLOYEES схемы HR и построим на ней куб по измерению employee_id, job_id с метрикой salary, как показано ниже.
SELECT employee_id, job_id, salary as sal FROM EMPLOYEES MODEL DIMENSION BY (employee_id, job_id) MEASURES (salary) RULES (); ORDER BY job_id;
Результат выполнения запроса (фрагмент):
EMPLOYEE_ID JOB_ID SAL 103 IT_PROG 9000 104 IT_PROG 6000 105 IT_PROG 4800 106 IT_PROG 4800 107 IT_PROG 4200 205 AC_MGR 12008 ….. 115 PU_CLERK 3100 116 PU_CLERK 2900 117 PU_CLERK 2800 118 PU_CLERK 2600 119 PU_CLERK 2500
…..
В следующем запросе давайте назначим всем сотрудникам, работающими агентами по продажам, оклад в размере 3200 у.е.
SELECT employee_id, job_id, salary as sal FROM EMPLOYEES MODEL DIMENSION BY (employee_id, job_id) MEASURES (salary) RULES (salary[any,'PU_CLERK']=3200); ORDER BY job_id;
Результат выполнения запроса (фрагмент):
EMPLOYEE_ID JOB_ID SAL 103 IT_PROG 9000 104 IT_PROG 6000 105 IT_PROG 4800 ….. 116 PU_CLERK 3200 117 PU_CLERK 3200 119 PU_CLERK 3200 115 PU_CLERK 3200 118 PU_CLERK 3200
В двух лекциях мы рассмотрели расширения предложения GROUP BY команды SELECT, предназначенные для агрегации данных в результирующем множестве:
Использование предложений CUBE и ROLLUP делают выполнение запросов и построение отчетов проще в среде ХД. Предложение ROLLUP целесообразно использовать для задач, в которых вычисляются промежуточные или частичные суммы. Предложение CUBE целесообразно использовать при решении задач создания перекрестных отчетов.
Также было представлено предложение MODEL команды SELECT, которое позволяет строить многомерный куб (массив ячеек) на результирующем множестве.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.