- •Лабораторная работа № 11. Пакеты oracle pl/sql
- •Цель работы
- •2 Методические указания
- •3 Задания к лабораторной работе № 11
- •4. Теоретический материал Создание пакетов
- •Правила построения пакетов
- •Спецификация пакета
- •Указания по разработке пакетов:
- •Правила вызова элементов пакета
- •Удаление пакетов
- •Перегрузка пакетов
- •Порядок разрешения вызова
- •Предварительное объявление
- •Одноразовые процедуры
- •Побочные эффекты
- •Ограничение функции в правах обращения
- •Курсорные переменные и пакеты
Правила вызова элементов пакета
Пакет является владельцем своих объектов точно так же, как таблица — владельцем своих столбцов. Чтобы обратиться к элементу, определённому в спецификации пакета, извне этого пакета, необходимо указать полное имя элемента, используя точечную нотацию.
Рассмотрим несколько примеров.
В данной спецификации пакета объявляются константа, исключение, курсор и несколько модулей:
CREATE OR REPLACE PACKAGE emps_inc
IS
max_salary CONSTANT INTEGER := 20000;
thiefs EXCEPTION;
CURSOR emp_cur (emp_id_in IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN EMPLOYEES%ROWTYPE;
FUNCTION get_hiredate ( emp_id_in IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN DATE;
PROCEDURE proc1 (emp_id_in IN EMPLOYEES.EMPLOYEE_ID%TYPE);
END emps_inc;
Для ссылки на любой из этих объектов необходимо использовать имя пакета в качестве префикса перед именем объекта, а именно:
DECLARE
c_emp CONSTANT EMPLOYEES.EMPLOYEE_ID%TYPE := 100;
v_hiredate DATE;
BEGIN
IF (emps_inc.max_salary > 10000)
THEN
OPEN emps_inc.emp_cur(c_emp);
ELSE
v_hiredate := emps_inc.get_hiredate(c_emp);
END IF;
EXCEPTION
WHEN (emps_inc.salary > 20000)
THEN
emps_inc.proc1(c_emp);
END;
Существуют два правила использования элементов пакета:
При ссылке извне (во внешней программе) на элемент, определённый в спецификации пакета, необходимо использовать точечную нотацию в формате имя_пакета.имя_элемента.
При ссылке внутри пакета (в спецификации или теле) на элемент пакета указывать имя пакета не обязательно; PL/SQL автоматически интерпретирует ссылку как направленную на внутренний элемент пакета.
Данные пакета
Данные пакета представляют собой переменные и константы, которые определены на уровне пакета (т.е. не внутри какой-то функции или процедуры пакета). Областью действия данных пакета является не какая-то одна программа, а весь пакет. Структуры данных пакета существуют (сохраняют свои значения) на протяжении всего сеанса, а не только во время исполнения одной программы.
Если данные пакета определены в теле пакета, то они сохраняют свои значения на протяжении сеанса, но доступ к ним разрешён только для элементов этого пакета (приватные данные).
Если данные пакета определены в спецификации пакета, то они сохраняют свои значения на протяжении сеанса и напрямую доступны (как на чтение, так и на запись) любой программе, обладающей привилегией EXECUTE для данного пакета (общие данные).
Если процедура пакета открывает курсор, то он остаётся открытым и доступным на протяжении всего сеанса. Нет необходимости в определении курсора в каждой программе. Один модуль может открывать курсор, а другой — выполнять выборку. Кроме того, переменные пакета могут передавать данные из одной транзакции в другую, т.к. переменные привязаны к сеансу, а не к какой-то определённой транзакции.
Обращение к глобальной переменной пакета
Если необходимо ссылаться на переменную, курсор, константу или исключение внутри пакета, то дополнять их имена именем пакета не требуется, но если вы ссылаетесь на переменную, курсор, константу или исключение из внешнего окружения, необходимо квалифицировать их имена именем пакета. Например:
CREATE OR REPLACE PROCEDURE hire_emp
(v_ename IN EMPLOYEES.FIRST_NAME%TYPE,
v_mgr IN EMPLOYEES.MANAGER_ID%TYPE,
v_job IN EMPLOYEES.JOB_ID%TYPE,
v_sal IN EMPLOYEES.SALARY%TYPE)
IS
v_comm emp.COMMISSION_PCT%TYPE ;
. . .
BEGIN
. . .
-- обращение к глобальной переменной
v_comm:=comm_package.g_соmm;
. . .
END hire_emp;
Пример пакетного курсора
Спецификация пакета:
CREATE OR REPLACE PACKAGE pack_cur
IS
CURSOR c1 IS
SELECT EMPLOYEE_ID
FROM EMPLOYEES
ORDER BY EMPLOYEE_ID DESC;
PROCEDURE proc1_3rows;
PROCEDURE proc4_6rows;
END pack_cur;
/
--Тело пакета:
CREATE OR REPLACE PACKAGE BODY pack_cur
IS
v_empno NUMBER;
PROCEDURE proc1_3rows IS
BEGIN
OPEN c1;
LOOP FETCH c1 INTO v_empno;
DBMS_OUTPUT.PUT_LINE('Id :'||(v_empno));
EXIT WHEN c1%ROWCOUNT >= 3;
END LOOP;
END proc1_3rows;
PROCEDURE proc4_6rows IS
BEGIN
LOOP FETCH c1 INTO v_empno;
DBMS_OUTPUT.PUT_LINE('Id :'||(v_empno));
EXIT WHEN c1%ROWCOUNT >= 6;
END LOOP;
CLOSE c1;
END proc4_6rows;
END pack_cur;
/
Пример вызовов пакетных курсоров:
SQL> SET SERVEROUTPUT ON
SQL> EXECUTE pack1.proc1_3rows;
Id : 7934
Id : 7902
Id : 7900
SQL> EXECUTE pack1.proc4_6rows;
Id : 7876
Id : 7844
Id : 7839
Отметим, что в PL/SQL есть средство, приводящее к автоматическому закрытию пакетного курсора после выполнения каждой процедуры. Это средство – директива PRAGMA SERIALLY_REUSABLE.
Главное назначение этой директивы – экономия памяти на сервере базы данных при большом числе пользователей. Присутствие директивы в пакете приводит к тому, что данные пакета существуют только на время выполнения процедуры пакета, а затем память выделенная под них освобождается.
При использовании этой директивы спецификация и тело пакета начинались бы так:
CREATE OR REPLACE PACKAGE pack_cur IS -- спецификация
PRAGMA SERIALLY_REUSABLE;
CURSOR c1 IS
SELECT EMPLOYEE_ID
FROM EMPLOYEES
. . .
-- тело пакета
CREATE OR REPLACE PACKAGE BODY pack_cur IS
PRAGMA SERIALLY_REUSABLE;
v_empno NUMBER;
. . .
Использование переменных типа Table и Record в пакете
Создаём пакет:
CREATE OR REPLACE PACKAGE emp_package IS
TYPE emp_table_type IS TABLE OF EMPLOYEES%ROWTYPE
INDEX BY BINARY INTEGER;
PROCEDURE read_emp_table(emp_table OUT
emp_table_type);
END emp_package;
CREATE OR REPLACE PACKAGE BODY emp_package IS
PROCEDURE read_emp_table(emp_table OUT
emp_table_type)
IS
i BINARY INTEGER:=0;
BEGIN
FOR emp_record IN (SELECT * FROM EMPLOYEES) LOOP
emp_table(i):=emp_record;
i:=i+1;
END LOOP;
END;
END emp_package;
Вызов процедуры READ_EMP_TABLE из анонимного PL/SQL-блока с помощью SQL*Plus.
SQL> DECLARE
2 emp_table emp_package.emp_table type;
3 BEGIN
4 emp_package.read_emp_table(emp_table);
5 dbms_output.put_line('An example:'|| emp_table(4).ename);
6 END;
