Хранимые процедуры и функции. Типы параметров, синтаксис описания формальных параметров.

SQL Server

Хранимая процедура - это подпрограмма, состоящая из SQL операторов и команд T-SQL. Хранимая процедура сохраняется на сервере и выполняется по команде пользователя или вызывается из блока T-SQL. План выполнения процедуры подготавливается во время запуска процедуры, поэтому собственно выполнение процедуры происходит очень быстро. Хранимая процедура может:

  • Содержать параметры (аргументы);
  • Вызывать другие процедуры;
  • Возвращать свой статус вызывающей процедуре или модулю T_SQL, указывающий на успешное окончание или ошибку, и в случае ошибки на ее причину;
  • Возвращать значения параметров вызывающей процедуре или модулю T-SQL;

Синтаксис для создания хранимой процедуры:

create procedure [владелец.]название_процедуры[;номер] [
[(] @название_параметра тип_данных [= default] [output]
[,  @название_параметра тип_данных [= default] [output]]...[)]] [with
recompile]
as sql_операторы

Оператор execute:

[execute] [@return_status =]
	[[[сервер.]база_данных.]владелец.]название_процедуры[;номер]
		[[@название_параметра = ] значение | 
		 [@название_параметра = ] @переменная [output]
[,[@название_параметра = ] значение | 
	 [@название_параметра = ] @переменная [output]...]]
[with recompile]
Параметры

Параметр - это аргумент хранимой процедуры. Один или несколько параметров могут быть объявлены в операторе создания процедуры. Значение каждого параметра, объявленного в операторе create procedure, должно указываться пользователем в момент вызова процедуры.
Названиям параметров должен предшествовать символ "@", а сами эти названия должны соответствовать правилам, установленным для идентификаторов. Для всех параметров должен быть указан системный или пользовательский тип данных, и если необходимо длина этого типа данных. Названия параметров являются локальными по отношению к содержащей их процедуре; такие же названия можно использовать для параметров в другой процедуре.
В операторе create procedure для параметра можно указать значение, принимаемое по умолчанию. Это значение, которое может быть любой константой, используется в качестве аргумента процедуры, если для этого параметра не было указано никакого значения.

CREATE   proc  dbo.tran_Begin /*
 * Инициализация транзакции
 * Если процедура выполняется внутри транзакции, то создается savepoint
 * Иначе инициализируется транзакция
 * На выходе передается null для транзакции и имя точки сохранения для savepoint
 */
(@trName DObjectName output, @explicitSavepointNameFlag DLogical = 0)
as
	declare @d Datetime
begin
--	return 0

	if @@trancount = 0
	begin
		select @trName = null
		begin transaction
	end
	else
	begin
-- если не задано имя и не установлен флаг использования переданного имени, то генерим имя
		if @explicitSavepointNameFlag = 0 or @trName is null
		begin
			select @d = getdate()
			select @trName = 'tr_'+ISNULL(convert(varchar, @@nestlevel), '')+'_'+ISNULL(convert(varchar(50),
				rand(datepart(mm, @d)*100000+datepart(ss, @d)*1000+datepart(ms, @d))), '')
		end
		save tran @trName
	end

	if @@error <> 0
	begin
-- ошибка создания транзакции
		return 5
	end

	return 0
end

CREATE  proc  dbo.tran_Commit /*
 * Подтверждение транзакции
 * Работает в паре с tran_Begin
 * на вход передается имя точки сохранения (транзакции), возвращенное tran_Begin
 * Если вызов идет из транзакции и @trName равно null, то выполняется commit,
 * иначе ничего не делается
 */
(@trName DObjectName)
as
begin
	if @@trancount > 0 and @trName is null
	begin
		commit

		if @@error <> 0
		begin
-- ошибка подтверждения транзакции
			return 6
		end
	end

	return 0
end

CREATE  proc  dbo.tran_Rollback /*
 * Откат транзакции
 * Работает в паре с tran_Begin
 * на вход передается имя точки сохранения (транзакции), возвращенное tran_Begin
 * Если вызов идет из транзакции и @trName равно null, то выполняется откат всей транзакции,
 * иначе выполняется откат до точки сохранения @trName
 */
(@trName DObjectName)
as
begin
	if @@trancount > 0
	begin
		if @trName is null
			rollback tran
		else
			rollback tran @trName

		if @@error <> 0
		begin
-- ошибка отката транзакции
			return 7
		end
	end

	return 0
end
Возврат результатов

Хранимые процедуры сообщают свой «статус возврата», который указывает, была ли выполнена процедура полностью, или нет, а также причины неудачи. Это значение может храниться в переменной, которая передается процедуре при ее вызове, и использоваться в последующих операторах Transact-SQL. Другой способ возврата информации из хранимых процедур состоит в возврате значений через выходные параметры. Параметры, определенные как выходные, в операторе create procedure (создать процедуру) или execute (выполнить) используются для возврата значений в место вызова процедуры. Затем с помощью условных операторов можно проверить возвращаемое значение.
Код возврата и выходные параметры позволяют разделить хранимые процедуры на модули. Группа SQL операторов, которые используются несколькими хранимыми процедурами, могут быть объединены в одну процедуру, которая сообщает свой статус выполнения или значения своих параметров вызывающей процедуре. Например, многие системные процедуры, поставляемые с SQL Сервером, обращаются к процедуре, которая проверяет являются ли указанные параметры правильными идентификаторами.
Если в операторах create procedure и execute указывается опция output в названии параметра, то процедура возвращает значение этого параметра вызывающему объекту. Этим объектом может быть SQL пакет или другая хранимая процедура, которые используют возвращаемые значения в своей дальнейшей работе. Если возвращаемые параметры используются в операторе execute, который является частью пакета, то значения возвращаемых параметров вместе с заголовком выводятся на экран перед выполнением последующих операторов пакета.

	declare @tranName DObjectName
declare @retCode integer
begin

	execute tran_Begin @tranName output

select @tranName
.........................................

Oracle

Создание процедур
create or replace procedure my_proc(i in number := 123, j out number, k in out number /* error := 123 */)
as
begin
    dbms_output.put_line('i = ' || i);
    dbms_output.put_line('j = ' || j);
    dbms_output.put_line('k = ' || k);

    if i is null then
        return;
    end if;

-- error
--    i := 10;
    j := 20;
    k := 30;
end;

create or replace procedure my_proc_error(i number /* error (1) */, s varchar2 /* error (10) */)
as
begin
    null;
end;

declare
    a number;
    b number;
    c number;
begin
    a := 1;
    b := 2;
    c := 3;
    my_proc(a, b, c);

    dbms_output.put_line('a = ' || a);
    dbms_output.put_line('b = ' || b);
    dbms_output.put_line('c = ' || c);
end;
Создание функций
create or replace function my_fun(i in number) return number
as
    j number;
begin
    j := i+10;
    return j;    
end;
    
declare
    a number;
begin
    a := 12+my_fun(10);
    dbms_output.put_line('a = ' || a);
end;

create or replace function my_fun(i in number) return number
as
    j number;

    procedure p(n in out number)
    as
    begin
        n := n+i+j;        
    end;
begin
    j := 3;
    p(j);
    j := j+i+10;
    return j;    
end;
    
declare
    a number;
begin
    a := 12+my_fun(10);
    dbms_output.put_line('a = ' || a);
end;
Вызов процедур и функций
create or replace procedure my_p(s varchar2, i number := 10, j number := 20)
as
begin
    null;
end;

declare
    a number;
    b number;
    s varchar2(100);
begin
    my_p(s);
    my_p(s, a);
    my_p(s, a, b);
    my_p(s=>s, j=>b);
    my_p(s, j=>b);
end;
Передача параметров. Атрибут NOCOPY
create or replace procedure my_p2(s1 out varchar2, s2 in out nocopy varchar2)
as
begin
    s1 := 'aaaaaaaaaaaaaaaaaaaaaaaaaa';
    s2 := 'bbbbbbbbbbbbbbbbbbbbbbbbbb';
end;
Модули PL/SQL
create or replace package PackageTest
as
    type point is record
        (
            x number,
            y number
         );

    tpoint point;
    n number;
         
    procedure p(ii in number, ss out varchar2);
    function f(n in point) return number;
    function f(x in number, y in number) return number;
end PackageTest;

create or replace package body PackageTest
as
    type t1 is record
        (
            i number,
            j number
         );

    cnt number;
    trow1 test%rowtype;
     
    procedure p(ii in number, ss out varchar2)
    is
    begin
        select s into ss from test where i = ii;
    exception
        when no_data_found then
            ss := 'empty string';
    end;

    function f(n in point) return number
    as
    begin
        return n.x+n.y;
    end;

    function f(x in number, y in number) return number
    as
    begin
        return x+y;
    end;
begin
    trow1.i := 1;
    trow1.s := 'null string';
    n := 100;
    cnt := -10;
end PackageTest;

create or replace procedure PackageTest_test
as
-- error
--    d1 PackageTest.tl;
    s varchar2(100);
    n PackageTest.point;
begin
    n.x := 100;
    n.y := 200;
    PackageTest.p(1, s);
    dbms_output.put_line('s = ' || s);
    dbms_output.put_line('f((100, 100)) = ' || PackageTest.f(n));
    dbms_output.put_line('f(100, 100) = ' || PackageTest.f(n));
    dbms_output.put_line('n = ' || PackageTest.n);
-- error
--    dbms_output.put_line('cnt = ' || PackageTest.cnt);
end;
Просмотр объектов и компиляция
desc user_objects

select object_name, object_type, status from user_objects;
Перекомпиляция процедур и функций
create or replace function f(i in number)
return varchar2
as
begin
    return to_char(i+1);
end;

create or replace procedure p(ii in number, ss out varchar2)
as
begin
    select s into ss from test where i = ii;
exception
    when no_data_found then
        ss := 'no data';
end;

select substr(object_name, 1, 30), object_type, status from user_objects;

select substr(object_name, 1, 30), object_type, status from user_objects where object_type in 
             ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY') and (object_name like 'P%' or object_name like 'F%');

declare
    i number;
    s varchar2(100);
begin
    i := 10;
    s := f(i);
    dbms_output.put_line(s);

    i := 1;
    p(i, s);
    dbms_output.put_line(s);
end;

alter table test add h date;
alter table test drop column h;

alter procedure p compile;

select * from user_errors;

alter function f compile;

alter package packagetest compile specification;
alter package packagetest compile body;
alter package packagetest compile package;

select substr(object_name, 1, 30), object_type, status from user_objects where object_type in 
             ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY') and (object_name like 'P%' or object_name like 'F%');

select * from user_errors;
Скрипт для перекомпиляции процедур и функций

Необходимо поместить в файл, запуск @<file>.sql

----------------------------------------------------
set echo off
set linesize 1000
set pagesize 500
set heading off
set feedback off
spool c:\tmp\a.sql
select 'ALTER ' || object_type || ' ' || object_name || ' COMPILE;' from user_objects where object_type in 
        ('PROCEDURE', 'FUNCTION', 'PACKAGE');
select 'SELECT * FROM USER_ERRORS;' from dual;
spool off
@c:\tmp\a.sql
host del c:\tmp\a.sql
-----------------------------------------------------
  • Нет меток