Цель лекции
Изучив материал настоящей лекции, вы будете знать:
GROUPING для агрегации данных в результирующем множестве;и научитесь:
GROUPING.SQL – язык манипулирования данными в реляционной БД. В настоящей лекции мы сконцентрируем внимание на тех возможностях, которые предоставляет SQL для аналитической работы в ХД.
Единственным средством общения и администраторов БД, и проектировщиков, и разработчиков, и пользователей с реляционной БД является
SQL — непроцедурный язык, который предназначен для обработки множеств, состоящих из строк и колонок таблиц реляционной БД. Существуют расширения SQL, допускающие процедурную обработку. Проектировщики используют SQL для создания всех физических объектов реляционной БД или ХД.
Теоретические основы SQL были заложены в известной статье [Кодд], положившей начало развитию теории реляционных БД. Первая практическая реализации была выполнена в исследовательских лабораториях фирмы IBM Chamberlin D.D. и Royce R.F. Промышленное применение SQL было впервые реализовано в
Первый международный стандарт языка SQL был принят в 1989 г. (
Каждой конкретной СУБД соответствует своя собственная реализация SQL, в целом поддерживающая определенный стандарт, но имеющая свои особенности. Такие реализации называются диалектами. Так, стандарт ISO/IEC 9075-5 предусматривает объекты, называемые постоянно хранимыми модулями, или
Далее в примерах будет использоваться диалект SQL (Transact-SQL) СУБД MS SQL Server 2005/2008.
Синтаксис
<SELECT statement> ::=
[WITH <common_table_expression> [,...n]]
<query_expression>
[ ORDER BY { order_by_expression | column_position [ ASC | DESC ] }
[ ,...n ] ]
[ COMPUTE
{ { AVG | COUNT | MAX | MIN | SUM } ( expression ) } [ ,...n ]
[ BY expression [ ,...n ] ]
]
[ <FOR Clause>]
[ OPTION ( <query_hint> [ ,...n ] ) ]
<query_expression> ::=
{ <query_specification> | ( <query_expression> ) }
[ { UNION [ ALL ] | EXCEPT | INTERSECT }
<query_specification> | ( <query_expression> ) [...n ] ]
<query_specification> ::=
SELECT [ ALL | DISTINCT ]
[TOP ( expression ) [PERCENT] [ WITH TIES ] ]
< select_list >
[ INTO new_table ]
[ FROM { <table_source> } [ ,...n ] ]
[ WHERE <search_condition> ]
[ <GROUP BY> ]
[ HAVING < search_condition > ]
SELECT select_list [ INTO new_table ] определяет список имен колонок таблиц БД, представлений или производных полей.FROM table_source определяет список имен таблиц и представлений БД, участвующих в выборке.WHERE search_condition определяет предикат отбора строк в выборку.GROUP BY group_by_expression определяет условия группировки строк в выборке.HAVING search_condition, которое условия поиска на разбиении результирующего множества ORDER BY order_expression [ ASC | DESC ] определяет правила упорядочивания строк в выборке.Операторы UNION, EXCEPT и INTERSECT используются для построения комбинации
Теперь перейдем к примерам конструирования запросов к схеме типа "звезда". Рассмотрим схему "звезда" ХД, предназначенного для анализа сбыта продукции торговой организации (рис 22.1).
(рис 22.1) Схема "звезда" для анализа сбыта продукции торговой организацииХД предназначено для анализа системы сбыта продукции организации и включает, помимо времени, следующие объекты:
| Имя поля | Описание |
|---|---|
| product_id | Идентификатор товара |
| product_name | Наименование товара |
| product_category | Категория товара |
| Имя поля | Описание |
|---|---|
| time_id | Идентификатор времени |
| time_month | Месяц |
| time_quarter | Квартал |
| time_year | Год |
| time_dayno | День |
| time_weekno | Неделя |
| time_day_of_week | День недели |
| Имя поля | Описание |
|---|---|
| customer_id | Идентификатор покупателя |
| customer_name | Покупатель |
| customer_address | Адрес |
| customer_city | Город |
| customer_subregion | Район |
| customer_region | Область |
| customer_postalcode | Почтовый индекс |
| customer_age | Возраст |
| customer_gender | Тип покупателя |
| Имя поля | Описание |
|---|---|
| region_id | Идентификатор региона |
| region_name | Наименование региона |
| region_country | Страна |
| Имя поля | Описание |
|---|---|
| sales_transaction_id | Идентификатор транзакции |
| product_id | Идентификатор товара |
| customer_id | Идентификатор покупателя |
| time_id | Идентификатор времени |
| region_id | Идентификатор региона |
| sales_quantity_sold | Количество проданного товара |
| sales_dollar_amount | Цена в долларах проданного товара |
Таким образом, получаем одну таблицу фактов и четыре таблицы измерений. Рассмотрим, как конструируется
Рассмотрим пример запроса к схеме, приведенной на рис 22.1.
Пример 22.1. Пусть требуется просмотреть данные о продажах товара с идентификационным номером 33 за месяцы с мая по август текущего года по региону "Москва" с идентификационным номером 81. Тогда запрос может выглядеть следующим образом:
SELECT SUM(sales_dollar_amount* sales_quantity_sold), time_month, region_name FROM Sales, Time, Region WHERE Sales.region_id = Region.region_id AND Sales.time_id = Time_time_id AND Sales.product_id = 33 AND Sales.region_id = 81 AND Time.time_month BETWEEN 'Май' AND 'Август' AND Time.time_year = 2009 GROUP BY time_month, region_name
Изменяя данные о регионе, месяцах и товаре, при помощи вышеприведенного запроса можно выявить тенденции изменения данных о продажах. Для схемы "звезда" характерно использование односторонних
Метрика типа "Объем продаж", рассмотренная в предыдущем примере, является
Метрика "Остаток на складе" является типичным примером
(рис 22.2) Схема "звезда" с полуаддитивным фактом в таблице фактовВ схеме представлено три таблицы измерений: "Месяц" (Data_month), "Магазин" (Store), "Товары" (Products) и таблица фактов "Остаток на складе" (Quantity_on_hand_fact). Описание таблиц приведено в табл. 22.6 ниже.
| Имя поля | Описание |
|---|---|
| Таблица измерения "Месяц" (Data_month) | |
| month_id | Идентификатор месяца |
| data_month | Месяц |
| data_quarter | Квартал |
| data_year | Год |
| Таблица измерения "Магазин" (Store) | |
| store_id | Идентификатор магазина |
| store_name | Название магазина |
| store_location | Месторасположение магазина |
| store_region | Регион |
| Таблица измерения "Товары" (Products) | |
| product_id | Идентификатор товара |
| product_name | Название товара |
| product_category | Категория товара |
| Таблица фактов "Остаток на складе" (Quantity_on_hand_fact) | |
| month_id | Идентификатор месяца |
| store_id | Идентификатор магазина |
| product_id | Идентификатор товара |
| Quantity_on_hand | Остаток на складе |
Метрика "Остаток на складе" является аддитивной по измерениям "Товары" (Products) и "Магазин" (Store), но не является аддитивной по измерению "Месяц" (Data_month). Рассмотрим, как можно получить итоговое количество товаров на складе в магазине в любой момент времени, используя измерения, для которых
Пример 22.2. Пусть нам необходимо просуммировать остатки товара "Подушка" на складе магазинов за январь 2009 года с учетом месторасположения последних, т.е. определить, сколько нереализованных подушек было в сети магазинов торговой организации в январе 2009 года. Сделаем это за счет соединения таблицы фактов с измерением "Магазин", как показано ниже.
SELECT Store.store_location, SUM(Quantity_on_hand_fact.Quantity_on_hand) FROM Store, Quantity_on_hand_fact, Products, Data_month WHERE Store.store_id = Quantity_on_hand_fact.store_id AND Quantity_on_hand_fact.month_id = Data_month.month_id AND Products.product_id = Quantity_on_hand_fact.product_id AND Data_month.data_month = 'Январь' AND Data_month.data_year = 2009 AND Products.product_name ='Подушка' GROUP BY Store.store_location
Аналогично можно суммировать метрику "Остаток на складе" по измерению "Товары", чтобы получать количество нереализованных товаров, сгруппированных по категориям товара.
В примерах, приведенных выше, мы использовали агрегатную функцию SUM() для суммирования и
В ХД, как правило, отношение имеет внутреннюю структуру, и при его обработке требуется проводить разбиение отношения на подмножества, обладающие тем или иным значением определенного атрибута. Например, в достаточно общей постановке вопрос можно сформулировать так: протабулировать значение некоторой функции на каждом из этих подмножеств в соответствии с общим значением атрибута.
Кроме функции SUM(), к агрегатным функциям относятся функции: AVG() – вычисляет среднее значение, MIN() – вычисляет минимальное значение, MAX() – вычисляет максимальное значение, COUNT() – вычисляет количество итемов в результирующем множестве (или элементе разбиения результирующего множества), и ряд других, предусмотренных реализацией SQL в конкретной СУБД.
На использование колонок группировки существуют ограничения. В качестве колонок группировки в данном предложении SELECT можно указывать только колонки из заданного списка, а не любые из таблицы. В конкретных реализациях SQL предусмотрен еще целый ряд ограничений.
Можно задавать условия выборки на результаты выполнения WHERE состоит в том, что первое выбирает подмножества из разбиения целиком в зависимости от его агрегируемых свойств, в то время как последнее просматривает содержимое каждого из этих подмножеств построчно, не учитывая полученное разбиение.
Иногда наблюдается более быстрое выполнение команды SELECT с использованием WHERE.
Производители промышленных реляционных СУБД стремятся расширить возможности аналитической обработки данных в своих диалектах SQL. Обычно расширение таких возможностей SQL выполняется в следующих направлениях:
SELECT.Предложения
Другие расширения SQL включают в себя семейство функций для вычисления регрессий и if – then.
Одной из ключевых концепций систем поддержки принятия решений (DSS) и информационных систем руководителя (
Типичными примерами вопросов в многомерном анализе являются такие, которые мы будем называть многомерными запросами (MDQ).
Во всех перечисленных вопросах используется несколько измерений. Во многих MDQ требуется агрегировать данные по времени, географии или финансам и сравнивать полученные наборы данных.
Для визуализации данных, которые имеют несколько измерений, аналитики используют аналогию с
Вы можете разворачивать (делать сечения) данные (slices of data) из куба. Это соответствует перекрестному отчету, показанному в табл. 22.7. Например, региональный менеджер может изучать данные, сравнивая сечения куба по различным рынкам. Менеджер по товарам может сравнивать сечения куба по различным продуктам.
Ответы на MDQ часто требуют доступа к большому количеству данных, агрегации этих данных по уровням
Возможности агрегирования данных используются не только в многомерном анализе. Обработка транзакций, например, в финансовых или производственных системах (ERP), также генерирует большое число отчетов. Эффективность таких систем возрастает, когда создание отчетов не очень ограничивает нагрузку на систему. В практике финансовых и ERP-систем большое количество отчетов генерируется в ночное время, когда число пользователей таких систем значительно снижается. Важно, что проектировщики БД и ХД должны решать задачу оптимизации запросов, которые используют агрегацию и суммирование данных на различных уровнях их детализации, и в частности такие задачи, как:
Для иллюстрации расширений SQL в настоящей лекции мы взяли гипотетическое ХД организации, которая продает и сдает напрокат видеокассеты. В ХД сохраняется информация о действиях организации в нескольких регионах, отлеживаются продажи и
(рис 22.3) Схема "звезда" для хранилища данных организации, торгующей видеопродукцией| Имя поля | Описание |
|---|---|
| Таблица измерения "Время" (Time) | |
| Time | Год |
| Таблица измерения "Регион" (Region) | |
| Region | Наименование региона |
| Country | Страна |
| Таблица измерения "Отделы продаж" (Department) | |
| Department | Отдел продаж |
| Manager | Руководитель отдела |
| Location | Месторасположение |
| Таблица фактов "Продажи" (Sales) | |
| sales_id | Идентификатор продажи |
| Time | Год |
| Region | Наименование региона |
| Department | Отдел продаж |
| Profit | Прибыль |
В табл. 22.8 приведен типичный отчет, который руководство компании может запросить для анализа деятельности компании за определенный период времени.
| 2008 | |||
|---|---|---|---|
| Регион | Отдел продаж | ||
| Прибыль от проката | Прибыль от продажи | Итоговая прибыль | |
| Центральный | 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() и
Рассмотрим теперь подробнее расширения
SELECT вычислять многоуровневые частичные суммы для специфицированных групп измерений. Также вычисляется итоговая сумма.
Синтаксис:
SELECT ... GROUP BY ROLLUP(grouping_column_reference_list)
Действия ROLLUP являются следующими: создаются частичные суммы для каждого из раскрываемых уровней от наиболее низкого уровня иерархии к более высокому уровню и вычисляется итоговая сумма в соответствии с указанным списком колонок в GROUP BY в порядке возрастания их значений, справа налево по списку колонок группировки. И окончательно создается итоговая сумма (grand total).
n+1 уровней, где n есть число колонок группировки. Например, если в запросе указан ROLLUP на колонки группировки измерений "Время" (Time), "Регион" (Region) и "Отдел продаж" (Department) ( n=3 ), то результирующее множество (result set) будет включать в себя строки для 4-х уровней агрегации.
Рассмотрим примеры.
Пример 22.3. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2007-08 гг. Предложение схемы ХД может выглядеть следующим образом.
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY ROLLUP(Time, Region, Department);
Вывод 1: Агрегирование в ROLLUP для трех измерений
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | NULL | 598,00 |
| NULL | NULL | NULL | 1124,00 |
Как видно из примера выше, запрос возвращает следующий набор строк:
ROLLUP ;Заметим, что NULL-значения показываются только для ясности. В действительности при выводе будут показаны пробелы.
NULL-значения, возвращаемые в результате выполнения предложений
Можно использовать ROLLUP используют синтаксис как показано ниже:
GROUP BY expr1, ROLLUP(expr2, expr3);
В этом случае (2+1=3) уровней агрегации (aggregation levels), т.е. для уровней (expr1, expr2, expr3), (expr1, expr2) и (expr1). Итоговая сумма (grand total) не создается.
Пример 22.4. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. без итоговой суммы прибыли. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY Time, ROLLUP (Region, Department);
Вывод 2. Использование
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | NULL | 598,00 |
Как видно, запрос возвращает следующее множество строк:
ROLLUP ;Можно вычислить частичные суммы без использования
SELECT Time, Region, Department, SUM(Profit) FROM Sales GROUP BY Time, Region, Department UNION ALL SELECT Time, Region, '' , SUM(Profit) FROM Sales GROUP BY Time, Region UNION ALL SELECT Time, '', '', SUM(Profit) FROM Sales GROUP BY Time UNION ALL SELECT '', '', '', SUM(Profit) FROM Sales;
Как видно из примера выше, для этого требуется для n измерений n+1 SELECT с UNION ALL.
ROLLUP(y, m, day) или ROLLUP(country, state, city).Частичные суммы, генерируемые ROLLUP(Time, Region, Department). Для этого нужно изменить порядок колонок группировки в предложении ROLLUP: ROLLUP(Time, Department, Region). Простой способ генерации полного набора частичных сумм для перекрестных отчетов состоит в использовании расширения CUBE
SELECT вычислить частичные суммы для всех возможных комбинаций групп измерений. Оно также вычисляет итоговую сумму. Подобно ROLLUP,
Синтаксис:
SELECT ... GROUP BY CUBE (grouping_column_reference_list)
Из примера ниже видно, что CUBE берет указанный набор колонок группировки и создает частичные суммы для всех возможных комбинаций значений этих колонок. С точки зрения многомерного анализа, CUBE(Time, Region, Department), то результирующее множество запроса будет включать все значения, которые входят в аналогичную конструкцию ROLLUP, плюс набор дополнительных комбинаций.
Пример 22.5. Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY CUBE(Time, Region, Department);
Вывод 3. Выполнение CUBE с агрегацией по трем измерениям
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | VideoRental | 279,00 |
| 2008 | NULL | VideoSales | 319,00 |
| 2008 | 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 для вычисления частичных сумм аналогично использованию
Синтаксис:
GROUP BY expr1, CUBE(expr2, expr3);
В результате выполнения этой команды будет вычислено 4 частичные суммы:
(expr1, expr2, expr3)(expr1, expr2)(expr1, expr3)(expr1)Пример 22.6. Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. без вывода частичных сумм. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY Time CUBE(Region, Department);
Вывод 4. Использование CUBE для вычислений частичных сумм
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | VideoRental | 251,00 |
| 2007 | NULL | VideoSales | 275,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | VideoRental | 279,00 |
| 2008 | NULL | VideoSales | 319,00 |
| 2008 | NULL | NULL | 598,00 |
Без использования n -мерного куба потребуется 2n команд SELECT с UNION ALL.
Квалификатор DISTINCT имеет ошибочную семантику в предложениях DISTINCT в комбинации с этими предложениями.
Две проблемы возникают при использовании ROLLUP и CUBE. Первая: как можно программно определить, какие строки результирующего множества являются частичными суммами, и как найти точный уровень агрегации данной частичной суммы? Часто необходимо использовать частичные суммы для вычислений процентных отношений между суммами, поэтому нужен простой способ находить частичные суммы. Вторая проблема: что произойдет, если результат запроса содержит и NULL-значение хранимых строк, и псевдо-NULL-значения, созданные ROLLUP или CUBE? Как различить их в результирующем множестве?
Для решения этой задачи предназначена CUBE или ROLLUP, или значение 0 — в ином случае.
Синтаксис (указывается в списке предложения SELECT ):
SELECT ... [GROUPING(column_name)...] ...
GROUP BY ... {CUBE | ROLLUP} (column_name)
Пример 22.7. Использование
SELECT Time, Region, Department, SUM(Profit) AS Profit, GROUPING (Time) as T, GROUPING (Region) as R, GROUPING (Department) as D FROM Sales GROUP BY ROLLUP (Time, Region, Department);
Вывод 5. Использование
| Time | Region | Department | Profit | Т | R | D |
|---|---|---|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 | 0 | 0 | 0 |
| 2007 | Центральный | VideoSales | 74,00 | 0 | 0 | 0 |
| 2007 | Центральный | NULL | 149,00 | 0 | 0 | 1 |
| 2007 | Восточный | VideoRental | 89,00 | 0 | 0 | 0 |
| 2007 | Восточный | VideoSales | 115,00 | 0 | 0 | 0 |
| 2007 | Восточный | NULL | 204,00 | 0 | 0 | 1 |
| 2007 | Западный | VideoRental | 87,00 | 0 | 0 | 0 |
| 2007 | Западный | VideoSales | 86,00 | 0 | 0 | 0 |
| 2007 | Западный | NULL | 173,00 | 0 | 0 | 1 |
| 2007 | NULL | NULL | 526,00 | 0 | 1 | 1 |
| 2008 | Центральный | VideoRental | 82,00 | 0 | 0 | 0 |
| 2008 | Центральный | VideoSales | 85,00 | 0 | 0 | 0 |
| 2008 | Центральный | NULL | 167,00 | 0 | 0 | 1 |
| 2008 | Восточный | VideoRental | 101,00 | 0 | 0 | 0 |
| 2008 | Восточный | VideoSales | 137,00 | 0 | 0 | 0 |
| 2008 | Восточный | NULL | 238,00 | 0 | 0 | 1 |
| 2008 | Западный | VideoRental | 96,00 | 0 | 0 | 0 |
| 2008 | Западный | VideoSales | 97,00 | 0 | 0 | 0 |
| 2008 | Западный | NULL | 193,00 | 0 | 0 | 1 |
| 2008 | 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" — итоговая сумма.
Пример 22.8. Предположим, что CUBE.
Вывод 6.
| Time | Region | Profit |
|---|---|---|
| 2007 | Восточный | 200,00 |
| 2007 | 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-значения, построенные
Использование CASE -выражением и функцией преобразования типов данных CAST (expression AS data_type [ (length ) ]) позволяет решить эту проблему.
Выражение CASE выполняет оценку списка условий и возвращает одно из нескольких возможных выражений результатов.
Синтаксис:
CASE input_expression
WHEN when_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
или
CASE
WHEN Boolean_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
Теперь можно преобразовать отчет из примера 22.8 таким образом, чтобы выделить агрегаты
Пример 22.9. Разграничение агрегатных и хранимых NULL-значений.
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 GROUP BY CUBE(Time, Region);
Вывод 7. Разграничение агрегатных и хранимых NULL-значений
| Time | Region | Profit |
|---|---|---|
| 2007 | Восточный | 200,00 |
| 2007 | 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, содержащим CASE работает с результатом "All Times", если это 1, и значение колонки "Время" (time) из БД, если это 0. Значениями из базы данных будут либо фактическое значение, такое как 2007, или сохраняемое NULL-значение. Функция CAST используется для
CUBE.
Пример 22.10.
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);
Вывод 8. Пример использования
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | NULL | NULL | 526,00 |
| 2008 | NULL | NULL | 598,00 |
| NULL | Центральный | NULL | 316,00 |
| NULL | Восточный | NULL | 442,00 |
| NULL | Западный | NULL | 366,00 |
| NULL | NULL | NULL | 1124,00 |
Предложения
Пример 22.11.
SELECT Year, Quarter, Month, SUM(Profit) AS Profit FROM sales GROUP BY ROLLUP(Year, Quarter, Month) HAVING Year = 2008;
(рис 22.4) Модифицированная схема ХД учебного примераВывод 9. Пример ROLLUP с использованием уровней иерархии измерения "Время" (Time)
| Year | Quarter | Month | Profit |
|---|---|---|---|
| 2008 | Первый | Январь | 55,00 |
| 2008 | Первый | Февраль | 64,00 |
| 2008 | Первый | Март | 71,00 |
| 2008 | Первый | NULL | 190,00 |
| 2008 | Второй | Апрель | 75,00 |
| 2008 | Второй | Май | 86,00 |
| 2008 | Второй | Июнь | 88,00 |
| 2008 | Второй | NULL | 249,00 |
| 2008 | Третий | Июль | 91,00 |
| 2008 | Третий | Август | 87,00 |
| 2008 | Третий | Сентябрь | 101,00 |
| 2008 | Третий | NULL | 279,00 |
| 2008 | Четвертый | Октябрь | 109,00 |
| 2008 | Четвертый | Ноябрь | 114,00 |
| 2008 | Четвертый | Декабрь | 133,00 |
| 2008 | Четвертый | NULL | 356,00 |
| 2008 | NULL | NULL | 1074,00 |
Обратим внимание на некоторые особенности использования предложений
Предложения
SELECT не влияет на использование предложений
Предложение ORDER BY команды SELECT не влияет на использование предложений ORDER BY применяется ко всем строкам результирующего множества.
В настоящей лекции мы рассмотрели расширения
Использование предложений
В заключение приведем подробный синтаксис
Синтаксис, совместимый со стандартом ISO/IEC 9075-5:
GROUP BY <group by spec>
<group by spec> ::=
<group by item> [ ,...n ]
<group by item> ::=
<simple group by item>
| <rollup spec>
| <cube spec>
| <grouping sets spec>
| <grand total>
<simple group by item> ::=
<column_expression>
<rollup spec> ::=
ROLLUP ( <composite element list> )
<cube spec> ::=
CUBE ( <composite element list> )
<composite element list> ::=
<composite element> [ ,...n ]
<composite element> ::=
<simple group by item>
| ( <simple group by item list> )
<simple group by item list> ::=
<simple group by item> [ ,...n ]
<grouping sets spec> ::=
GROUPING SETS ( <grouping set list> )
<grouping set list> ::=
<grouping set> [ ,...n ]
<grouping set> ::=
<grand total>
| <grouping set item>
| ( <grouping set item list> )
<empty group> ::=
( )
<grouping set item> ::=
<simple group by item>
| <rollup spec>
| <cube spec>
<grouping set item list> ::=
<grouping set item> [ ,...n ]
Синтаксис, не предусмотренный стандартом ISO/IEC 9075-5:
[ GROUP BY [ ALL ] group_by_expression [ ,...n ]
[ WITH { CUBE | ROLLUP } ]
]
<column_expression>
Цель лекции
Изучив материал настоящей лекции, вы будете знать:
GROUPING для агрегации данных в результирующем множестве;и научитесь:
GROUPING.SQL – язык манипулирования данными в реляционной БД. В настоящей лекции мы сконцентрируем внимание на тех возможностях, которые предоставляет SQL для аналитической работы в ХД.
Единственным средством общения и администраторов БД, и проектировщиков, и разработчиков, и пользователей с реляционной БД является
SQL — непроцедурный язык, который предназначен для обработки множеств, состоящих из строк и колонок таблиц реляционной БД. Существуют расширения SQL, допускающие процедурную обработку. Проектировщики используют SQL для создания всех физических объектов реляционной БД или ХД.
Теоретические основы SQL были заложены в известной статье [Кодд], положившей начало развитию теории реляционных БД. Первая практическая реализации была выполнена в исследовательских лабораториях фирмы IBM Chamberlin D.D. и Royce R.F. Промышленное применение SQL было впервые реализовано в
Первый международный стандарт языка SQL был принят в 1989 г. (
Каждой конкретной СУБД соответствует своя собственная реализация SQL, в целом поддерживающая определенный стандарт, но имеющая свои особенности. Такие реализации называются диалектами. Так, стандарт ISO/IEC 9075-5 предусматривает объекты, называемые постоянно хранимыми модулями, или
Далее в примерах будет использоваться диалект SQL (Transact-SQL) СУБД MS SQL Server 2005/2008.
Синтаксис
<SELECT statement> ::=
[WITH <common_table_expression> [,...n]]
<query_expression>
[ ORDER BY { order_by_expression | column_position [ ASC | DESC ] }
[ ,...n ] ]
[ COMPUTE
{ { AVG | COUNT | MAX | MIN | SUM } ( expression ) } [ ,...n ]
[ BY expression [ ,...n ] ]
]
[ <FOR Clause>]
[ OPTION ( <query_hint> [ ,...n ] ) ]
<query_expression> ::=
{ <query_specification> | ( <query_expression> ) }
[ { UNION [ ALL ] | EXCEPT | INTERSECT }
<query_specification> | ( <query_expression> ) [...n ] ]
<query_specification> ::=
SELECT [ ALL | DISTINCT ]
[TOP ( expression ) [PERCENT] [ WITH TIES ] ]
< select_list >
[ INTO new_table ]
[ FROM { <table_source> } [ ,...n ] ]
[ WHERE <search_condition> ]
[ <GROUP BY> ]
[ HAVING < search_condition > ]
SELECT select_list [ INTO new_table ] определяет список имен колонок таблиц БД, представлений или производных полей.FROM table_source определяет список имен таблиц и представлений БД, участвующих в выборке.WHERE search_condition определяет предикат отбора строк в выборку.GROUP BY group_by_expression определяет условия группировки строк в выборке.HAVING search_condition, которое условия поиска на разбиении результирующего множества ORDER BY order_expression [ ASC | DESC ] определяет правила упорядочивания строк в выборке.Операторы UNION, EXCEPT и INTERSECT используются для построения комбинации
Теперь перейдем к примерам конструирования запросов к схеме типа "звезда". Рассмотрим схему "звезда" ХД, предназначенного для анализа сбыта продукции торговой организации (рис 22.1).
(рис 22.1) Схема "звезда" для анализа сбыта продукции торговой организацииХД предназначено для анализа системы сбыта продукции организации и включает, помимо времени, следующие объекты:
| Имя поля | Описание |
|---|---|
| product_id | Идентификатор товара |
| product_name | Наименование товара |
| product_category | Категория товара |
| Имя поля | Описание |
|---|---|
| time_id | Идентификатор времени |
| time_month | Месяц |
| time_quarter | Квартал |
| time_year | Год |
| time_dayno | День |
| time_weekno | Неделя |
| time_day_of_week | День недели |
| Имя поля | Описание |
|---|---|
| customer_id | Идентификатор покупателя |
| customer_name | Покупатель |
| customer_address | Адрес |
| customer_city | Город |
| customer_subregion | Район |
| customer_region | Область |
| customer_postalcode | Почтовый индекс |
| customer_age | Возраст |
| customer_gender | Тип покупателя |
| Имя поля | Описание |
|---|---|
| region_id | Идентификатор региона |
| region_name | Наименование региона |
| region_country | Страна |
| Имя поля | Описание |
|---|---|
| sales_transaction_id | Идентификатор транзакции |
| product_id | Идентификатор товара |
| customer_id | Идентификатор покупателя |
| time_id | Идентификатор времени |
| region_id | Идентификатор региона |
| sales_quantity_sold | Количество проданного товара |
| sales_dollar_amount | Цена в долларах проданного товара |
Таким образом, получаем одну таблицу фактов и четыре таблицы измерений. Рассмотрим, как конструируется
Рассмотрим пример запроса к схеме, приведенной на рис 22.1.
Пример 22.1. Пусть требуется просмотреть данные о продажах товара с идентификационным номером 33 за месяцы с мая по август текущего года по региону "Москва" с идентификационным номером 81. Тогда запрос может выглядеть следующим образом:
SELECT SUM(sales_dollar_amount* sales_quantity_sold), time_month, region_name FROM Sales, Time, Region WHERE Sales.region_id = Region.region_id AND Sales.time_id = Time_time_id AND Sales.product_id = 33 AND Sales.region_id = 81 AND Time.time_month BETWEEN 'Май' AND 'Август' AND Time.time_year = 2009 GROUP BY time_month, region_name
Изменяя данные о регионе, месяцах и товаре, при помощи вышеприведенного запроса можно выявить тенденции изменения данных о продажах. Для схемы "звезда" характерно использование односторонних
Метрика типа "Объем продаж", рассмотренная в предыдущем примере, является
Метрика "Остаток на складе" является типичным примером
(рис 22.2) Схема "звезда" с полуаддитивным фактом в таблице фактовВ схеме представлено три таблицы измерений: "Месяц" (Data_month), "Магазин" (Store), "Товары" (Products) и таблица фактов "Остаток на складе" (Quantity_on_hand_fact). Описание таблиц приведено в табл. 22.6 ниже.
| Имя поля | Описание |
|---|---|
| Таблица измерения "Месяц" (Data_month) | |
| month_id | Идентификатор месяца |
| data_month | Месяц |
| data_quarter | Квартал |
| data_year | Год |
| Таблица измерения "Магазин" (Store) | |
| store_id | Идентификатор магазина |
| store_name | Название магазина |
| store_location | Месторасположение магазина |
| store_region | Регион |
| Таблица измерения "Товары" (Products) | |
| product_id | Идентификатор товара |
| product_name | Название товара |
| product_category | Категория товара |
| Таблица фактов "Остаток на складе" (Quantity_on_hand_fact) | |
| month_id | Идентификатор месяца |
| store_id | Идентификатор магазина |
| product_id | Идентификатор товара |
| Quantity_on_hand | Остаток на складе |
Метрика "Остаток на складе" является аддитивной по измерениям "Товары" (Products) и "Магазин" (Store), но не является аддитивной по измерению "Месяц" (Data_month). Рассмотрим, как можно получить итоговое количество товаров на складе в магазине в любой момент времени, используя измерения, для которых
Пример 22.2. Пусть нам необходимо просуммировать остатки товара "Подушка" на складе магазинов за январь 2009 года с учетом месторасположения последних, т.е. определить, сколько нереализованных подушек было в сети магазинов торговой организации в январе 2009 года. Сделаем это за счет соединения таблицы фактов с измерением "Магазин", как показано ниже.
SELECT Store.store_location, SUM(Quantity_on_hand_fact.Quantity_on_hand) FROM Store, Quantity_on_hand_fact, Products, Data_month WHERE Store.store_id = Quantity_on_hand_fact.store_id AND Quantity_on_hand_fact.month_id = Data_month.month_id AND Products.product_id = Quantity_on_hand_fact.product_id AND Data_month.data_month = 'Январь' AND Data_month.data_year = 2009 AND Products.product_name ='Подушка' GROUP BY Store.store_location
Аналогично можно суммировать метрику "Остаток на складе" по измерению "Товары", чтобы получать количество нереализованных товаров, сгруппированных по категориям товара.
В примерах, приведенных выше, мы использовали агрегатную функцию SUM() для суммирования и
В ХД, как правило, отношение имеет внутреннюю структуру, и при его обработке требуется проводить разбиение отношения на подмножества, обладающие тем или иным значением определенного атрибута. Например, в достаточно общей постановке вопрос можно сформулировать так: протабулировать значение некоторой функции на каждом из этих подмножеств в соответствии с общим значением атрибута.
Кроме функции SUM(), к агрегатным функциям относятся функции: AVG() – вычисляет среднее значение, MIN() – вычисляет минимальное значение, MAX() – вычисляет максимальное значение, COUNT() – вычисляет количество итемов в результирующем множестве (или элементе разбиения результирующего множества), и ряд других, предусмотренных реализацией SQL в конкретной СУБД.
На использование колонок группировки существуют ограничения. В качестве колонок группировки в данном предложении SELECT можно указывать только колонки из заданного списка, а не любые из таблицы. В конкретных реализациях SQL предусмотрен еще целый ряд ограничений.
Можно задавать условия выборки на результаты выполнения WHERE состоит в том, что первое выбирает подмножества из разбиения целиком в зависимости от его агрегируемых свойств, в то время как последнее просматривает содержимое каждого из этих подмножеств построчно, не учитывая полученное разбиение.
Иногда наблюдается более быстрое выполнение команды SELECT с использованием WHERE.
Производители промышленных реляционных СУБД стремятся расширить возможности аналитической обработки данных в своих диалектах SQL. Обычно расширение таких возможностей SQL выполняется в следующих направлениях:
SELECT.Предложения
Другие расширения SQL включают в себя семейство функций для вычисления регрессий и if – then.
Одной из ключевых концепций систем поддержки принятия решений (DSS) и информационных систем руководителя (
Типичными примерами вопросов в многомерном анализе являются такие, которые мы будем называть многомерными запросами (MDQ).
Во всех перечисленных вопросах используется несколько измерений. Во многих MDQ требуется агрегировать данные по времени, географии или финансам и сравнивать полученные наборы данных.
Для визуализации данных, которые имеют несколько измерений, аналитики используют аналогию с
Вы можете разворачивать (делать сечения) данные (slices of data) из куба. Это соответствует перекрестному отчету, показанному в табл. 22.7. Например, региональный менеджер может изучать данные, сравнивая сечения куба по различным рынкам. Менеджер по товарам может сравнивать сечения куба по различным продуктам.
Ответы на MDQ часто требуют доступа к большому количеству данных, агрегации этих данных по уровням
Возможности агрегирования данных используются не только в многомерном анализе. Обработка транзакций, например, в финансовых или производственных системах (ERP), также генерирует большое число отчетов. Эффективность таких систем возрастает, когда создание отчетов не очень ограничивает нагрузку на систему. В практике финансовых и ERP-систем большое количество отчетов генерируется в ночное время, когда число пользователей таких систем значительно снижается. Важно, что проектировщики БД и ХД должны решать задачу оптимизации запросов, которые используют агрегацию и суммирование данных на различных уровнях их детализации, и в частности такие задачи, как:
Для иллюстрации расширений SQL в настоящей лекции мы взяли гипотетическое ХД организации, которая продает и сдает напрокат видеокассеты. В ХД сохраняется информация о действиях организации в нескольких регионах, отлеживаются продажи и
(рис 22.3) Схема "звезда" для хранилища данных организации, торгующей видеопродукцией| Имя поля | Описание |
|---|---|
| Таблица измерения "Время" (Time) | |
| Time | Год |
| Таблица измерения "Регион" (Region) | |
| Region | Наименование региона |
| Country | Страна |
| Таблица измерения "Отделы продаж" (Department) | |
| Department | Отдел продаж |
| Manager | Руководитель отдела |
| Location | Месторасположение |
| Таблица фактов "Продажи" (Sales) | |
| sales_id | Идентификатор продажи |
| Time | Год |
| Region | Наименование региона |
| Department | Отдел продаж |
| Profit | Прибыль |
В табл. 22.8 приведен типичный отчет, который руководство компании может запросить для анализа деятельности компании за определенный период времени.
| 2008 | |||
|---|---|---|---|
| Регион | Отдел продаж | ||
| Прибыль от проката | Прибыль от продажи | Итоговая прибыль | |
| Центральный | 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() и
Рассмотрим теперь подробнее расширения
SELECT вычислять многоуровневые частичные суммы для специфицированных групп измерений. Также вычисляется итоговая сумма.
Синтаксис:
SELECT ... GROUP BY ROLLUP(grouping_column_reference_list)
Действия ROLLUP являются следующими: создаются частичные суммы для каждого из раскрываемых уровней от наиболее низкого уровня иерархии к более высокому уровню и вычисляется итоговая сумма в соответствии с указанным списком колонок в GROUP BY в порядке возрастания их значений, справа налево по списку колонок группировки. И окончательно создается итоговая сумма (grand total).
n+1 уровней, где n есть число колонок группировки. Например, если в запросе указан ROLLUP на колонки группировки измерений "Время" (Time), "Регион" (Region) и "Отдел продаж" (Department) ( n=3 ), то результирующее множество (result set) будет включать в себя строки для 4-х уровней агрегации.
Рассмотрим примеры.
Пример 22.3. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2007-08 гг. Предложение схемы ХД может выглядеть следующим образом.
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY ROLLUP(Time, Region, Department);
Вывод 1: Агрегирование в ROLLUP для трех измерений
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | NULL | 598,00 |
| NULL | NULL | NULL | 1124,00 |
Как видно из примера выше, запрос возвращает следующий набор строк:
ROLLUP ;Заметим, что NULL-значения показываются только для ясности. В действительности при выводе будут показаны пробелы.
NULL-значения, возвращаемые в результате выполнения предложений
Можно использовать ROLLUP используют синтаксис как показано ниже:
GROUP BY expr1, ROLLUP(expr2, expr3);
В этом случае (2+1=3) уровней агрегации (aggregation levels), т.е. для уровней (expr1, expr2, expr3), (expr1, expr2) и (expr1). Итоговая сумма (grand total) не создается.
Пример 22.4. Пусть руководству компании требуется отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. без итоговой суммы прибыли. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY Time, ROLLUP (Region, Department);
Вывод 2. Использование
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | NULL | 598,00 |
Как видно, запрос возвращает следующее множество строк:
ROLLUP ;Можно вычислить частичные суммы без использования
SELECT Time, Region, Department, SUM(Profit) FROM Sales GROUP BY Time, Region, Department UNION ALL SELECT Time, Region, '' , SUM(Profit) FROM Sales GROUP BY Time, Region UNION ALL SELECT Time, '', '', SUM(Profit) FROM Sales GROUP BY Time UNION ALL SELECT '', '', '', SUM(Profit) FROM Sales;
Как видно из примера выше, для этого требуется для n измерений n+1 SELECT с UNION ALL.
ROLLUP(y, m, day) или ROLLUP(country, state, city).Частичные суммы, генерируемые ROLLUP(Time, Region, Department). Для этого нужно изменить порядок колонок группировки в предложении ROLLUP: ROLLUP(Time, Department, Region). Простой способ генерации полного набора частичных сумм для перекрестных отчетов состоит в использовании расширения CUBE
SELECT вычислить частичные суммы для всех возможных комбинаций групп измерений. Оно также вычисляет итоговую сумму. Подобно ROLLUP,
Синтаксис:
SELECT ... GROUP BY CUBE (grouping_column_reference_list)
Из примера ниже видно, что CUBE берет указанный набор колонок группировки и создает частичные суммы для всех возможных комбинаций значений этих колонок. С точки зрения многомерного анализа, CUBE(Time, Region, Department), то результирующее множество запроса будет включать все значения, которые входят в аналогичную конструкцию ROLLUP, плюс набор дополнительных комбинаций.
Пример 22.5. Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY CUBE(Time, Region, Department);
Вывод 3. Выполнение CUBE с агрегацией по трем измерениям
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | VideoRental | 279,00 |
| 2008 | NULL | VideoSales | 319,00 |
| 2008 | 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 для вычисления частичных сумм аналогично использованию
Синтаксис:
GROUP BY expr1, CUBE(expr2, expr3);
В результате выполнения этой команды будет вычислено 4 частичные суммы:
(expr1, expr2, expr3)(expr1, expr2)(expr1, expr3)(expr1)Пример 22.6. Пусть руководству компании требуется перекрестный отчет о прибыли по всем регионам по всем отделам продаж за 2007-2008 гг. без вывода частичных сумм. Предложение схемы ХД может выглядеть следующим образом:
SELECT Time, Region, Department, SUM(Profit) AS Profit FROM sales GROUP BY Time CUBE(Region, Department);
Вывод 4. Использование CUBE для вычислений частичных сумм
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 |
| 2007 | Центральный | VideoSales | 74,00 |
| 2007 | Центральный | NULL | 149,00 |
| 2007 | Восточный | VideoRental | 89,00 |
| 2007 | Восточный | VideoSales | 115,00 |
| 2007 | Восточный | NULL | 204,00 |
| 2007 | Западный | VideoRental | 87,00 |
| 2007 | Западный | VideoSales | 86,00 |
| 2007 | Западный | NULL | 173,00 |
| 2007 | NULL | VideoRental | 251,00 |
| 2007 | NULL | VideoSales | 275,00 |
| 2007 | NULL | NULL | 526,00 |
| 2008 | Центральный | VideoRental | 82,00 |
| 2008 | Центральный | VideoSales | 85,00 |
| 2008 | Центральный | NULL | 167,00 |
| 2008 | Восточный | VideoRental | 101,00 |
| 2008 | Восточный | VideoSales | 137,00 |
| 2008 | Восточный | NULL | 238,00 |
| 2008 | Западный | VideoRental | 96,00 |
| 2008 | Западный | VideoSales | 97,00 |
| 2008 | Западный | NULL | 193,00 |
| 2008 | NULL | VideoRental | 279,00 |
| 2008 | NULL | VideoSales | 319,00 |
| 2008 | NULL | NULL | 598,00 |
Без использования n -мерного куба потребуется 2n команд SELECT с UNION ALL.
Квалификатор DISTINCT имеет ошибочную семантику в предложениях DISTINCT в комбинации с этими предложениями.
Две проблемы возникают при использовании ROLLUP и CUBE. Первая: как можно программно определить, какие строки результирующего множества являются частичными суммами, и как найти точный уровень агрегации данной частичной суммы? Часто необходимо использовать частичные суммы для вычислений процентных отношений между суммами, поэтому нужен простой способ находить частичные суммы. Вторая проблема: что произойдет, если результат запроса содержит и NULL-значение хранимых строк, и псевдо-NULL-значения, созданные ROLLUP или CUBE? Как различить их в результирующем множестве?
Для решения этой задачи предназначена CUBE или ROLLUP, или значение 0 — в ином случае.
Синтаксис (указывается в списке предложения SELECT ):
SELECT ... [GROUPING(column_name)...] ...
GROUP BY ... {CUBE | ROLLUP} (column_name)
Пример 22.7. Использование
SELECT Time, Region, Department, SUM(Profit) AS Profit, GROUPING (Time) as T, GROUPING (Region) as R, GROUPING (Department) as D FROM Sales GROUP BY ROLLUP (Time, Region, Department);
Вывод 5. Использование
| Time | Region | Department | Profit | Т | R | D |
|---|---|---|---|---|---|---|
| 2007 | Центральный | VideoRental | 75,00 | 0 | 0 | 0 |
| 2007 | Центральный | VideoSales | 74,00 | 0 | 0 | 0 |
| 2007 | Центральный | NULL | 149,00 | 0 | 0 | 1 |
| 2007 | Восточный | VideoRental | 89,00 | 0 | 0 | 0 |
| 2007 | Восточный | VideoSales | 115,00 | 0 | 0 | 0 |
| 2007 | Восточный | NULL | 204,00 | 0 | 0 | 1 |
| 2007 | Западный | VideoRental | 87,00 | 0 | 0 | 0 |
| 2007 | Западный | VideoSales | 86,00 | 0 | 0 | 0 |
| 2007 | Западный | NULL | 173,00 | 0 | 0 | 1 |
| 2007 | NULL | NULL | 526,00 | 0 | 1 | 1 |
| 2008 | Центральный | VideoRental | 82,00 | 0 | 0 | 0 |
| 2008 | Центральный | VideoSales | 85,00 | 0 | 0 | 0 |
| 2008 | Центральный | NULL | 167,00 | 0 | 0 | 1 |
| 2008 | Восточный | VideoRental | 101,00 | 0 | 0 | 0 |
| 2008 | Восточный | VideoSales | 137,00 | 0 | 0 | 0 |
| 2008 | Восточный | NULL | 238,00 | 0 | 0 | 1 |
| 2008 | Западный | VideoRental | 96,00 | 0 | 0 | 0 |
| 2008 | Западный | VideoSales | 97,00 | 0 | 0 | 0 |
| 2008 | Западный | NULL | 193,00 | 0 | 0 | 1 |
| 2008 | 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" — итоговая сумма.
Пример 22.8. Предположим, что CUBE.
Вывод 6.
| Time | Region | Profit |
|---|---|---|
| 2007 | Восточный | 200,00 |
| 2007 | 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-значения, построенные
Использование CASE -выражением и функцией преобразования типов данных CAST (expression AS data_type [ (length ) ]) позволяет решить эту проблему.
Выражение CASE выполняет оценку списка условий и возвращает одно из нескольких возможных выражений результатов.
Синтаксис:
CASE input_expression
WHEN when_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
или
CASE
WHEN Boolean_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
Теперь можно преобразовать отчет из примера 22.8 таким образом, чтобы выделить агрегаты
Пример 22.9. Разграничение агрегатных и хранимых NULL-значений.
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 GROUP BY CUBE(Time, Region);
Вывод 7. Разграничение агрегатных и хранимых NULL-значений
| Time | Region | Profit |
|---|---|---|
| 2007 | Восточный | 200,00 |
| 2007 | 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, содержащим CASE работает с результатом "All Times", если это 1, и значение колонки "Время" (time) из БД, если это 0. Значениями из базы данных будут либо фактическое значение, такое как 2007, или сохраняемое NULL-значение. Функция CAST используется для
CUBE.
Пример 22.10.
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);
Вывод 8. Пример использования
| Time | Region | Department | Profit |
|---|---|---|---|
| 2007 | NULL | NULL | 526,00 |
| 2008 | NULL | NULL | 598,00 |
| NULL | Центральный | NULL | 316,00 |
| NULL | Восточный | NULL | 442,00 |
| NULL | Западный | NULL | 366,00 |
| NULL | NULL | NULL | 1124,00 |
Предложения
Пример 22.11.
SELECT Year, Quarter, Month, SUM(Profit) AS Profit FROM sales GROUP BY ROLLUP(Year, Quarter, Month) HAVING Year = 2008;
(рис 22.4) Модифицированная схема ХД учебного примераВывод 9. Пример ROLLUP с использованием уровней иерархии измерения "Время" (Time)
| Year | Quarter | Month | Profit |
|---|---|---|---|
| 2008 | Первый | Январь | 55,00 |
| 2008 | Первый | Февраль | 64,00 |
| 2008 | Первый | Март | 71,00 |
| 2008 | Первый | NULL | 190,00 |
| 2008 | Второй | Апрель | 75,00 |
| 2008 | Второй | Май | 86,00 |
| 2008 | Второй | Июнь | 88,00 |
| 2008 | Второй | NULL | 249,00 |
| 2008 | Третий | Июль | 91,00 |
| 2008 | Третий | Август | 87,00 |
| 2008 | Третий | Сентябрь | 101,00 |
| 2008 | Третий | NULL | 279,00 |
| 2008 | Четвертый | Октябрь | 109,00 |
| 2008 | Четвертый | Ноябрь | 114,00 |
| 2008 | Четвертый | Декабрь | 133,00 |
| 2008 | Четвертый | NULL | 356,00 |
| 2008 | NULL | NULL | 1074,00 |
Обратим внимание на некоторые особенности использования предложений
Предложения
SELECT не влияет на использование предложений
Предложение ORDER BY команды SELECT не влияет на использование предложений ORDER BY применяется ко всем строкам результирующего множества.
В настоящей лекции мы рассмотрели расширения
Использование предложений
В заключение приведем подробный синтаксис
Синтаксис, совместимый со стандартом ISO/IEC 9075-5:
GROUP BY <group by spec>
<group by spec> ::=
<group by item> [ ,...n ]
<group by item> ::=
<simple group by item>
| <rollup spec>
| <cube spec>
| <grouping sets spec>
| <grand total>
<simple group by item> ::=
<column_expression>
<rollup spec> ::=
ROLLUP ( <composite element list> )
<cube spec> ::=
CUBE ( <composite element list> )
<composite element list> ::=
<composite element> [ ,...n ]
<composite element> ::=
<simple group by item>
| ( <simple group by item list> )
<simple group by item list> ::=
<simple group by item> [ ,...n ]
<grouping sets spec> ::=
GROUPING SETS ( <grouping set list> )
<grouping set list> ::=
<grouping set> [ ,...n ]
<grouping set> ::=
<grand total>
| <grouping set item>
| ( <grouping set item list> )
<empty group> ::=
( )
<grouping set item> ::=
<simple group by item>
| <rollup spec>
| <cube spec>
<grouping set item list> ::=
<grouping set item> [ ,...n ]
Синтаксис, не предусмотренный стандартом ISO/IEC 9075-5:
[ GROUP BY [ ALL ] group_by_expression [ ,...n ]
[ WITH { CUBE | ROLLUP } ]
]
<column_expression>
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.