Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лаврентев Освоение СQЛ 2009

.pdf
Скачиваний:
63
Добавлен:
16.08.2013
Размер:
2.47 Mб
Скачать

51

В запросе использована совместно с RANK() опция PARTITION BY, в которой указывается, в привязке к какому столбцу идет детализация ранга (в нашем случае к столбцу «идентификатор отдела»).

Q3_27. Выполним запрос: определить, подразделив отчет по группам сотрудников, подчиняющихся своим менеджерам (PARTITION BY manager_id), нарастающим итогом количество сотрудников, принятых на работу в заданном временном интервале (100 дней), отсчитываемом от даты принятия на работу под управлением «своего» менеджера первого сотрудника. Для задания интервала в запросе используется опция «RANGE NUMTODSINTERVAL(100, 'DAY') PRECEDING». Здесь «RANGE» диапазон, NUMTODSINTERVAL(100, 'DAY') – интервал в 100 дней, «preceding» означает разницу даты приема на работу (HIRE_DATE – в нашем запросе) очередного извлекаемого запросом сотрудника с датой первого принятого в этом временном интервале на работу сотрудника (в группе сотрудников под управлением «своего» менеджера – ведь мы используем «PARTITION BY manager_id»). Если в группе сотрудников, подчиненных одному менеджеру, дата приема на работу очередного извлекаемого запросом сотрудника отличается от даты первого принятого на работу под управлением этого менеджера сотрудника более, чем на сто дней, отсчет количества принятых на работу сотрудников вновь начинается с единицы.

DEMO@ORCL> SELECT manager_id, last_name, hire_date, COUNT(*) OVER (PARTITION BY manager_id ORDER BY hire_date RANGE NUMTODSINTERVAL(100, 'DAY') PRECEDING) AS t_count FROM employee

WHERE manager_id is NOT NULL;

В отработанном запросе для менеджера с manager_id=7505 к 01 июня 1985 года на работу было принято 4 сотрудника (разница даты приема на работу (четвертым) ROSSа и даты приема на работу (первым) PETERSа меньше 100 дней). Следующим под управление этого менеджера был принят на работу JENSEN 15 января 1987г. Разница

52

этой даты с датой приема на работу PETERSа (31 марта 1985г.) больше 100 дней, поэтому счет количества принятых на работу сотрудников под управлением этого менеджера опять начинается с единицы (T_COUNT=1) .

Если мы выполним тот же запрос с временным интервалом в 1000 дней, внутри групп отдельных менеджеров у нас уже не будет повторяющихся цифр T_COUNT для строк с одним и тем же менеджером.

Q3_28. Продемонстрируем формирование агрегатных данных опцией ROLLUP. ROLLUP обеспечивает агрегирование на каждом уровне, заданном столбцами в GROUP BY. Это приводит к получению информации, которую нельзя было получить раньше без дополнительного кодирования. Следующий запрос извлекает номер отдела, код должности, сгруппированные по этим двум столбцам суммы зарплат, но также и промежуточные итоги общую сумму зарплаты по отделам.

DEMO@ORCL>SELECT department_id, job_id, SUM(salary) AS "Total SAL" FROM employee

GROUP BY ROLLUP (department_id,job_id);

Поменяем порядок столбцов в опции ROLLUP:

DEMO@ORCL>SELECT job_id,department_id, SUM(salary) AS "Total SAL" FROM employee

GROUP BY ROLLUP (job_id, department_id);

53

Отформатируем вывод:

DEMO@ORCL>COL department FORMAT A20 DEMO@ORCL>BREAK ON department

DEMO@ORCL>SELECT NVL(TO_CHAR(department_id), 'Whole Company') AS department , DECODE(job_id, NULL, 'All Employees') AS job, SUM(salary) AS "Total SAL"

FROM employee

GROUP BY ROLLUP (department_id, job_id);

Q3_29. Использование GROUP BY с опцией CUBE

В дополнение к групповым подитогам и общим итогам, созданным ROLLUP, CUBE автоматически вычисляет все возможные комбинации возможных подитогов. Выведем столбцы: код должности, номер отдела, суммарная зарплата, а также идентификаторы группирования по этим столбцам.

DEMO@ORCL>SELECT job_id, department_id, SUM(salary),

GROUPING(department_id), GROUPING(job_id)

FROM employee

GROUP BY CUBE(job_id, department_id);

54

Наличие «1» в столбце grouping(department_id) означает, что группирование проводится по номерам отделов – сумма зарплаты подсчитывается и выводится для строк с одинаковым номером отдела, «1» в столбце grouping(job_id) – по коду работы. Если в обоих столбцах значения «0», подсчитывается сумма по зарплате для этого кода работы и номера отдела.

Q3_30. Получим имя работника, департамент и заработную плату; также требуется получить общую сумму заработной платы по департаментам и процент заработной платы конкретного служащего в сумме департамента и общей сумме (например, работник Х в департаменте Y получает 10 % заработной платы от суммы заработной платы его департамента и 1 % от заработной платы компании) /4/.

DEMO@ORCL>SELECT employee.department_id, employee.last_name, employee.salary, sum(employee4.salary) cum_salary

,round(100*employee.salary/employee2.salary_by_dept,1) AS pct_dept

,round(100*employee.salary/employee3.salary_overall,1) AS pct_overall FROM employee

,(SELECT department_id, sum(salary) AS salary_by_dept

FROM employee GROUP BY department_id ) employee2

,(SELECT sum(salary) AS salary_overall FROM employee ) employee3

,employee employee4

WHERE employee.department_id = employee2.department_id AND employee.department_id = employee4.department_id AND (employee.salary > employee4.salary

OR

(employee.salary = employee4.salary AND employee.last_name >= employee4.last_name)) GROUP BY employee.department_id, employee.last_name, employee.salary

,round(100*employee.salary/employee2.salary_by_dept,1)

,round(100*employee.salary/employee3.salary_overall,1)

55

ORDER BY department_id, salary;

Теперь отработаем этот запрос с помощью аналитических функций. Сначала отформатируем столбцы, чтобы номер отдела повторно не выводился и между группами строк разных отделов была пустая строка:

DEMO@ORCL>COLUMN pct_dept FORMAT 99.9 DEMO@ORCL>COLUMN pct_overall FORMAT 99.9 DEMO@ORCL>BREAK ON department_id SKIP 1

а теперь сам запрос:

DEMO@ORCL>SELECT department_id, last_name, salary

, SUM(salary) OVER (PARTITION BY department_id ORDER BY salary,last_name) AS cum_sal, ROUND(100*RATIO_TO_REPORT(salary) OVER (PARTITION BY department_id), 1 ) pct_dept, ROUND(100*RATIO_TO_REPORT(salary) OVER () , 1 ) AS pct_overall FROM employee

ORDER BY department_id, salary;

Получим такой же точно результат, какой был в предыдущем запросе, который выполнялся без аналитических функций. RATIO_TO_REPORT(salary) – краткая и удобная функция получения процентных долей как внутри отделов OVER (PARTITION BY department_id), так и по всей фирме OVER ().

II.4. Другие команды языка манипулирования данными DМL и обработка транзакций

II.4.1. Команды DML insert, update, delete

Q4_1. Включение в таблицу одной строки

DEMO@ORCL>INSERT INTOdepartment (department_id, name, location_id) VALUES(77, 'Teaching', null);

1 строка создана.

56

Напомним здесь, что при создании таблицы DEPARTMENT столбец location_id был объявлен foreign key с допущением null значений. Если бы вставляли новую строку в таблицу EMPLOYEE, мы не смогли бы вставить ее с null значением в столбце внешнего ключа department_id, так как при создании таблицы EMPLOYEE столбцу department_id было задано ограничение not null.

Вернем таблицу DEPARTMENT к прежнему состоянию:

DEMO@ORCL>ROLLBACK;

Откат завершен.

Повторим предыдущую команду немного в другом варианте:

DEMO@ORCL>INSERT INTO department VALUES(77, 'Teaching', null);

1 строка создана.

Если вставляются все столбцы, указывать после названия таблицы столбцы необязательно.

Повторим откат:

DEMO@ORCL>ROLLBACK;

Откат завершен.

Выполним вставку только в отдельные столбцы:

DEMO@ORCL>INSERT INTO department(department_id,name) VALUES(77,'Teaching');

1 строка создана.

При вставке не всех столбцов их названия надо приводить после названия таблицы. Повторим откат:

DEMO@ORCL>ROLLBACK;

Откат завершен.

Рассмотрим варианты вставки даты. Приведем структуру таблицы SALES_ORDER: DEMO@ORCL>DESC sales_order

Имя

Пусто?

Тип

-------------------------

--------

-------------

ORDER_ID

NOT NULL NUMBER(4)

ORDER_DATE

 

DATE

CUSTOMER_ID

 

NUMBER(6)

SHIP_DATE

 

DATE

TOTAL

 

NUMBER(8,2)

Посмотрим формат и language даты в нашем сеансе (см. раздел II.3.2): DEMO@ORCL>SELECT * FROM nls_session_parameters

WHERE parameter in ('NLS_LANGUAGE', 'NLS_DATE_FORMAT');

В соответствии с этим форматом и выполним вставку:

DEMO@ORCL>INSERT INTO sales_order VALUES(900, '21.07.89',208, '23.07.89', 2345);

1 строка создана.

Выполним откат:

DEMO@ORCL>ROLLBACK;

Откат завершен.

Воспользуемся при вставке даты функцией to_date():

57

DEMO@ORCL>INSERT INTO sales_order

VALUES(900, TO_DATE('21.07.89'), 208, TO_DATE('23.07.89'), 2345);

1 строка создана.

DEMO@ORCL>ROLLBACK;

Откат завершен.

А теперь вставим дату в другом формате:

DEMO@ORCL>INSERT INTO sales_order(order_id, order_date, customer_id, ship_date, total)

VALUES(900, TO_DATE('21-jul-1989', 'DD-MON-YYYY'

,'NLS_DATE_LANGUAGE=AMERICAN')

,208, TO_DATE('23-jul-1989', 'DD-MON-YYYY'

,'NLS_DATE_LANGUAGE=AMERICAN'), 2345);

1 строка создана.

Отметим, что вставленная строка извлекает дату в формате сеанса:

DEMO@ORCL>ROLLBACK;

Откат завершен.

DEMO@ORCL>SELECT * FROM sales_order WHERE order_id=900;

ORDER_ID ORDER_DA

CUSTOMER_ID

SHIP_DAT

TOTAL

----------

--------

-----------

--------

----------

900

21.07.89

208

23.07.89

2345

Q4_2. Создание последовательности

DEMO@ORCL>CREATE SEQUENCE departmentseq START WITH 60;

Последовательность создана.

Q4_3. Используем последовательность для генерации значений столбца department_id: DEMO@ORCL>INSERT INTO department VALUES(departmentseq.NEXTVAL, 'training', 122);

1 строка создана.

DEMO@ORCL>SELECT * FROM department WHERE name='training';

DEPARTMENT_ID

NAME

LOCATION_ID

-------------

--------------

-----------

60

training

122

DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_4. Для вставки копированием с использованием запроса создадим таблицу S_GRADE, которая не будет иметь столбец PRIMARY KEY: DEMO@ORCL>CREATE TABLE s_grade AS SELECT * FROM salary_grade; DEMO@ORCL>SELECT * FROM s_grade;

DEMO@ORCL>INSERT INTO s_grade (SELECT * FROM s_grade); demo@10g>SELECT * FROM s_grade;

58

При наличии столбца ―primary key‖ Oracle не допустил бы вставку повторяющихся по primary key строк. Теперь удалим созданную таблицу s_grade:

DEMO@ORCL>DROP TABLE s_grade;

Таблица удалена.

Q4_5. Вставка одной строки с использованием запроса: DEMO@ORCL>INSERT INTO (SELECT * FROM department) VALUES(90, 'teaching', 122);

1 строка создана. DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_6. Иногда требуется обеспечить ввод определенных значений столбца, не допуская другие значения для ввода. Такой ввод возможен с использованием WITH CHECK OPTION

DEMO@ORCL> INSERT INTO (SELECT customer_id,salesperson_id,zip_code FROM customer

WHERE zip_code = 10009 WITH CHECK OPTION) VALUES(333 , 7557,75051);

INSERT INTO (SELECT customer_id,salesperson_id,zip_code

*

ошибка в строке 1:

ORA-01402: представление WITH CHECK OPTION не соответствует фразе WHERE Ошибка зарегистрирована, так как в опции WITH CHECK OPTION указано

допустимое для ввода значение zip_code = 10009, а вводимое значение 75051.

DEMO@ORCL> INSERT INTO (SELECT customer_id,salesperson_id,zip_code FROM customer

WHERE zip_code = 10009 WITH CHECK OPTION) VALUES(333 , 7557,10009);

1 строка создана.

DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_7 Изменение строк таблицы командой UPDATE Создадим еще раз таблицу s_grade

DEMO@ORCL>CREATE TABLE s_grade AS SELECT * FROM salary_grade;

Таблица создана.

DEMO@ORCL>SELECT * FROM s_grade;

59

Изменим в таблице s_grade значения столбца lower_bound, установив в этом столбце значения из столбца upper_bound таблицы salary_grade

DEMO@ORCL>UPDATE s_grade SET lower_bound=(SELECT upper_bound FROM salary_grade sg WHERE sg.grade_id=s_grade.grade_id);

5 строк обновлено.

DEMO@ORCL>SELECT * FROM s_grade;

DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_8. Выполним простое изменение строки таблицы department DEMO@ORCL>UPDATE department

SET name='TEACHING' WHERE department_id=12;

1 строка обновлена.

DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_9. «Передать» покупателей от продавца PETERS к продавцу SHAW, установив им кредитный рейтинг в 80% от среднего кредитного рейтинга всех покупателей, обслуживаемых (до этой передачи) продавцом SHOW

DEMO@ORCL>UPDATE customer SET salesperson_id = (SELECT employee_id FROM employee where last_name = 'SHAW')

, credit_limit=(SELECT ROUND(0.8*AVG(credit_limit),0) FROM customer WHERE salesperson_id=

(SELECT employee_id FROM employee WHERE last_name = 'SHAW')) WHERE salesperson_id=

(SELECT employee_id FROM employee WHERE last_name='PETERS');

2 строк обновлено.

Q4_10. Удаление строк из таблицы командой DELETE. Удаление сведений обо всех отделах, расположенных в boston

DEMO@ORCL>DELETE FROM department WHERE location_id in (SELECT location_id FROM location WHERE regional_group='BOSTON');

2 строк удалено.

DEMO@ORCL>ROLLBACK;

Откат завершен.

60

Q4_11. Удаление из таблицы location сведений о городах (regional_group), в которых нет отделов

DEMO@ORCL>DELETE FROM location X

WHERE NOT EXISTS (SELECT 1 FROM department WHERE location_id=X.location_id);

2 строк удалено.

DEMO@ORCL>ROLLBACK;

Откат завершен.

Q4_12. Удалим дубликаты записей из таблицы s_grade, предварительно создав их.

DEMO@ORCL>INSERT INTO s_grade (select* FROM s_grade);

DEMO@ORCL>SELECT rowid, s_grade.* FROM s_grade;

DEMO@ORCL>DELETE FROM s_grade X WHERE rowid > (SELECT MIN(rowid)

FROM s_grade Y

WHERE X.grade_id=Y.grade_id);

5 строк удалено.

DEMO@ORCL>SELECT rowid, s_grade.* FROM s_grade;

II.4.2. Обработка транзакций

Завершим предыдущую транзакцию

DEMO@ORCL>commit;

Q4_13. Начнем новую транзакцию. В ходе ее создадим точку сохранения и вернемся к ней, восстановив изменения после нее.

DEMO@ORCL>SELECT * FROM s_grade;