Документация Oracle на русском языке





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

Главная :: Карта


Oracle Database или Oracle RDBMS — объектно-реляционная система управления базами данных компании Oracle.



 

DeepEdit!

Программирование баз данных на Oracle, техническая документация, литература, статьи и публикации

  • Увеличить размер шрифта
  • Размер шрифта по умолчанию
  • Уменьшить размер шрифта

Правильное размещение PL/SQL


Лучшая практика размещения, размещения и ещё раз размещения PL / SQL

Я пишу пакеты и процедуры как в Oracle Database , так и в Oracle Developer ( Oracle Forms ). Как мне выбрать , где разместить код ?

По другому этот вопрос звучит так: "В каком контексте должна быть программа?" То есть, из каких программ приложения она может вызываться? Только из единственной формы? Любой формы? Из единственной программы на сервере? Из любой схемы экземпляра БД?

По этим вопросам я принимаю решение, руководствуясь принципом: "Включу программу как можно ближе к тому месту, где она будет использоваться (вызываться)" .

Демонстрация размещения и перемещения

Для демонстрации различных возможных и подходящих способов определения кода я в этой статье использую следующие бизнес-условия:

Моя команда создает приложение для call-центра. Продавцы продукта моей компании звонят нам, когда у них возникают проблемы, а мы помещаем их звонки в очередь, если они не могут быть обработаны сразу. И вот я должен написать программу, которая распределяет необработанные звонки по членам группы поддержки. Пакет, который будет содержать всю логику, называется call_manager. Процедура для распределения необработанных звонков называется distribute_calls. Листинг 1 показывает спецификацию и тело этой программы.

Листинг 1: процедура distribute_calls

PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE)
IS
BEGIN
WHILE ( calls_are_unhandled ( ) )
LOOP
FOR emp_rec IN emps_in_dept_cur (department_id_in)
LOOP
IF current_caseload (emp_rec.employee_id) <
avg_caseload_for_dept (department_id_in)
THEN
assign_next_open_call (emp_rec.employee_id);
END IF;
END LOOP;
END LOOP;
END distribute_calls;

Как видите, этот выполняемый раздел легко читается: Пока есть хотя бы один необработанный вызов для каждого сотрудника заданного отдела, проверяем, меньше ли текущая загрузка средней по отделу, и связываем очередной вызов с этим сотрудником. Продолжаем обработку, если остались другие необработанные вызовы.

Выполняемый раздел вызывает несколько подпрограмм для выполнения этой работы:

  • Функция calls _ are _ unhandled : аргументов не имеет; возвращает TRUE , если есть хотя бы один необработанный вызов, иначе возвращает FALSE
  • Функция current _ caseload : возвращает количество вызовов (загрузку), связанных с заданным сотрудником
  • Функция avg _ caseload _ for _ dept : возвращает среднее число вызовов, связанных с сотрудниками заданного отдела
  • Процедура assign _ next _ open _ call : связывает вызов с сотрудником, делая его обработанным (в противоположность необработанному)

Один момент: ни одна из этих программ ещё не реализована. Я использую проектирование верхнего уровня, которое называется также пошаговым уточнением ( refinement ), чтобы сосредоточиться на программе с обобщенной, высокоуровневой логикой. Этот способ помогает избежать затрат на всякие мелкие подробности .

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

Как я уже говорил, мое правило следующее: Описание подпрограммы должно быть как можно ближе к месту ее использования. Если следовать этому правилу без какого-либо дополнительного анализа, мне следовало бы описать каждую программу как локальную подпрограмму в самой distribute _ calls , как показано на Листинге 2 (троеточием [...] показано место реализации этих подпрограмм).

Листинг 2: Четыре локальные подпрограммы в distribute _ calls

PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE)
IS
FUNCTION calls_are_handled RETURN BOOLEAN
IS BEGIN ... END calls_are_handled;

FUNCTION current_caseload (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN ... END current_caseload;

FUNCTION avg_caseload_for_dept (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN ... END current_caseload;

PROCEDURE assign_next_open_call (
employee_id_in IN employees.employee_id%TYPE)
IS BEGIN ... END assign_next_open_call;
BEGIN

Процедуры и функции, определенные непосредственно в декларационной секции любого PL / SQL -блока, называются локальными, или вложенными, подпрограммами. В этом примере они могут вызываться только из процедуры distribute _ calls , и это, конечно же, соответствует определению наиближайшего их использования.

Но, как только я это сделал, сразу же поймал себя на мысли о других программах, которые уже написаны в этом пакете и о том, что могу захотеть использовать некоторые из этих подпрограмм в программах, которые напишу в будущем.

Я вспомнил, например, что на последней неделе написал другую функцию, которая очень похожа на current _ caseload . Она сейчас "живет" в процедуре show _ caseload . Прежде чем реализовывать дважды одну и ту же логику (а значит, необходимо отлаживать и поддерживать код в обоих местах), лучше вытащить функцию current _ caseload и из distribute _ calls , и из show _ caseload .

Итак, после небольшой перегруппировки кода, я закончил тело пакета, показанное на Листинге 3.

Листинг 3: Перемещение функции current_caseload

CREATE OR REPLACE PACKAGE BODY call_manager
IS
FUNCTION current_caseload (
employee_id_in IN employees.employee_id%TYPE)
RETURN PLS_INTEGER
IS BEGIN ... END current_caseload;

PROCEDURE show_caseload (
department_id_in IN departments.department_id%TYPE)
IS BEGIN ... END show_caseload;

PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE
)
IS BEGIN ... END distribute_calls;
END;
/

И вот я переместил функцию current _ caseload дальше от distribute _ calls , но сделал это, потому что она используется двумя подпрограммами пакета. Поэтому теперь она близка насколько возможно обоим ее использованиям . Однако я ещё не рассмотрел и не вижу необходимости использования current _ caseload извне пакета distribute _ calls , поэтому я не помещаю заголовок current _ caseload в спецификацию пакета.

Теперь мое внимание переключилось на avg _ caseload _ for _ dept . Что-то в этой программе выглядит знакомым. Что же это , что же это ? Да ! Моя коллега Сандра прислала на последней неделе письмо по электронной почте, извещающее всех нас, что она сложила в один пакет call _ util несколько полезных утилит, включающих функцию, которая возвращает среднюю загрузку сотрудника .

Я хлопнул себя по лбу, откопал письмо, и нашел, что эта функция называется dept _ avg _ caseload . Я проверил, что call _ util у меня есть, и - глядите-ка - функция call _ util . dept _ avg _ caseload вообще-то уже прекрасно реализована в нем, и ждет, когда ее будут использовать.

Тогда я возвращаюсь к процедуре distribute _ calls , удаляю функцию avg _ caseload _ for _ dept и заменяю мою выполняемую часть как показано на Листинге 4.

Листинг 4: Переделанная исполняемая часть distribute _ calls

BEGIN
WHILE ( calls_are_unhandled ( ) )
LOOP
FOR emp_rec IN emps_in_dept_cur (department_id_in)
LOOP
IF current_caseload (emp_rec.employee_id) <
call_util.dept_avg_caseload (department_id_in)
THEN
assign_next_open_call (emp_rec.employee_id);
END IF;
END LOOP;
END LOOP;
END distribute_calls;

Теперь одна из подпрограмм, используемых в моей процедуре, объявлена так далеко, что я не могу даже контролировать ее реализацию, и могу даже никогда не увидеть эту реализацию. Является ли это проблемой ? Нет. Мне больше ничего не надо делать и беспокоиться о ней!

Функция call _ util . dept _ avg _ caseload реализована вне моего использования, но она наиболее близка ко всем использованиям из различных пакетов, и поэтому объявлена в спецификации пакета call _ utils .

Вау. Я думаю, что закончил оптимизацию расположения определений моих подпрограмм. Я остался с двумя локальными подпрограммами ( calls _ are _ unhandled и assign _ next _ open _ call ), одной программой ( current _ caseload ), определенной непосредственно на уровне пакета (без включения в спецификацию пакета), и еще одной функцией ( call _ util . dept _ avg _ caseload ), которая написана кем-то другим и которая доступна всем схемам с правом на выполнение пакета call _ util .

Я надеюсь, что шаги, которые я выполнил, создавая distribute _ calls , помогут вам принимать собственные решения в том, где лучше реализовать ваши сложные, многоуровневые программы.

Размещение кода Oracle Developer

В этой статье уделялось внимание тому, где и как описать код в Oracle Database , однако такие же правила и логика применимы к Oracle Developer . Я предлагаю делать так:

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

Если клиентская программа весьма специфичная форма или отчет, опишите ее в этом же модуле.

oracle
Стивен Ферстайн
 



jAntivirus