Функции (FUNCTION) PL/SQL: программирование на примерах

Создание функций на языке PL/SQLФункция PL/SQL представляет собой модуль, который возвращает значение командой RETURN (вместо аргументов OUT или IN OUT). В отличие от вызова процедуры, который представляет собой отдельный оператор, вызов функции всегда является частью исполняемого оператора, то есть включается в выражение или служит в качестве значения по умолчанию, присваиваемого переменной при объявлении.

Возвращаемое функцией значение принадлежит к определенному типу данных. Функция может использоваться вместо выражения, которое имеет тот же тип данных, что и возвращаемое ею значение.


Оглавление статьи[Показать]


 

Функции играют важную роль в реализации модульного подхода. К примеру, реализацию отдельного бизнес-правила или формулы в приложении рекомендуется оформить в виде функции. Вместо того чтобы писать один и тот же запрос снова и снова («Получить имя работника по идентификатору», «Получить последнюю строку заказа из таблицы order для заданного идентификатора компании» и т. д.), поместите его в функцию и вызовите эту функцию в нужных местах. Такой код создает меньше проблем с отладкой, оптимизацией и сопровождением.

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

В приложениях, которые не определяют и не используют функции, со временем обычно возникают трудности с сопровождением и расширением.

 

Структура функции

Функция (рис. 1) имеет почти такую же структуру, как и процедура, не считая того, что ключевое слово RETURN в ней играет совершенно другую роль:

FUNCTION [схема.]имя[( параметр[, параметр...] ) ]
   RETURN возвращаемый_тип 
   [AUTHID DEFINER | CURRENT_USER]
   [DETERMINISTIC]
   [PARALLEL_ENABLE ...]
   [PIPELINED]
   [RESULT_CACHE ...]
   [ACCESSIBLE BY (program_unit_list)
   [AGGREGATE ...]
   [EXTERNAL ...]
IS
   [объявления]
BEGIN
   исполняемые команды 
[EXCEPTION
   обработчики исключений]
END [имя];

Основные элементы этой структуры:

 

Код функции

Рис. 1. Код функции

 

На рис. 1 изображено строение функции PL/SQL и ее различных разделов. Обратите внимание: функция total_sales не имеет раздела исключений.

 

Возвращаемый тип

Функция PL/SQL может возвращать данные практически любого типа, поддерживаемого PL/SQL, — от скаляров (единичных значений вроде даты или строки) до сложных структур: коллекций, объектных типов, курсорных переменных и т. д.

Несколько примеров использования RETURN:

FUNCTION favorite_nickname (
   name_in IN VARCHAR2) RETURN VARCHAR2 
IS
BEGIN
   ...
END;
TYPE pet_t IS OBJECT (
   tag_no	INTEGER,
   NAME	VARCHAR2 (60),
   breed	VARCHAR2(100),
   dob DATE,
   MEMBER FUNCTION age RETURN NUMBER
)
PACKAGE book_info IS
   FUNCTION onerow (isbn_in IN books.isbn%TYPE)
      RETURN books%ROWTYPE;

...
PACKAGE book_info 
IS
   TYPE overdue_rt IS RECORD (
      isbn books.isbn%TYPE, 
      days_overdue PLS_INTEGER);

   TYPE overdue_rct IS REF CURSOR RETURN overdue_rt;
   
   FUNCTION overdue_info (username_in IN lib_users.username%TYPE)
      RETURN overdue_rct;

...

 

Метка END

Вы можете указать имя функции за завершающим ключевым словом END:

FUNCTION total_sales (company_in IN INTEGER) RETURN NUMBER 
IS
BEGIN
   ...
END total_sales;

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

 

Вызов функции

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

DECLARE
   v_nickname VARCHAR2(100) :=
      favorite_nickname ('Steven');
DECLARE
   my_parrot pet_t :=
      pet_t (1001, 'Mercury', 'African Grey',
         TO_DATE ('09/23/1996', 'MM/DD/YYYY'));
BEGIN
   IF my_parrot.age () < INTERVAL '50' YEAR 
    THEN
      DBMS_OUTPUT.PUT_LINE ('Still a youngster!');
   END IF;
DECLARE
   my_first_book books%ROWTYPE;
BEGIN
   my_first_book := book_info.onerow ('1-56592-335-9');

...
DECLARE
   l_name employees.last_name%TYPE;
BEGIN
   SELECT last_name INTO l_name 
   FROM employees
   WHERE employee_id = hr_info_pkg.employee_of_the_month ('FEBRUARY');
   ...

Вызов написанной вами функции из команды CREATE VIEW с использованием выражения CURSOR для передачи результирующего набора в аргументе функции:

VIEW young_managers 
AS
   SELECT managers.employee_id AS manager_employee_id 
     FROM employees managers 
     WHERE most_reports_before_manager 
      (
         CURSOR ( SELECT reports.hire_date 
                  FROM employees reports
                  WHERE reports.manager_id = managers.employee_id
                 ),
         managers.hire_date 
) = 1;

 

 

В PL/SQL, в отличие от некоторых других языков программирования, невозможно просто проигнорировать возвращаемое значение функции, даже если оно не представляет интереса для вас. Например, для следующего вызова функции:

BEGIN
   favorite_nickname('Steven');
END;

будет выдана ошибка PLS-00221: ‘FAVORITE_NICKNAME’ is not a procedure or is undefined.

Функцию нельзя использовать так, как если бы она была процедурой.

 

Функции без параметров

Если функция не имеет параметров, ее вызов может записываться с круглыми скобками или без них. Следующий код демонстрирует эту возможность на примере вызова метода age объектного типа pet_t:

IF my_parrot.age < INTERVAL '50' YEAR -- 9i INTERVAL type 
IF my_parrot.age() < INTERVAL '50' YEAR

 

Заголовок функции

Часть определения функции, предшествующая ключевому слову IS, называется заголовком функции, или сигнатурой. Заголовок предоставляет программисту всю информацию, необходимую для вызова функции:

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

Заголовок упоминавшейся ранее функции total_sales выглядит так:

FUNCTION total_sales
   (company_id_in IN company.company_id%TYPE, 
    status_in IN order.status_code%TYPE := NULL)
RETURN NUMBER

Он состоит из типа модуля, имени и списка из двух параметров и возвращаемого типа NUMBER. Это означает, что любое выражение или команда PL/SQL, в которых задействовано числовое значение, может вызвать total_sales для получения этого значения. Пример:

DECLARE
   v_sales NUMBER;
BEGIN
   v_sales := total_sales (1505, 'ACTIVE');
END;

 

Тело функции

В теле функции содержится код, необходимый для реализации этой функции; тело состоит из объявления, исполняемого раздела и раздела исключений этой функции. Все, что следует за ключевым словом IS, образует тело функции.

Как и в случае с процедурами, разделы исключений и объявлений не являются обязательными. Если обработчики исключений отсутствуют, опустите ключевое слово EXCEPTION и завершите функцию командой END. Если объявления отсутствуют, команда BEGIN просто следует непосредственно за ключевым словом IS.

Исполняемый раздел функции должен содержать команду RETURN. Функция откомпилируется и без него, но если выполнение функции завершится без выполнения команды RETURN, Oracle выдаст ошибку: ORA-06503: PL/SQL: Function returned without value.

Эта ошибка не выдается, если функция передает наружу свое необработанное исключение.

 

Команда RETURN

В исполняемом разделе функции должна находиться по меньшей мере одна команда RETURN. Команд может быть и несколько, но в одном вызове функции должна выполняться только одна из них. После обработки команды RETURN выполнение функции прекращается, и управление передается вызывающему блоку PL/SQL.

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

 

Любое допустимое выражение

Команда RETURN может возвращать любое выражение, совместимое с типом, обозначенным в секции RETURN. Это выражение может включать вызовы других функций, сложные вычисления и даже преобразования данных. Все следующие примеры использования RETURN допустимы:

RETURN 'buy me lunch';
RETURN POWER (max_salary, 5);
RETURN (100 - pct_of_total_salary (employee_id));
RETURN TO_DATE ('01' || earliest_month || initial_year, 'DDMMYY');

Вы также можете возвращать сложные типы данных — экземпляры объектных типов, коллекции и записи.

Выражение в команде RETURN вычисляется в момент выполнения RETURN. При возврате управления в вызывающий блок также передается результат вычисленного выражения.

 

множественные команды RETURN

В функции total_sales на рис. 2 я использую две разные команды RETURN для обработки разных ситуаций в функции: если из курсора не удалось получить информацию, возвращается NULL (не нуль). Если же от курсора было получено значение, оно возвращается вызывающей программе. В обоих случаях команда RETURN возвращает значение: в одном случае NULL, в другом — переменную return_value.

Конечно, наличие нескольких команд RETURN в исполняемом разделе функции разрешено, однако лучше ограничиться одной командой RETURN, размещаемой в последней строке исполняемого раздела. Причины объясняются в следующем разделе.

 

RETURN как последняя исполняемая команда

В общем случае команду RETURN желательно делать последней исполняемой командой; это лучший способ гарантировать, что функция всегда возвращает значение. Объявите переменную с именем return_value (которое четко указывает, что в переменной будет храниться возвращаемое значение функции), напишите весь код вычисления этого значения, а затем в самом конце функции верните значение return_value командой RETURN:

FUNCTION do_it_all (parameter_list) RETURN NUMBER IS
   return_value NUMBER;
BEGIN
   ... множество исполняемых команд ...
   RETURN return_value;
END;

Переработанная версия логики на рис. 2, в которой решена проблема множественных команд RETURN, выглядит так:

OPEN sales_cur;
IF sales_cur%NOTFOUND 
THEN
   return_value:= NULL;
END IF;
CLOSE sales_cur;
RETURN return_value;

Остерегайтесь исключений! Помните, что инициированное исключение может «перепрыгнуть» через последнюю команду прямо в обработчик. Если обработчик исключения не содержит команды RETURN, то будет выдана ошибка ORA-06503: Function returned without value независимо от того, как было обработано исходное исключение.

Вас заинтересует / Intresting for you:

Встроенные методы коллекций PL...
Встроенные методы коллекций PL... 14829 просмотров sepia Tue, 29 Oct 2019, 09:54:01
Управление приложениями PL/SQL...
Управление приложениями PL/SQL... 4647 просмотров Stas Belkov Thu, 16 Jul 2020, 06:20:48
Символьные функции и аргументы...
Символьные функции и аргументы... 18582 просмотров Анатолий Wed, 23 May 2018, 18:54:01
Тип данных RAW в PL/SQL
Тип данных RAW в PL/SQL 12325 просмотров Doctor Thu, 12 Jul 2018, 08:41:33
Печать
Войдите чтобы комментировать