Ганеш Вариар
SQL для бизнеса
(Business-Savvy SQL, by Ganesh Variar)
Источник:
Oracle Magazine, no.2, 2002, http://www.oracle.com/oramag/oracle/02-mar/o22sql.html
Быстрое и эффективное формирование и исполнение сложных бизнес-запросов обеспечит аналитический SQL, включенный в Oracle9i.
|
Web-локатор Дополнительная информация по отдельным функциям содержится
в главах 18 и 19 руководства Еще раз об OTN Приобретение пакета Oracle9i Поддержка мультимедийных средств www.oracle.com/oramag/mar02/ebiz.html Витрина Oracle.com Публикации Oracle |
Пытались ли Вы когда-нибудь написать SQL-запрос, который бы составил список лучших 20% товаров Вашей фирмы? А как насчет того, чтобы сравнить объемы продаж в текущем месяце с аналогичным показателем годичной давности? Если Вам доводилось создавать такие или подобные им, популярные в области коммерции запросы, то Вы, наверняка, знаете о несовершенстве SQL, как средства анализа данных.
К счастью для делового мира, SQL сделал заметный шаг в своем развитии, выразившийся в появлении новых аналитических возможностей, впервые реализованных в Oracle8i Release 2 и улучшенных с выходом Oracle9i. Они упрощают программирование сложных аналитических запросов и повышают эффективность их выполнения.
В данной статье описываются наиболее важные особенности
аналитического (analytic) SQL, уделяется внимание усовершенствованиям Oracle9i,
а также приводятся примеры применения новых SQL-функций для решения сложных
практических задач.
Важнейшие преимущества аналитического SQL
Аналитический SQL в Oracle9i предоставляет ряд существенных преимуществ, включая:
Снижение затрат на написание запросов.
Теперь пространные PL/SQL-блоки могут быть заменены простыми запросами.
Более оперативный доступ к таблицам. В
большинстве случаев новый SQL уменьшает количество просматриваемых данных, что
обуславливает десятикратное сокращение времени доступа, особенно если поиск
осуществляется в крупной таблице.
Кэширование результатов. Результаты
выполненного запроса остаются в кэше, так что впоследствии схожие запросы могут
ими воспользоваться.
Сложные задачи становятся проще
Вот некоторые задачи, выполнение которых становится более простым и эффективным, благодаря этому SQL.
Многомерный анализ данных и кросс-табуляция (Multidimensional
analysis and cross-tabulation). Функция GROUPING и дополнительные
опции CUBE и ROLLUP фразы GROUP BY позволяют рассчитывать
многоуровневые агрегации данных для совокупности столбцов, извлекая тем самым
информацию, обычно необходимую при составлении кросс-табличных отчетов. В
Oracle9i введены понятия составных (composite) столбцов и
конкатенированных группировок (concatenated groupings), а также выражение GROUPING
SETS и функции GROUPING_ID и GROUP_ID.
Ранжирование (ranking). С
помощью функций ранжирования Вы можете вычислять ранги, сотые (percentiles -
процентили) и другие n-ые доли величин в результирующем множестве и
осуществлять последовательную нумерацию строк. В Oracle9i добавлены две
новые важные возможности в области ранжирования: инверсные процентные функции
(inverse percentile functions) и функции условного ранжирования (hypothetical
ranking functions).
Кумулятивное и перемещаемое
агрегирование (оконность) (Cumulative and moving aggregations (windowing)).
Используя фразу windowing и соответствующие функции, можно задать окно
из нескольких последовательных строк, для которых будут применяться агрегатные
функции, что позволит Вам рассчитать не только текущие и обобщенные средние
значения, но и другую статистику.
Разбиение множества результов
(partitioned result sets). Применив фразу PARTITION BY совместно с какой-либо аналитической
функцией, нетрудно разделить множество результов по группам, основанных на
одном или нескольких значениях.
Есть два важных момента, на которые следует обратить внимание при использовании аналитических функций:
Применять аналитические функции в
запросах Вы можете только в предложениях SELECT, включающих фразы ORDER
BY, поскольку эти функции оперируют с множеством результаов, полученным после
соединений (joins) и выполнения фраз WHERE, GROUP BY и HAVING. Заметим также,
что расширения GROUP BY используются только в фразе GROUP BY.
Вы не можете использовать одну
аналитическую функцию напрямую “поверх” другой. Чтоне запрещает, однако, обойти
это ограничение с помощью нескольких вложенных предложений SELECT.
Вложением предложений SELECT можно ограничить (constrain) результаты
аналитической функции.
Примеры из реальной жизни
Для того, чтобы исследовать эффективность и гибкость возможностей аналитическогоSQL, давайте рассмотрим ряд примерных запросов, имеющих отношение к актуальным задачам современного бизнеса. За основу возьмем таблицу следующей структуры:
SALEStrans_date dateproduct_id varchar(10)customer_id varchar(10)sales_amt number
Каждая покупка клиента отражается в таблице в виде записи.
Для простоты предположим, что в таблице отсутствуют столбцы, содержащие
NULL-значения. Однако хочу заметить, что аналитические функции умеют достаточно
элегантно обращаться и с такими объектами.
Определение пятерки самых популярных продуктов среди наиболее активной группы покупателей. “Какие пять товаров чаще всего приобретаются “лучшими” 15% наших клиентов?” - классический вопрос из области анализа потребительского спроса. Функции ранжирования CUME_DIST и RANK помогут реализовать этот запрос в два этапа. После Вы сможете провести небольшой условный анализ, воспользовавшись Oracle9i-механизмом условного ранжирования.
Для начала, используя запрос, приведенный в листинге 1, выявим 15% самых “ценных” клиентов (то есть тех, чей суммарный объем покупок превышает аналогичный показатель оставшихся 85% покупателей). Данный запрос с помощью фразы GROUP BY подсчитывает общий объем покупок по каждому клиенту. Затем применяется функция CUME_DIST, призванная найти значение процентиля для каждого клиента (процент клиентов, чей объем покупок меньше или равен аналогичному значению для текущего клиента). Поскольку Вы не можете использовать аналитическую функцию в фразе WHERE, Вам придется составить внешний запрос с тем, чтобы наложить на результаты CUME_DIST необходимые ограничения. Этот запрос производит выборку значений, удовлетворяющих поставленному выше условию, из результатов внутреннего запроса, отсеивая всех, кроме интересующих нас 15%.
Далее расширим первоначальный запрос и с помощью его модернизированного варианта, приведенного в листинге 2, найдем те пять продуктов, которые чаще всего приобретаются группой “элитных” покупателей. Два внутренних уровня вложенности те же, что и в первой части нашего примера. Список наиболее ценных клиентов служит источником информации для внешнего запроса, расставляющего продукты по их популярности на основе объемов продаж среди тех самых 15%, посредством функции RANK. Самый внешний запрос выделяет из полученного перечня пятерку наиболее ходовых товаров. Финальная фраза ORDER BY располагает итоговую информацию в порядке возрастания популярности продукта.
Продолжая пример, можно провести условный анализ, применив для этого функции условного ранжирования Oracle9i, аргументами которых служат гипотетические величины. Листинг 3 иллюстрирует ситуацию, в которой оказался бы новый продукт, если 15% “самых-самых” покупают товары стоимостью $100.000.
Результаты запроса показывают, что потенциальный продукт
занял бы седьмое место по объемам продаж среди таких клиентов. Чтобы полностью
оценить пример, представьте, как бы Вы достигли тех же результатов без
использования аналитических функций. Сначала Вам пришлось бы с помощью курсора
вычислить 15% “ценной ”клиентуры, а затем, используя дополнительные курсоры и
временные таблицы, определить пятерку “народных” товаров и рейтинг гипотетического
продукта. Или же обойтись без базы данных вообще. На практике большинство
пользователей вычисляют подобные запросы с применением средств анализа,
требующих значительно более мощного технического оснащения, что способствуют
увеличению сетевого трафика и, как следствие, ведет к достаточно слабой
производительности.
Определение самого удачного, с точки зрения объемов продаж, квартала. Очень часто компании интересует вопрос, какой период времени принес им наибольший доход. Листинг 4 содержит пример запроса, который в качестве результата выводит список последовательных трехмесячных временных отрезков, в течение которых прибыль составляла 10 и более миллионов долларов. Запрос сообщает первый и последний месяцы интервала плюс общую сумму выручки за данный промежуток. Этот пример иллюстрирует применение скользящих окон, создаваемых с помощью фразы windowing и аналитических функций FIRST_VALUE, LAST_VALUE и SUM OVER.
Кроме того, в листинге 4 показано, каким образом вложенные предложения SELECT образуют запрос, порождающий нужный результат. Самый внутренний запрос вычисляет общий доход за каждый месяц. Следующий по вложенности запрос использует полученный результат в качестве входных данных. Для каждой входной строки запрос использует оконные функции, чтобы создать трех месячное окно, охватывающее текущий месяц вместе с двумя его предшественниками. Затем для определения первого и последнего месяцев и общей суммы выручки данного трехмесячного периода каждое окно обрабатывается функциями FIRST_VALUE, LAST_VALUE и SUM OVER. Наконец, внешний запрос отсекает все временные интервалы, прибыль за которые не превышала 10 миллионов долларов, располагая оставшиеся в хронологическом порядке.
Благодаря аналитическим функциям, можно избежать сложной
курсорной логики в процессе обработки базы данных.
Выявление тенденций потребления. Многие компании хотят иметь в своем распоряжении анализ активности клиентов за определенный период времени. Обычно подобная информация отображается посредством кросс-табличных отчетов, составление которых иногда представляет собой непростую задачу. На листинге 5 приведен пример, где все это достигается путем одного единственного запроса с привлечением функции NTILE и расширений CUBE (или ROLLUP) в фразе GROUP BY. Там же показывается, как через Oracle9i-механизм конкатенации можно добыть более детальную информацию.
Как и прежде, применение в запросе нескольких аналитических функций реализуется с помощью последовательности вложенных предложений SELECT. Сначала внутренний запрос расчитывает для каждого клиента совокупный объем сделанных им покупок. Затем, в зависимости от полученных для каждого покупателя цифр, все они делятся функцией NTILE на четыре группы или квартала. В первую группу входят 25% наиболее активных клиентов компании, во вторую группа – следующие 25 и так далее.
Внешний запрос проецирует результаты внутреннего на статистику продаж, определяя общее количество сделок, инициированных представителями каждой группы в течение года. Затем с помощью расширения CUBE фразы GROUP BY вычисляются промежуточные суммарные значения объемов продаж по всем группам за каждый год и по каждой группе за все годы. В результате функция CUBE возвращает конечную сумму по всем группам и годам, оставляя неопределенные значения в строках, содержащих конечную и промежуточные суммы. Внешний запрос использует функции GROUPING и DECODE, чтобы в итоговом отчете заменить отсутствующие значения на о смысленные заголовки, что хорошо видно из примерного отчета на листинге 5.
Если Вы хотите уделить особое внимание тенденциям продаж и сбыта товаров в течение нескольких лет, как, впрочем, и месяцев, то можно воспользоваться Oracle9i-механизмом конкатенации для сопряжения двух выражений GROUPING SETS в следующем операторе GROUP BY:
group byGROUPING SETS (to char(trans date, ‘YYYY’),to_char(trans_date, ‘YYYY-MM’)),GROUPING SETS (band, product_id)
Этот запрос вычисляет следующие четыре группировки: (Year,band), (Year,product_id), (Month,band), (Month,product_id).
В отличие от функций CUBE и ROLLUP, которые
вычисляют промежуточные результаты на разных этапах, этот запрос выдает только
те группы, которые Вы запрашивали, причем делает это за один единственных
проход. Чтобы получить те же результаты без использования механизма
конкатенации, Вам потребуется четыре предложения SELECT …GROUP BY,
соединенных фразами UNION ALL и четыре раза произвести сканирование
таблицы.
Вернемся к началу
Аналитические функции в составе Oracle8i и Oracle9i помогают SQL идти в ногу с растущими требованиями, предъявляемыми к оперативному анализу данных. Их наличие ведет к значительному сокращению времени разработки и заметному повышению эффективности Ваших информационно-аналитических запросов. И если грамотно сочетать их между собой, что не раз было продемонстрировано в данной статье, то можно добиться от них максимальной пользы.
Листинг 1. Нахождение 15% самых “ценных” клиентов
select * from (select customer_id as cust_id, sum(sales_amt) as cust_sales, round(100 * CUME_DIST() over(order by sum(sales_amt) asc)) as cust_percentile from sales group by customer_id) where cust_percentile > 85 order by cust_percentile desc; CUST_ID CUST_SALES CUST_PERCENTILE-------- ----------- ---------------133461 2175523 100
270570 1805256 99
160160 1801329 98
...... ....... ..
...... ....... ..
133508 1124910 86
Листинг 2. Пять продуктов наиболее покупаемых “элитой” select * from (select product_id, sum(sales_amt) as product_sales, RANK() over(order by sum(sales_amt) desc) as product_rank from sales where customer_id in (select customer_id from (select customer_id, 100*CUME_DIST() over(order by sum(sales_amt) asc) as cust_percentile from sales group by customer_id) where cust_percentile > 85) group by product_id) where product_rank <= 5 order by product_rank; PRODUCT_ID PRODUCT_SALES PRODUCT_RANK---------------- ---------------------- -----------------SO-1278 3569871 1SE-9087 2998109 2SI-7652 2587397 3SD-0386 2001298 4
CO-8925 1098763 5
Листинг 3.Нахождение гипотетического рейтинга
select RANK(100000)within group (order by product_sales desc) as HRANK from (select product_id, sum(sales_amt) as product_sales from sales where customer_id in (select customer_id from (select customer_id, 100*CUME_DIST() over(order by sum(sales_amt) asc) as cust_percentile from sales group by customer_id) where cust_percentile > 85) group by product_id); HRANK
-----
7
Листинг 4. Нахождение главных трех месячных периодов
select * from (select FIRST_VALUE(to_char(month,'YYYY-MON')) over (order by month range interval '2' month preceding) as start_month, LAST_VALUE(to_char(month,'YYYY-MON')) over (order by month range interval '2' month preceding) as end_month, SUM(monthly_sales) over (order by month range interval '2' month preceding) as three_month_sales from (select trunc(trans_date, 'MM') as month, sum(sales_amt) as monthly_sales from sales group by trunc(trans_date, 'MM'))) where three_month_sales > 10000000 order by to_date(start_month, 'YYYY-MON'); START_MONTH END_MONTH THREE_MONTH_SALES------------ --------- -----------------1999-APR 1999-JUN 101072342000-MAY 2000-JUL 112134782000-JUN 2000-AUG 109872512000-SEP 2000-NOV 11334987 Листинг 5. Выявление тенденций потребления.
select decode(GROUPING(to_char(trans_date, 'YYYY')), 1, 'All Years',to_char(trans_date, 'YYYY')) as Year,decode(GROUPING(band),1, 'All Bands', band) as Band,sum(sales_amt) as total_sales_amtfrom (select customer_id, NTILE(4) over(order by sum(sales_amt)) as band from sales group by customer_id) a, sales bwhere a.customer_id = b.customer_idgroup by cube(to_char(trans_date, 'YYYY'), band); YEAR BAND TOTAL_SALES_AMT------- ------- ---------------1999 1 135068971999 2 109872371999 3 56298141999 4 33289761999 All Bands 334529242000 1 209871542000 2 98523952000 3 56809232000 4 29841092000 All Bands 39504581All Years 1 34494051All Years 2 20839632All Years 3 11310737All Years 4 6313085 All Years All Bands 72957505
Ганеш Вариар ( ganesh_variar@yahoo.com ), cпециалист консалтинговой компании Saama Technologies Inc.(www.saama.com), располагающейся в Силиконовой долине.