ГЛАВА 9 ---------------------------------------------------------------- УПРАВЛЕНИЕ ЗАВИСИМОСТЯМИ МЕЖДУ ОБЪЕКТАМИ СХЕМЫ Определения некоторых объектов, таких как обзоры и процедуры, ссылаются на другие объекты, например, таблицы. Поэтому некоторые объекты зависят от других объектов, адремуемых в их определениях. Эта глава обсуждает управление зависимостями между объектами. Темы этой главы включают обсуждение следующих вопросов: * использование пакетов для минимизации зависимостей * форсирование компиляции недействительных обзоров, процедур, пакетов и триггеров * вывод информации управления зависимостями Информация о хранимых процедурах и пакетах в этой главе применима лишь к тем системам, которые используют ORACLE с процедурной опцией. Если вы используете Trusted ORACLE, обратитесь также к документу Trusted ORACLE7 Server Administrator's Guide для информации об управлении зависимостями в этом окружении. Управление зависимостями между объектами схемы 9-1 ---------------- Вопросы зависимостей Когда вы создаете хранимую процедуру или пакет, ORACLE проверяет, что операции, которые выполняет эта процедура или пакет, возможны с точки зрения доступа к адресуемым объектам. Например, если хранимая процедура содержит предложение SELECT, выбирающее столбцы из таблицы, то ORACLE проверяет, что эта таблица существует и содержит указанные столбцы. Если таблица впоследствии переопределяется так, что один из ее столбцов перестает существовать, то хранимая процедура может перестать правильно работать. По этой причине хранимая процедура называется ЗАВИСИМОЙ от данной таблицы. В случаях, подобных этому, ORACLE автоматически управляет зависимостями между объектами схемы. После того, как объект схемы будет переопределен, ORACLE автоматически перекомпилирует любую хранимую процедуру и функцию в вашей базе данных, зависящую от переопределенного объекта, при очередном вызове этой процедуры или функции. Эта перекомпиляция позволяет ORACLE проверить, что процедуры и функции могут по-прежнему исполняться правильно на переопределенном объекте. Устранение перекомпиляции во время выполнения --------------------------------------------- Избегайте перекомпиляции хранимых процедур, функций и пакетов во время выполнения ваших приложений. Такая перекомпиляция не только ухудшает производительность; хуже того, результирующие ошибки компиляции могут помешать использованию ваших приложений. Чтобы избежать перекомпиляций времени выполнения, соблюдайте следующие правила: * Не переопределяйте объектов схемы (таких как таблицы, обзоры, хранимые процедуры и функции) в то время, когда работают ваши производственные приложения. Переопределение объектов заставляет ORACLE перекомпилировать хранимые процедуры и функции, зависящие от этих объектов. * Переопределив объект схемы, вручную перекомпилируйте все зависимые процедуры, функции и пакеты. Эта мера не только устранит фактор снижения производительности во время выполнения, но и немедленно уведомит вас об ошибках компиляции и позволит вам заблаговременно исправить их. Вы можете внучную перекомпилировать процедуру, хранимую функцию или пакет с помощью опции COMPILE команды ALTER PROCEDURE, ALTER FUNCTION или ALTER PACKAGE. Для дополнительной информации об этих командах обратитесь к документу ORACLE7 Server SQL Language Reference Manual. 9-2 Руководство разработчика приложений Вы можете определить зависимости между объектами схем в вашей базе данных, запустив скрипт UTLDTREE.SQL. Точное имя и местоположение этого скрипта может зависеть от вашей операционной системы. Дополнительная информация о том, как использовать этот скрипт, приведена в тексте самого скрипта. * Пакетируйте процедуры и функции, когда это возможно. Если процедура или функция размещена в пакете, вы можете модифицировать ее определение, не заставляя ORACLE перекомпилировать другие процедуры и функции, которые вызывают ее. Есть несколько вопросов, касающихся зависимостей, которые должны рассматриваться перед тем, как удалять процедуру или пакет. Дополнительная информация об этих вопросах содержится в документе ORACLE7 Server Concepts Manual. Ниже приведены некоторые рекомендации по управлению зависимостями. СТАРАЙТЕСЬ ИСПОЛЬЗОВАТЬ ПАКЕТЫ. Пакеты дают наиболее эффективный метод предотвращения излишних проверок зависимостей. Следующий пример иллюстрирует преимущества пакетов. Предположим следующую ситуацию: * Независимая процедура PROC зависит от пакетированной процедуры PACK_PROC. * Определение процедуры PACK_PROC изменяется, и тело пакета перекомпилируется. * Спецификация процедуры PACK_PROC в спецификации пакета не изменяется. Несмотря на то, что тело пакета перекомпилировано, независимая процедура PROC, зависимая от пакетированной процедуры PACK_PROC, не становится недействительной и не требует перекомпиляции, потому что спецификация процедуры PACK_PROC не изменилась. Эта техника особенно полезна в распределенных окружениях. Если процедуры всегда являются частями пакетов, то удаленные процедуры, которые зависят от локальных пакетированных процедур, никогда не становятся недействительными, пока спецификация пакета не заменяется или не становится недействительной. Каждый раз, когда вы перекомпилируете процедуру, вы должны проконсультироваться с администраторами других баз данных и разработчиками приложений, чтобы идентифицировать все удаленные зависимые процедуры и обеспечить их перекомпиляцию. Это позволит избежать перекомпиляций во время выполнения и выявит любые возможные ошибки компиляции, которые в ином случае были бы выданы пользователю приложения; для дополнительной информации обратитесь к секции "Ручная перекомпиляция недействительных обзоров, процедур, пакетов и триггеров" на странице 9-4. Управление зависимостями между объектами схемы 9-3 АТРИБУТЫ %TYPE И %ROWTYPE. Атрибут %TYPE предоставляет тип данных переменной, константы или столбца. Этот атрибут особенно полезен при объявлении переменной или аргумента процедуры с типом данных, зависящим от определения столбца в таблице базы данных. Атрибут %ROWTYPE полезен, когда вы хотите объявить переменную-запись, которая должна иметь такую же же структуру, что и строка в таблице или обзоре, или строка, возвращаемая извлечением из курсора. Объявление конструктов через атрибуты %TYPE И %ROWTYPE имеет следующие достоинства и недостатки. * Вы не обязаны знать тип данных столбца или структуру таблицы. Например, список аргументов процедуры, которая вставляет строку в таблицу EMP, мог бы быть объявлен следующим образом: CREATE PROCEDURE hire_fire(emp_record emp%ROWTYPE) AS ... Если вы измените типы столбцов или структуру таблицы, то конструкты, определенные по этим типам или по структуре, автоматически изменятся соответствующим образом. * Когда вы определяете конструкт через ОБЪЕКТ%TYPE или ОБЪЕКТ%ROWTYPE, этот конструкт становится зависимым от ОБЪЕКТА. Если ОБЪЕКТ изменяется, все зависимые от него конструкты становятся недействительными. Коротко говоря, атрибуты %TYPE и %ROWTYPE позволяют устранить один тип зависимости, но создают другой. ---------------- Ручная перекомпиляция недействительных обзоров, процедур, пакетов и триггеров ORACLE динамически (автоматически) перекомпилирует недействительный обзор или программную единицу PL/SQL при очередном обращении. Альтернативно, вы можете заставить ORACLE перекомпилировать обзор иди программную единицу, используя подходящую команду SQL с параметром COMPILE. Принудительная (форсированная) перекомпиляция чаще всего используется для того, чтобы проверить на ошибки, когда известно, что зависимый объект недействителен, но он временно не используется; в таком случае для автоматической перекомпиляции пришлось бы дожидаться очередного обращения к объекту. Чтобы идентифицировать недействительные зависимые объекты, опросите обзоры USER_/ALL_/DBA_OBJECTS; примеры приведены в секции "Вывод информации управления зависимостями" на странице 9-6. 9-4 Руководство разработчика приложений Ручная перекомпиляция обзоров ----------------------------- Чтобы перекомпилировать обзор, используйте команду ALTER VIEW с параметром COMPILE. Например, следующее предложение перекомпилирует обзор EMP_DEPT, содержащийся в вашей схеме: ALTER VIEW emp_dept COMPILE; Привилегии, требуемые для ручной перекомпиляции обзоров Для ручной перекомпиляции обзора вы должны либо иметь его в своей схеме, либо иметь системную привилегию ALTER ANY TABLE. Ручная перекомпиляция процедур и функций ---------------------------------------- Чтобы перекомпилировать независимую процедуру или функцию, используйте команду ALTER PROCEDURE/FUNCTION с параметром COMPILE. Например, следующее предложение перекомпилирует хранимую процедуру UPDATE_SALARY, содержащуюся в вашей схеме: ALTER PROCEDURE update_salary COMPILE; Ручная перекомпиляция пакетов ----------------------------- Чтобы перекомпилировать тело пакета или тело вместе со спецификацией пакета, используйте команду ALTER PACKAGE с параметром COMPILE. Например, следующие предложения перекомпилируют только тело и тело со спецификацией пакета ACCT_MGMT_PACKAGE, соответственно: ALTER PACKAGE acct_mgmt_package COMPILE BODY; ALTER PACKAGE acct_mgmt_package COMPILE PACKAGE; Следующий синтаксис позволяет выполнить перекомпиляцию всех пакетов, процедур и функций. Объекты при этом перекомпилируются в порядке зависимости, что позволяет перекомпилировать каждый из них лишь один раз. EXECUTE DBMS_UTILITY.COMPILE_ALL; Привилегии, требуемые для ручной перекомпиляции процедур или пакетов Для ручной перекомпиляции независимой процедуры или пакета вы должны либо иметь ее (его) в своей схеме, либо обладать системной привилегией ALTER ANY PROCEDURE. Ручная перекомпиляция триггеров ------------------------------- Чтобы перекомпилировать существующий триггер, независимо от того, включен от или выключен, используйте команду ALTER TRIGGER с параметром COMPILE. Например, следующее предложение форсирует компиляцию триггера с именем REORDER: ALTER TRIGGER reorder COMPILE; Привилегии, требуемые для ручной перекомпиляции триггеров Для ручной перекомпиляции триггера вы должны либо иметь его в своей схеме, либо обладать системной привилегией ALTER ANY TRIGGER. Управление зависимостями между объектами схемы 9-5 ---------------- Вывод информации управления зависимостями Следующие обзоры словаря данных выдают информацию о прямых зависимостях и об управлении зависимостями: * USER_ \ ALL_ \ DBA_DEPENDENCIES * USER_ \ ALL_ \ DBA_OBJECTS Замечание: Для полного описания этих обзоров словаря данных обратитесь к приложению D. Приведенные ниже примеры предполагают, что в базе данных были выданы следующие предложения: CREATE TABLE emp . . . ; CREATE PROCEDURE hire_emp BEGIN . . . END; ALTER TABLE emp . . . ; Пример 1: Вывод состояния объекта Обзор словаря данных ALL_OBJECTS выдает информацию обо всех объектах, доступных текущему пользователю, включая состояние каждого объекта (действителен он или недействителен). Например, следующий запрос выдает имя, тип и текущее состояние каждого объекта, доступного текущему пользователю базы данных: SELECT object_name, object_type, status FROM all_objects; Этот запрос возвратит информацию, подобную следующей: OBJECT_NAME OBJECT_TYPE STATUS --------------- --------------- ------------ EMP TABLE VALID HIRE_EMP PROCEDURE INVALID Пример 2: Вывод зависимостей Обзор словаря данных DBA_DEPENDENCIES выдает все зависимые объекты в базе данных, и все объекты, от которых они зависят непосредственно. Например, следующий запрос выдает все зависимые объекты в схеме пользователя JWARD: SELECT name, type, referenced_name, referenced_type FROM sys.dba_dependencies WHERE owner = 'JWARD'; Если предположить, что предложения, показанные в начале этой секции, были выданы пользователем JWARD, то этот запрос возвратит информацию, подобную следующей: NAME TYPE REFERENCED_NAME REFERENCED_TYPE ----------- ------------ ------------------- ------------------- HIRE_EMP PROCEDURE EMP TABLE 9-6 Руководство разработчика приложений Утилита отслеживания зависимостей --------------------------------- Обзоры словаря данных USER_ \ ALL_ \ DBA_DEPENDENCIES предоставляют информацию лишь о непосредственной зависимости объектов. В дополнение к этому, вы можете использовать специальную утилиту отслеживания зависимостей, чтобы выдать как прямые, так и косвенные зависимости для объекта. Чтобы создать утилиту отслеживания зависимостей, выполните скрипт UTLDTREE.SQL. Местоположение этого файла зависит от операционной системы. Скрипт UTLDTREE.SQL создает следующие объекты схемы: Таблица DEPTREE_TEMPTAB Структура: object_id NUMBER referenced_object_id NUMBER nest_level NUMBER seq# NUMBER Временная таблица, используемая для хранения информации о зависимостях, возвращаемой процедурой DEPTREE_FILL. Обзор DEPTREE Имена столбцов: nested_level,object_type,owner,object_name,seq# Обзор, который выдает информацию о зависимостях по таблице DEPTREE_TEMPTAB. Родительский объект имеет нулевое значение NESTED_LEVEL, зависимые объекты имеют ненулевые значения NESTED_LEVEL. Обзор IDEPTREE Имя столбца: dependencies Обзор, который выдает информацию о зависимостях по таблице DEPTREE_TEMPTAB. Вывод представлен в графическом формате, где зависимые объекты выдаются с отступами от тех объектов, от которых они зависят. Последовательность DEPTREE_SEQ Последовательность, используемая для уникальной идентификации наборов зависимостей, хранящихся в таблице DEPTREE_TEMPTAB. Процедура DEPTREE_FILL Синтаксис: DEPTREE_FILL (object_type CHAR, object_owner CHAR, object_name CHAR) Процедура, которая сначала очищает таблицу DEPTREE_TEMPTAB в схеме текущего пользователя, а затем заполняет эту таблицу информацией об объектах, которые прямо или косвенно зависят от указанного объекта (т.е. ссылаются на него). Выдаются все объекты, которые рекурсивно ссылаются на указанный объект, в предположении, что пользователю разрешено знать о существовании этих объектов. Использование UTLDTREE в режиме INTERNAL Если вы запускаете скрипт UTLDTREE.SQL и используете утилиту отслеживания зависимостей, подключившись как INTERNAL, то собирается выдается информация о зависимостях не только для зависимых объектов, но также для зависимых курсоров (разделяемых областей SQL). Управление зависимостями между объектами схемы 9-7 Пример Следующий пример показывает, как применять утилиту UTLDTREE для отслеживания зависимостей объекта. Предположим, что были выданы следующие предложения SQL: CONNECT scott/tiger; CREATE TABLE scott.emp ( ... ); CREATE SEQUENCE scott.emp_sequence; CREATE VIEW scott.sales_employees AS SELECT * FROM scott.emp WHERE deptno = 10; CREATE PROCEDURE scott.hire_salesperson (name VARCHAR2, job VARCHAR2, mgr NUMBER, hiredate DATE, sal NUMBER, comm NUMBER) IS BEGIN INSERT INTO scott.sales_employees VALUES (scott.emp_sequence.NEXTVAL, name, job, mgr, hiredate, sal, comm, 10); END; CREATE PROCEDURE scott.fire_salesperson (emp_id NUMBER) IS BEGIN DELETE FROM scott.sales_employees WHERE empno = emp_id; END; SELECT * FROM scott.emp; SELECT * FROM scott.sales_employees; EXECUTE scott.hire_salesperson ('ARNALL', 'MANAGER', 7839, \ SYSDATE, 1000, 500); EXECUTE scott.fire_salesperson (7934); Допустим, SCOTT хочет изменить (ALTER) таблицу EMP, и перед этим он хотел бы узнать, какие зависимые объекты станут недействительными в результате изменения таблицы EMP. Следующий вызов процедуры заполнит таблицу DEPTREE_TEMPTAB информацией о зависимостях, относящихся к таблице EMP (предполагается, что этот вызов осуществляется из SQL*DBA): EXECUTE deptree_fill('TABLE', 'SCOTT', 'EMP'); 9-8 Руководство разработчика приложений Следующие два запроса показывают информацию о зависимостях, собранную этим вызовом для таблицы EMP: SELECT * FROM deptree; NESTED_LEV TYPE OWNER NAME SEQ# ---------- ---------- -------------- ------------------ -------- 0 TABLE SCOTT EMP 0 1 VIEW SCOTT SALES_EMPLOYEES 1 2 PROCEDURE SCOTT FIRE_SALESPERSON 2 3 PROCEDURE SCOTT HIRE_SALESPERSON 3 SELECT * FROM ideptree; DEPENDENCIES ---------------------------------------- TABLE SCOTT.EMP VIEW SCOTT.SALES_EMPLOYEES PROCEDURE SCOTT.FIRE_SALESPERSON PROCEDURE SCOTT.HIRE_SALESPERSON Альтернативно, с помощью утилиты UTLDTREE вы можете выявить все курсоры, которые зависят от таблицы EMP (т.е. зависимые разделяемые области SQL, которые в данный момент находятся в разделяемом пуле). Для этого подключитесь к базе данных как INTERNAL и выполните процедуру DEPTREE_FILL, как выше; затем выдайте следующие два запроса: SELECT * FROM deptree; NESTED_LEV TYPE OWNER NAME SEQ# ---------- ---------- -------------- ------------------------------ ---- 0 TABLE SCOTT EMP 0 1 CURSOR"select * from scott.emp" 0.5 2 CURSOR "select * from scott.sales_emp 7.5 3 CURSOR "BEGIN hire_salesperson ('ARNA 9.5 3 CURSOR "BEGIN fire_salesperson (7934) 9.5 SELECT * FROM ideptree; DEPENDENCIES ---------------------------------------------------------------- TABLE SCOTT.EMP CURSOR ."select * from scott.emp" CURSOR ."select * from scott.sales_employees" CURSOR ."BEGIN hire_salesperson ('ARNALL', 'MAN CURSOR ."BEGIN fire_salesperson (7934) END"