Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МУ к лабораторным работам №11 Пакеты.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
131 Кб
Скачать
☆

Правила вызова элементов пакета

Пакет является владельцем своих объектов точно так же, как таблица — владельцем своих столбцов. Чтобы обратиться к элементу, определённому в спецификации пакета, извне этого пакета, необходимо указать полное имя элемента, используя точечную нотацию.

Рассмотрим несколько примеров.

В данной спецификации пакета объявляются константа, исключение, курсор и несколько модулей:

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;