
- •Лабораторная работа № 11. Пакеты oracle pl/sql
- •Цель работы
- •2 Методические указания
- •3 Задания к лабораторной работе № 11
- •4. Теоретический материал Создание пакетов
- •Правила построения пакетов
- •Спецификация пакета
- •Указания по разработке пакетов:
- •Правила вызова элементов пакета
- •Удаление пакетов
- •Перегрузка пакетов
- •Порядок разрешения вызова
- •Предварительное объявление
- •Одноразовые процедуры
- •Побочные эффекты
- •Ограничение функции в правах обращения
- •Курсорные переменные и пакеты
Спецификация пакета
При создании пакетов все общедоступные конструкции объявляются в спецификации пакета. Если спецификация пакета уже существует, используйте опцию REPLACE.
Если требуется, присвойте переменной при объявлении постоянное значение или формулу; в противном случае переменная автоматически принимает неопределенное начальное значение (NULL).
В спецификации пакета перечислены все доступные для использования в приложениях элементы пакета, а также приведена вся информация, которая необходима разработчику для использования этих элементов (эту информацию часто называют программным интерфейсом приложения (API — application programming interface)). Разработчик должен иметь возможность работать с элементами, приведёнными в спецификации, не обращаясь за разъяснениями по их использованию к коду тела пакета.
--
¦ --
¦ ¦ --
¦ ¦ ¦ процедура
¦ спецификация объекта -+ тело пакета -+ функция
¦ ¦ ¦ процедура
¦ ¦ L-
¦ L-
¦
схема -+
¦
¦ --
¦ ¦ --
¦ ¦ ¦ функция
¦ спецификация объекта -+ тело пакета -+ функция
¦ ¦ ¦ процедура
¦ ¦ L-
¦ L-
¦
¦ другие объекты
¦
L-
Рисунок 2 – Сфера пакетов
Спецификация перечисляет ресурсы пакета, доступные приложениям. Она содержит всю информацию, необходимую вашему приложению для использования этих ресурсов. Например, следующее объявление показывает, что функция с именем fac принимает один аргумент типа INTEGER и возвращает значение типа INTEGER:
FUNCTION fac (n INTEGER) RETURN INTEGER; -- возвращает n!
Это вся информация, необходимая для вызова данной функции. Вам нет необходимости рассматривать фактическую реализацию функции fac (например, итеративна она или рекурсивна).
Только подпрограммы и курсоры имеют реализацию, или ОПРЕДЕЛЕНИЕ. Поэтому, если спецификация пакета объявляет лишь типы, константы, переменные и исключения, тело пакета не нужно.
Приведем пример такого пакета:
Пример: Пакет, состоящий только из спецификации.
PACKAGE trans_data IS
TYPE TimeTyp IS RECORD
(minute SMALLINT,
hour SMALLINT);
TYPE TransTyp IS RECORD
(category VARCHAR2,
account INTEGER,
amount REAL,
time TimeTyp);
minimum_balance CONSTANT REAL := 10.00;
number_processed INTEGER;
insufficient_funds EXCEPTION;
END trans_data;
Пакет trans_data не нуждается в теле, потому что типы, константы, переменные и исключения не требуют реализации. Такие пакеты позволяют вам определять глобальные переменные – для использования подпрограммами и триггерами - которые существуют на протяжении всей сессии.
Приведём некоторые правила создания спецификации пакета:
Вы можете объявлять элементы практически всех типов данных: числа, исключения, типы и коллекции, на уровне пакета (т.е. не внутри конкретной процедуры или функции пакета). Такие данные называются данными уровня пакета. При этом следует избегать объявления переменных в пакете, тогда как объявление констант вполне допустимо. В спецификации (или теле) пакета нельзя объявлять курсорные переменные (переменные, определяемые на основе типа REF CURSOR). Курсорные переменные не могут сохранять своё значение на протяжении сеанса.
Вы можете объявлять практически любые виды структур данных, такие как коллекции, записи или тип REF CURSOR.
Вы можете объявлять в спецификации пакета процедуры и функции, но указывать можно только заголовок программы (все, что находится выше ключевого слова IS или AS).
Вы можете включать в спецификацию пакета явные курсоры. Возможны две формы явного курсора: объявление запроса может включать в себя SQL-запрос, или же запрос может быть спрятан в теле пакета, тогда в объявлении курсора будет присутствовать только предложение RETURN.
Если в спецификации пакета объявлены какие-то процедуры или функции, а также если курсорная переменная объявлена без SQL-запроса, то необходимо написать тело пакета, в котором будут реализованы эти элементы кода.
В спецификацию пакета можно включить предложение AUTHID, которое будет определять, в соответствии с какими привилегиями разрешены любые ссылки на объекты: владельца пакета (AUTHID DEFINER) или пользователя, вызывающего пакет (AUTHID CURRENT_USER).
После оператора END в спецификации пакета можно поместить необязательную метку с именем пакета, например:
END my_package;
Синтаксис создания пакета:
CREATE [OR REPLACE] PACKAGE имя_пакета
[AUTHID] {CURRENT_USER | DEFINER}]
{IS | AS}
[определения общих структур данных]
[объявления общих переменных, типов и объектов]
[объявления исключений]
[директивы]
[спецификации курсоров]
[спецификации функций и процедур]
END [имя_пакета];
Пример 1:
CREATE OR REPLACE PACKAGE comm_pkg
IS
std_comm NUMBER := 0.10; -- начальное значение
PROCEDURE reset_comm(new_comm NUMBER);
END comm_pkg;
/
Примеры вызовов общедоступной переменной или процедуры.
SQL>EXECUTE comm_pkg.reset_comm(8)
Если считать, что процедура создавалась в схеме scott, то вызов таков:
SQL>EXECUTE scott.comm_pkg.reset_comm(8)
А если она на удалённой БД со строкой связи my, то – такой:
SQL>EXECUTE comm_pkg.reset_comm.@my(8)
Пример 2: пакетируются тип записи, курсор и две процедуры управления кадрами (создание тела пакета рассматривается ниже).
CREATE PACKAGE emp_actions AS -- спецификация
TYPE EmpRecTyp IS RECORD (emp_id INTEGER, salary REAL);
CURSOR desc_salary (emp_id NUMBER) RETURN EmpRecTyp;
PROCEDURE hire_employee
(ename CHAR,
job CHAR,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER);
PROCEDURE fire_employee (emp_id NUMBER);
END emp_actions;
CREATE PACKAGE BODY emp_actions AS -- тело
CURSOR desc_salary (emp_id NUMBER) RETURN EmpRecTyp IS
SELECT employeeid, salary
FROM employees
ORDER BY salary DESC;
PROCEDURE hire_employee
(ename CHAR,
job CHAR,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER) IS
BEGIN
INSERT INTO employees
VALUES (empno_seq.NEXTVAL, ename, job,
mgr, SYSDATE, sal, comm, deptno);
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM employees
WHERE employee_id = emp_id;
END fire_employee;
END emp_actions;
Видимыми и доступными для приложений являются лишь объявления в спецификации пакета. Детали реализации в теле пакета скрыты и недоступны. Поэтому вы можете исправлять тело (реализацию), не перекомпилируя вызывающих программ.
Пример 2: Рассмотрим правила создания пакета на примере очень простой спецификации пакета:
CREATE OR REPLACE PACKAGE emps_pkg
AUTHID CURRENT_USER
IS
min_salary CONSTANT PLS_INTEGER := 10000;
-- Объявление типа вложенной таблицы
TYPE codes_nt IS TABLE OF INTEGER;
-- Вложенная таблица объявляется на основе этого типа.
emp_int codes_nt;
TYPE emp_info_rct IS REF CURSOR RETURN employees%ROWTYPE;
PROCEDURE show_emps (list_in IN codes_nt);
FUNCTION most_salary RETURN emp_info_rct;
END emps_pkg;
Как видите, спецификация пакета по структуре очень похоже на секцию объявлений PL/SQL-блока. Однако важным отличием является то, что спецификация пакета не может содержать никакого кода реализации.
Обращение к содержимому пакета
Для обращения к типам, объектам и подпрограммам, объявленным в спецификации пакета, используются квалифицированные ссылки:
имя_пакета.имя_типа
имя_пакета.имя_объекта
имя_пакета.имя_подпрограммы
Вы можете обращаться к содержимому пакета из триггеров базы данных, хранимых подпрограмм, встроенных блоков PL/SQL, а также анонимных блоков PL/SQL, посылаемых в ORACLE интерактивно через SQL*Plus или SQL*DBA. В следующем примере вы обращаетесь к пакетированной переменной miminum_balance, которая объявлена в пакете trans_data:
DECLARE
new_balance REAL;
...
BEGIN
...
IF new_balance < trans_data.minimum_balance THEN
...
END IF;
...
Тело пакета
В теле пакета определяются все общедоступные и частные процедуры и функции. Последовательность, в которой конструкции определяются в теле пакета, имеет значение; конструкция должна быть определена прежде, чем на нее можно будет ссылаться из другой конструкции.
Тело пакета содержит весь код, который необходим для реализации спецификации пакета. Тело пакета требуется не всегда, но оно обязано присутствовать при наличии хотя бы одного из перечисленных далее условий:
Спецификация пакета содержит объявление курсора с предложением RETURN. Необходимо определить в теле пакета оператор SELECT.
Спецификация пакета содержит объявление процедуры или функции. Необходимо представить полную реализацию модуля в теле пакета.
Вам требуется исполнение кода в разделе инициализации тела пакета. Спецификация пакета не включает в себя исполняемый раздел (исполняемые операторы внутри конструкции BEGIN…END); вы можете исполнять код только в теле пакета.
По своей структуре тело пакета очень похоже на определение процедуры, но имеет и некоторые отличительные особенности:
Тело пакета может включать в себя раздел объявлений, исполняемый раздел и раздел исключений. Раздел объявлений содержит полную реализацию всех курсоров и программ, определённых в спецификации, а также определение любых приватных элементов (не приведённых в спецификации). При наличии раздела инициализации раздел объявлений может быть пуст.
Исполняемый раздел пакета принято называть разделом инициализации. Он может содержать код, который исполняется при создании в сеансе экземпляра пакета.
Раздел исключений обрабатывает любые исключения, порождённые в разделе инициализации. Он может присутствовать в конце пакета только в случае наличия раздела инициализации.
Тело пакета может быть составлено следующими способами: оно может включать в себя только раздел объявлений, только исполняемый раздел, исполняемый раздел и раздел исключений, а также раздел объявлений, исполняемый раздел и раздел исключений.
Не разрешается использовать предложение AUTHID в теле пакета, оно должно содержаться в спецификации. В теле пакета могут использоваться любые объекты, объявленные в спецификации этого пакета.
Все правила и ограничения, существующие для объявления структур данных уровня пакета, относятся как к спецификации, так и к телу пакета. Например, запрещено объявлять курсорные переменные.
После оператора END можно поместить необязательную метку с именем пакета, например:
END my_package;
Синтаксис:
CREATE [OR REPLACE] PACKAGE BODY имя_пакета
{IS | AS}
[определения закрытых структур данных]
[объявления закрытых переменных,типов и объектов]
[полные определения курсоров пакета]
[полные определения функций и процедур пакета]
[BEGIN
[выполняемые операторы]
[EXCEPTION
обработка исключений] ]
END [имя_пакета];
Примечание. Выполняемые операторы между BEGIN и EXCEPTION (END) образуют одноразовую процедуру, автоматически запускаемую в момент первого вызова пакета и только в этот момент.
Пример 3: Рассмотрим создание пакета в схеме hr с именем hr.pkgSalary, который бы включал в себя
функцию fSalary, которая должна принимать в качестве входящего параметра номер сотрудника и должна возвращать размер заработной платы для данного сотрудника на основе информации из таблицы hr.employees.
хранимую процедуру pSalary, которая принимала бы в качестве параметров номер сотрудников и размер новой заработной платы для этого сотрудника и изменяла бы размер заработной платы для сотрудника, заменяя его на новый (указанный вами в качестве входящего параметра для хранимой процедуры);
напишите код, который бы использовал функцию и хранимую процедуру из этого пакета.
Решение:
Код на создание пакета может быть таким:
create package pkgSalary
as
function fSalary(emp_id IN number) RETURN number;
procedure pSalary(emp_id number, nSalary number);
end pkgSalary;
--Код на создание тела пакета может быть таким:
create package body pkgSalary
AS
function fSalary(emp_id IN number) RETURN number
is nSalary number(10,2);
BEGIN
SELECT salary
into nSalary
from employees
where employee_id = emp_id;
RETURN nSalary;
end;
procedure pSalary(emp_id number, nSalary number)
AS
BEGIN
update hr.employees
set salary = nSalary
where employee_id = emp_id;
END;
end pkgSalary;
-- Код на запуск функции из пакета для сотрудника
-- с номером 100 может выглядеть так:
begin
dbms_output.put_line(pkgSalary.fSalary(100));
end;
--Код на запуск данной процедуры для сотрудника
--с номером 100 (новая зарплата должна составлять 25000)
--из пакета может выглядеть так:
begin
pkgSalary.pSalary(100, 26000);
end;