
- •Введение.
- •Соединение с бд Oracle.
- •Типы данных Oracle, создание и модификация таблиц и вьюшек.
- •Задача. Удаление дубликатов.
- •Задача. Удаление по условию not in.
- •Функции по работе со строками.
- •Функции по работе с числами.
- •Троичная логика, конструкция with, функции nvl, coalesce, case, decode.
- •Основы pl/sql.
- •Задача. Факториал.
- •Задача. Подсчет числа вхождений символа в строку.
- •Задача. Подсчет числа вхождений подстроки в строку.
- •Задача. Копирование таблицы фиксированной структуры.
- •Задача. Простановка count в цикле.
- •Задача. Распределение данных по условию.
- •Задача. Использование сиквенсов и dbms_random.
- •Задача. Процедура с out параметрами. Нотации.
- •Задача. Простой пакет.
- •Задача. Разные виды Count.
- •Задача. Получение скрипта заполнения таблицы имеющимися данными.
- •Раздача прав (grants).
- •Операции над множествами, joins.
- •Задача. Сравнение множеств.
- •Задача. Загрузка данных с помощью full join.
- •Задача. Проставление роста животных.
- •Оператор merge.
- •Задача. Merge данных о студентах.
- •Задача. Merge данных о клиентах.
- •Функции по работе с датами.
- •Словарь данных.
- •Задача. Last_ddl_time.
- •Задача. Отчет об объектах текущей схемы.
- •Задача. Dbms_output из явного курсора по словарю данных.
- •Задача. Компиляция схемы.
- •Задача. Поиск текста во вьюшках.
- •Динамический sql.
- •Задача. Удаление конкретной таблицы, если она существует.
- •Задача. Удаление любой таблицы с проверкой на существование.
- •Задача. Конструкция '%rowcount'.
- •1) С помощью динамического вызова команды truncate очистить таблицу kk_objects_list от данных.
- •3) Вставить в таблицу kk_objects_list перечень имен всех объектов текущей схемы. С помощью конструкции sql%rowcount выдать в dbms_output количество вставившихся строк.
- •Задача. Удаление всех таблиц и вьюшек.
- •Часть 1. Написать pl/sql блок, который дропает все таблицы текущей схемы, начинающиеся на 'kk_'.
- •Часть 2. Написать pl/sql блок, который выдает в dbms_output скрипт удаления всех вьюшек текущей схемы, начинающихся с 'vw_kk%'.
- •Задача. Отчет о максимальных id.
- •Задача. Переименование столбцов.
- •Задача. Создание вьюшек.
- •Триггеры.
- •Задача. Автоинкрементное поле.
- •Задача. Триггер конвертации сумм в валюте.
- •Задача. Триггер логирования dml-операций.
- •Задача. Триггер зеркалирования.
- •Задача. Триггер по поиску в справочнике.
- •Задача. Триггер контроля.
- •Global temporary tables и переменные в заголовке пакета.
- •Переменные в заголовке пакета.
- •Регулярные выражения.
- •Задача. Правка некачественных данных регулярными выражениями.
- •Задача. Парсинг строк с помощью регулярных выражений.
- •Автономные транзакции.
- •Задача. Автономное логирование этапов загрузки.
- •Иерархические запросы.
- •Задача. Вывод иерархии с отступами.
- •Задача. Соединение иерархической таблицы с обычной.
- •Задача. Сумма зарплат подчиненных.
- •Задача. Простой календарь.
- •Задача. Детальный календарь.
- •Задача. Календарь с индикаторами выходных дней.
- •Аналитические функции.
- •Отступление о Rownum.
- •Негрупповые аналитические функции.
- •Групповая функция keep dense_rank.
- •Задача. Взятие последней записи о клиенте.
- •Задача. Отчет об изменениях зарплаты.
- •Задача. Пакет функций о вкладах.
- •Задача. Группировка лога выполнения процессов.
- •Задача. Вьюшка с аналитическими функциями по вкладам.
- •Задача. Календарь с аналитикой.
- •Задача. Поиск баланса счета на заданную дату.
- •Задача. Удаление исторических данных о рейтинге.
- •Функции, выдающие более одного значения.
- •Задача. Вызов многозначной функции.
- •"Расклейка" и "склейка" строк.
- •“Расклейка” одной строки.
- •“Расклейка” нескольких строк с группировкой.
- •“Склейка” в одну строку.
- •“Склейка” нескольких строк с группировкой.
- •Задача. Простая расклейка-склейка.
- •Задача. Скрипт создания индексов.
- •Задача. Скрипт создания таблиц.
- •Задача. Отчет о созданных объектах.
- •Задача. Отчет о числе строк в таблицах.
- •Задача. Расклейка списка хобби.
- •Задача. Склейка запроса о максимумах числовых полей.
- •Оптимизация запросов.
Аналитические функции.
В этой главе мы расскажем об аналитических (оконных) функциях oracle. Они позволяют в рамках одного сканирования таблицы выдать различную ценную информацию.
Cоздадим таблицу:
CREATE TABLE KK_SALARY(NAME varchar2(50), DT date, WAGE number);
Заполним ее данными:
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Иванов', to_date('01.01.2001','DD.MM.YYYY'), 1000 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Иванов', to_date('01.07.2007','DD.MM.YYYY'), 1400 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Иванов', to_date('01.08.2008','DD.MM.YYYY'), 1600 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Иванов', to_date('01.10.2010','DD.MM.YYYY'), 1500 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Петров', to_date('01.01.2004','DD.MM.YYYY'), 3000 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Петров', to_date('01.01.2005','DD.MM.YYYY'), 2500 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Петров', to_date('01.01.2010','DD.MM.YYYY'), 1700 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Петров', to_date('01.01.2011','DD.MM.YYYY'), 1400 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Рябов', to_date('15.07.2003','DD.MM.YYYY'), 15 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Рябов', to_date('21.04.2009','DD.MM.YYYY'), 1050 );
Insert into KK_SALARY (NAME, DT, WAGE) Values ('Рябов', to_date('11.01.2012','DD.MM.YYYY'), 100500 );
Отступление о Rownum.
Сначала продемонстрируем, как получать номер строки в отсортированном наборе с помощью функции rownum, которая не является аналитической функцией:
select NAME, DT, WAGE, rownum
from (select NAME, DT, WAGE from KK_SALARY order by DT)
where rownum <= 5;
Заметим, что подзапрос тут использован не зря. Именно такую конструкцию следует использовать, чтобы взять три первых записи в наборе, отсортированном по DT.
А такой запрос не гарантирует правильный результат:
select NAME, DT, WAGE from KK_SALARY where rownum <= 5 order by DT;
Rownum можно использовать, если просто в выборке нужно взять несколько первых попавшихся строк без сортировки:
select NAME, DT, WAGE from KK_SALARY where rownum <= 3;
Негрупповые аналитические функции.
Теперь познакомимся с некоторыми наиболее важными аналитическими функциями Oracle.
С помощью функции row_number мы нумеруем строки в наборе, сгруппированном по name и отсортированном по dt.
Функции max over и count over находят максимальное значение поля и количество строк в каждой группе одинаковых name.
Два варианта функции sum over находят соответственно общую сумму и сумму с нарастающим итогом по всей группе одинаковых namе.
С помощью экспоненты и логарифма можно посчитать не сумму, а произведение зарплат во всей группе одинаковых namе.
select name,
dt,
wage,
row_number() over(order by name, dt) row_number_total,
row_number() over(partition by name order by dt) row_number_for_this_name,
max(wage) over(partition by name) maximal_wage_for_this_name,
count(1) over(partition by name) count_rows_for_this_name,
sum(wage) over(partition by name) total_sum_for_this_name,
sum(wage) over(partition by name order by dt) cumulative_sum_for_this_name,
exp(sum(ln(wage)) over(partition by name)) wage_multiplicat_for_this_name
from kk_salary
order by name, dt;
Функции lag и lead позволяют взять предыдущие и следующие значения поля в отсортированном наборе:
select name,
dt,
lag(wage, 2) over(partition by name order by dt) previous_previous_wage,
lag(wage) over(partition by name order by dt) previous_wage,
wage,
lead(wage) over(partition by name order by dt) next_wage,
lead(wage, 2) over(partition by name order by dt) next_next_wage
from kk_salary
order by name, dt;
First_value и Last_value.
Теперь расскажем о негрупповых аналитических функциях first_value и last_value.
Создадим таблицу о результатах рыбной ловли:
create table fish_results(fisher_name varchar2(50), fishing_date date, fishes_catched varchar2(500));
Заполним ее данными. В дни, когда ничего не поймано, в fishes_catched проставляется NULL:
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Курилов',to_date('05.08.2012','dd.mm.yyyy'),'изысканный лосось');
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Курилов',to_date('07.08.2012','dd.mm.yyyy'),'3 изысканных лосося');
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Курилов',to_date('08.08.2012','dd.mm.yyyy'),null);
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Курилов',to_date('09.08.2012','dd.mm.yyyy'),null);
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Охотский',to_date('03.08.2012','dd.mm.yyyy'),'4 краба');
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Охотский',to_date('10.08.2012','dd.mm.yyyy'),'карп и карась');
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Сахалинов',to_date('01.08.2012','dd.mm.yyyy'),null);
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Сахалинов',to_date('02.08.2012','dd.mm.yyyy'),'2 камбалы и щука');
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Сахалинов',to_date('04.08.2012','dd.mm.yyyy'),null);
insert into fish_results(fisher_name,fishing_date,fishes_catched) values('Сахалинов',to_date('06.08.2012','dd.mm.yyyy'),'большой сом, пескарик и 5 окуней');
Продемонстрируем возможности first_value и last_value:
select fisher_name,
fishing_date,
fishes_catched,
first_value(fishes_catched) over(partition by fisher_name order by fishing_date) /*первый улов рыбака*/,
first_value(fishes_catched ignore nulls) over(partition by fisher_name order by fishing_date) /*первый непустой улов рыбака*/,
last_value(fishes_catched) over(partition by fisher_name order by fishing_date) /*последний улов рыбака на момент fishing_date*/,
last_value(fishes_catched ignore nulls) over(partition by fisher_name order by fishing_date) /*последний непустой улов рыбака на момент fishing_date*/
from fish_results
order by fisher_name, fishing_date;