Как правильно писать хранимые процедуры в SQL Server. Создание хранимых процедур в microsoft sql server Sql создать хранимую процедуру

  1. Изучить операторы описания хранимых процедур и принципы передачи их входных и выходных параметров.
  2. Изучить порядок создания и отладки хранимых процедур на сервере MS SQL Server 2000.
  3. Разработать пять базовых хранимых процедур для учебной базы данных «Библиотека».
  4. Подготовить отчет о проделанной работе в электронном виде.

1. Общие сведения о хранимых процедурах

Хранимая процедура (Stored Procedure) — это набор команд, хранимый на сервере и выполняемый как единое целое. Хранимые процедуры являются механизмом, с помощью которого можно создавать подпрограммы, работающие на сервере и управляемые его процессами. Подобные подпрограммы могут быть активизированы вызывающим их приложением. Кроме того, они могут быть вызваны правилами, поддерживающими целостность данных, или триггерами.

Хранимые процедуры могут возвращать значения. В процедуре можно выполнять сравнение вводимых пользователем значений с заранее установленной в системе информацией. Хранимые процедуры применяют в работе мощные аппаратные решения SQL Server. Они ориентированы на базы данных и тесно взаимодействуют с оптимизатором SQL Server. Это позволяет получить высокую производительность при обработке данных.

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

Хранимые процедуры бывают двух типов: обычные и расширенные . Обычные хранимые процедуры представляют собой набор команд на Transact-SQL, в то время как расширенные хранимые процедуры представлены в виде динамических библиотек (DLL). Такие процедуры, в отличие от обычных, имеют префикс xp_ . Сервер имеет стандартный набор расширенных процедур, но пользователи могут писать и свои процедуры на любом языке программирования. Главное при этом — использовать интерфейс программирования SQL Server Open Data Services API . Расширенные хранимые процедуры могут находиться только в базе данных Master .

Обычные хранимые процедуры также можно разделить на два типа: системные и пользовательские . Системные процедуры — это стандартные процедуры, служащие для работы сервера; пользовательские — любые процедуры, созданные пользователем.

1.1. Преимущества хранимых процедур

В самом общем случае хранимые процедуры обладают следующими преимуществами:

  • Высокая производительность. Является результатом расположения хранимых процедур на сервере. Сервер, как правило, — более мощная машина, поэтому время выполнения процедуры на сервере значительно меньше, чем на рабочей станции. Кроме того, информация из базы данных и хранимая процедура находятся в одной и той же системе, поэтому на передачу записей по сети время практически не затрачивается. Хранимые процедуры имеют непосредственный доступ к базам данных, что делает работу с информацией очень быстрой.
  • Преимущество разработки системы в архитектуре «клиент-сервер». Заключается в возможности раздельного создания программного обеспечения клиента и сервера. Это преимущество является ключевым при разработке, и благодаря ему можно значительно уменьшить время, необходимое для окончания проекта. Код, работающий на сервере, может разрабатываться отдельно от кода клиентской части. При этом компоненты серверной части могут совместно использоваться компонентами стороны клиента.
  • Уровень безопасности. Хранимые процедуры могут выступать в качестве инструмента улучшения безопасности. Можно создать хранимые процедуры, осуществляющие операции добавления, изменения, удаления и отображения списков, и, таким образом, получить контроль над каждым из аспектов доступа к информации.
  • Усиление правил сервера, работающих с данными. Это одна из самых важных причин применения интеллектуального ядра баз данных. Хранимые процедуры позволяют применять правила и другую логику, помогающую контролировать вводимую в систему информацию.

Хотя язык SQL определен как непроцедурный, в SQL Server применяются ключевые слова, связанные с управлением ходом выполнения процедур. Такие ключевые слова используются при создании процедур, которые можно сохранять для последующего выполнения. Хранимые процедуры могут быть применены вместо программ, созданных с помощью стандартных языков программирования (например, С или Visual Basic) и выполняющих операции в базе данных SQL Server.

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

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

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

Хранимые процедуры могут быть выполнены либо на локальной машине, либо на удаленной системе SQL Server. Это дает возможность активизировать процессы на других машинах и работать не только с локальными базами данных, но и с информацией на нескольких серверах.

Прикладные программы, написанные на одном из языков высокого уровня, таком как С или Visual Basic .NET, также могут вызывать хранимые процедуры, что обеспечивает оптимальное решение по распределению нагрузки между программным обеспечением клиентской части и SQL-сервера.

1.2. Создание хранимых процедур

Для создания хранимой процедуры применяется инструкция Create Procedure . Имя хранимой процедуры может быть длиной до 128 символов, включая символы # и ## . Синтаксис определения процедуры:

CREATE PROC имя_процедуры [; число]
[{@параметр тип_данных} [= значение_по_умолчанию] ] [,...n]

AS
<Инструкции_SQL>

Рассмотрим параметры этой команды:

  • Имя_процедуры — имя процедуры; должно удовлетворять правилам для идентификаторов: его длина не может превышать 128 символов; для локальных временных процедур перед именем используется знак #, а для глобальных временных процедур — знаки ##;
  • Число — необязательное целое число, используемое для группировки нескольких процедур под одним именем;
  • @параметр тип_данных — список имен параметров процедуры с указанием соответствующего типа данных для каждого; таких параметров может быть до 2100. В качестве значения параметра разрешается передавать NULL . Могут использоваться все типы данных за исключением типов text , ntext и image . В качестве выходного параметра (ключевое слово OUTPUT или VARYING ) можно использовать тип данных Cursor . Параметры с типом данных Cursor могут быть только выходными параметрами;
  • VARYING — ключевое слово, определяющее, что в качестве выходного параметра используется результирующий набор (используется только для типа Cursor );
  • OUTPUT — говорит о том, что указанный параметр может быть использован как выходной;
  • значение_по_умолчанию — используется в случае, когда при вызове процедуры параметр пропущен; должно быть константой и может включать символы маски (% , _ , [ , ] , ^ ) и значение NULL ;
  • WITH RECOMPILE — ключевые слова, показывающие, что SQL Server не будет записывать план процедуры в кэш, а будет создавать его каждый раз при выполнении;
  • WITH ENCRYPTION — ключевые слова, показывающие, что SQL Server будет зашифровывать процедуру перед записью в системную таблицу Syscomments . Для того чтобы текст зашифрованных процедур было невозможно восстановить, необходимо после шифрования удалить соответствующие им кортежи из таблицы syscomments;
  • FOR REPLICATION — ключевые слова, показывающие, что эта процедура создается только для репликации. Эта опция несовместима с ключевыми словами WITH RECOMPILE ;
  • AS — начало определения текста процедуры;
  • <Инструкции_SQL> — набор допустимых инструкций SQL, ограниченный только максимальным размером хранимой процедуры — 128 Кб. Недопустимыми являются следующие операторы: ALTER DATABASE , ALTER PROCEDURE , ALTER TABLE , CREATE DEFAULT , CREATE PROCEDURE , ALTER TRIGGER , ALTER VIEW , CREATE DATABASE , CREATE RULE , CREATE SCHEMA , CREATE TRIGGER , CREATE VIEW , DISK INIT , DISK RESIZE , DROP DATABASE , DROP DEFAULT , DROP PROCEDURE , DROP RULE , DROP TRIGGER , DROP VIEW , RESOTRE DATABASE , RESTORE LOG , RECONFIGURE , UPDATE STATISTICS .

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

CREATE Procedure Count_Ex1
-- процедура подсчета количества экземпляров книг,
-- находящихся в настоящий момент в библиотеке,
-- а не на руках у читателей
As
-- зададим временную локальную переменную
Declare @N int
Select @N = count(*) from Exemplar Where Yes_No = "1"
Select @N
GO

Поскольку хранимая процедура является полноценным компонентом базы данных, то, как вы уже поняли, создать новую процедуру можно только для текущей базы данных. При работе в SQL Server Query Analyzer установление текущей базы данных выполняется с помощью оператора Use , за которым следует имя базы данных, где должна быть создана хранимая процедура. Выбрать текущую базу данных можно также с помощью раскрывающегося списка.

После создания в системе хранимой процедуры SQL Server компилирует ее и проверяет выполняемые подпрограммы. При возникновении каких-либо проблем процедура отвергается. Перед повторной трансляцией ошибки должны быть устранены.

В SQL Server 2000 используется отложенное распознавание имен (delayed name resolution), поэтому если хранимая процедура содержит обращение к другой, еще не реализованной процедуре, то выводится предупреждение, но вызов несуществующей процедуры сохраняется.

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

Создать хранимую процедуру можно также с помощью SQL Server Enterprise Manager:

Для того чтобы проверить работоспособность созданной хранимой процедуры, необходимо перейти в Query Analyzer и запустить процедуру на исполнение оператором EXEC <имя процедуры> . Результаты запуска созданной нами процедуры представлены на рис. 4.

Рис. 4. Запуск хранимой процедуры в Query Analyzer

Рис. 5. Результат выполнения процедуры без оператора вывода на экран

1.3. Параметры хранимых процедур

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

  • Для процедуры можно определить один или несколько параметров.
  • Параметры используются в качестве именованных мест хранения данных, точно так же, как переменные в языках программирования, таких как С, Visual Basic .NET.
  • Имя параметра обязательно предваряется символом @ .
  • Имена параметров являются локальными в той процедуре, где они определены.
  • Параметры служат для передачи информации процедуре при ее выполнении. Они помешаются в командной строке после имени процедуры.
  • В случае если процедура имеет несколько параметров, они разделяются запятыми.
  • Для определения типа информации, передаваемой в качестве параметра, применяют системные или пользовательские типы данных.

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

Create Procedure Count_Ex(@ISBN varchar(14))
As
Declare @N int
Select @N
GO

При запуске этой процедуры на исполнение мы должны передать ей значение входного параметра (рис. 6).

Рис. 6. Запуск процедуры с передачей параметра

Для создания нескольких версий одной и той же процедуры, имеющих одинаковое имя, следует после основного имени поставить точку с запятой и целое число. Как это сделать, показано в следующем примере, где описано создание двух процедур с одним и тем же именем, но с разными номерами версий (1 и 2). Номер служит для контроля выполняемой версии этой процедуры. Если номер версии не указан, выполняется первая версия процедуры. Эта опция не показана в предыдущем примере, но, тем не менее, она доступна для вашего приложения.

Для вывода сообщения, идентифицирующего версию, обе процедуры применяют инструкцию print . Первая версия считает количество свободных экземпляров, а вторая — количество экземпляров на руках для данной книги.

Текст обеих версий процедур приведен ниже:

CREATE Procedure Count_Ex_all; 1
(@ISBN varchar(14))
-- процедура подсчета свободных экземпляров заданной книги
As
Declare @N int
Select @N = count(*) from Exemplar Where ISBN = @ISBN and Yes_No = "1"
Select @N
--
GO
--
CREATE Procedure Count_Ex_all; 2
(@ISBN varchar(14))
-- процедура подсчета свободных экземпляров заданной книги
As
Declare @N1 int
Select @N1 = count(*) from Exemplar Where ISBN = @ISBN and Yes_No = "0"
Select @N1
GO

Результаты выполнения процедуры с разными версиями приведены на рис. 7.

Рис. 7. Результаты запуска разных версий одной и той же хранимой процедуры

При написании нескольких версий необходимо помнить следующие ограничения: так как все версии процедуры компилируются вместе, то все локальные переменные считаются общими. Поэтому, если это требуется по алгоритму обработки, необходимо использовать разные имена внутренних переменных, что мы и сделали, назвав во второй процедуре переменную @N именем @N1 .

Написанные нами процедуры не возвращают ни одного параметра, они просто выводят на экран полученное число. Однако чаще всего нам требуется получить параметр для дальнейшей обработки. Существует несколько способов возврата параметров из хранимой процедуры. Самый простой — это воспользоваться оператором возврата значений RETURN . Этот оператор позволят вернуть одно числовое значение. Но мы должны указать имя переменной или выражение, которое присваивается возвращаемому параметру. Ниже перечислены значения, возвращаемые оператором RETURN , зарезервированные системой:

Код Значение
0 Все нормально
–1 Объект не найден
–2 Ошибка типа данных
–3 Процесс стал жертвой «дедлока»
–4 Ошибка доступа
–5 Синтаксическая ошибка
–6 Некоторая ошибка
–7 Ошибка с ресурсами (нет места)
–8 Произошла исправимая внутренняя ошибка
–9 Системный лимит исчерпан
–10 Неисправимое нарушение внутренней целостности
–11 То же самое
–12 Разрушение таблицы или индекса
–13 Разрушение базы данных
–14 Ошибка оборудования

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

Например, мы можем изменить текст ранее написанной хранимой процедуры Count_ex следующим образом:

Create Procedure Count_Ex2(@ISBN varchar(14))
As
Declare @N int
Select @N = count(*) from Exemplar
Where ISBN = @ISBN and YES_NO = "1"
-- возвращаем значение переменной @N,
-- если значение переменной не определено, возвращаем 0
Return Coalesce(@N, 0)
GO

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

Exec <переменная> = <имя_процедуры> <значение_входных_параметров>

Пример вызова нашей процедуры приведен на рис. 8.

Рис. 8. Передача возвращаемого значения хранимой процедуры локальной переменной

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

Значение по умолчанию задается через знак равенства после описания входного параметра и его типа. Рассмотрим хранимую процедуру, которая считает количество экземпляров книг заданного года выпуска. Год выпуска по умолчанию — 2006.

CREATE PROCEDURE ex_books_now(@year int = 2006)
-- подсчет количества экземпляров книг заданного года выпуска
AS
Declare @N_books int
select @N_books = count(*) from books, exemplar
where Books.ISBN = exemplar.ISBN and YEARIZD = @year
return coalesce(@N_books, 0)
GO

На рис. 9 приведен пример вызова данной процедуры с указанием входного параметра и без него.

Рис. 9. Вызов хранимой процедуры с параметром и без параметра

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

Отметим, что определение в процедуре параметра как выходного не обязывает вас использовать его как таковой. То есть если указать в качестве фактического параметра константу, то ошибки не произойдет и она будет использована как обычный входной параметр.

Для указания того, что параметр является выходным, используется инструкция OUTPUT . Данное ключевое слово записывается после описания параметра. При описании параметров хранимых процедур желательно задавать значения выходных параметров после входных.

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

CREATE Procedure Count_books_all
(@ISBN varchar(14), @all int output, @free int output)
-- процедура подсчета общего количества земпляров заданной книги
-- и количества свободных экземпляров
As
-- подсчет общего количесва экземпляров
Select @all = count(*) from Exemplar Where ISBN = @ISBN
Select @free = count(*) from Exemplar Where ISBN = @ISBN and Yes_No = "1"
GO

Пример выполнения данной процедуры приведен на рис. 10.

Рис. 10. Тестирование хранимой процедуры с выходными параметрами

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

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

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

Текст процедуры выглядит следующим образом:

CREATE PROCEDURE GET3TITLES
(@MYCURSOR CURSOR VARYING OUTPUT)
-- процедура печати названий книг с курсором
AS
-- определяем локальную переменную типа Cursor в процедуре
SET @MYCURSOR = CURSOR
FOR SELECT DISTINCT TITLE
FROM BOOKS
-- открываем курсор
OPEN @MYCURSOR
-- описываем внутренние локальные переменные
DECLARE @TITLE VARCHAR(80), @CNT INT
--- устанавливаем начальное состояние счетчика книг
SET @CNT = 0
-- переходим на первую строку курсора
-- пока есть строки курсора,
-- то есть пока переход на новую строку корректен
WHILE (@@FETCH_STATUS = 0) AND (@CNT <= 2) BEGIN
PRINT @TITLE
FETCH NEXT FROM @MYCURSOR INTO @TITLE
-- изменяем состояние счетчика книг
SET @CNT = @CNT + 1
END
IF @CNT = 0 PRINT "НЕТ ПОДХОДЯЩИХ КНИГ"
GO

Пример вызова данной хранимой процедуры приведен на рис. 11.

В вызывающей процедуре курсор должен быть описан как локальная переменная. Потом мы вызвали нашу процедуру и передали ей имя локальной переменной типа Cursor . Процедура начала работать и вывела нам на экран первые три названия, а потом передала управление вызывающей процедуре, и та продолжила обработку курсора. Для этого она организовала цикл типа While по глобальной переменной @@FETCH_STATUS , которая отслеживает состояние курсора, и далее в цикле вывела все остальные строки курсора.

В окне вывода мы видим увеличенный интервал между первыми тремя строками и последующими названиями. Этот интервал как раз и показывает, что управление передано внешней программе.

Заметьте, что переменная @TITLE , являясь локальной для процедуры, будет уничтожена после завершения ее работы, поэтому в вызывающем процедуру блоке она объявляется еще раз. Создание и открытие курсора в данном примере происходит в процедуре, а закрытие, уничтожение и дополнительная обработка выполняются в блоке команд, в котором вызывается процедура.

Проще всего посмотреть текст процедуры, изменить или удалить ее с помощью графического интерфейса Enterprise Manager. Но можно это сделать и при помощи специальных системных хранимых процедур Transact-SQL. В Transact-SQL просмотр определения процедуры выполняется с помощью системной процедуры sp_helptext , а системная процедура sp_help позволяет вывести контрольную информацию о процедуре. Системные процедуры sp_helptext и sp_help используются и для просмотра таких объектов баз данных, как таблицы, правила и установки по умолчанию.

Информация обо всех версиях одной процедуры, независимо от номера, выводится сразу. Удаление разных версий одной хранимой процедуры также происходит одновременно. В следующем примере показано, как выводятся определения версий 1 и 2 процедуры Count_Ex_all , когда ее имя указано в качестве параметра системной процедуры sp_helptext (рис. 12).

Рис. 12. Просмотр текста хранимой процедуры с использованием системной хранимой процедуры

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

Name
Owner
Type
Created_datetime
Count_books_all
dbo
stored procedure
2006-12-06 23:15:01.217
Parameter_name
Type
Length Prec
Scale Param_order Collation
@ISBN
varchar
14 14
NULL 1 Cyrillic_General_CI_AS
@all
int
4 10
0 2 NULL
@free
int
4 10
0 3 NULL

Попробуйте самостоятельно расшифровать эти параметры. О чем они говорят?

1.4. Компиляция хранимой процедуры

Преимущество применения хранимых процедур для выполнения набора инструкций Transact-SQL состоит в том, что они компилируются при первом выполнении. В процессе компиляции инструкции Transact-SQL конвертируются из их первоначального символьного представления в исполняемую форму. Любые объекты, к которым происходит обращение в процедуре, также конвертируются в альтернативное представление. Например, имена таблиц конвертируются в идентификаторы объектов, а имена столбцов — в идентификаторы столбцов.

План выполнения создается точно так же, как и для выполнения одной инструкции Transact-SQL. Этот план содержит, например, индексы, применяемые для считывания строк из таблиц, к которым обращается процедура. План выполнения процедуры сохраняется в кэше и используется при каждом ее вызове.

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

1.5. Автоматическая повторная компиляция

Обычно план выполнения находится в кэше процедур. Это позволяет увеличить производительность при его выполнении. Однако при некоторых обстоятельствах процедура автоматически перекомпилируется.

  • Процедура всегда перекомпилируется при начале работы SQL Server. Обычно это происходит после перезагрузки операционной системы и при первом выполнении процедуры после создания.
  • План выполнения процедуры всегда автоматически перекомпилируется, если удаляется индекс таблицы, к которому обращается процедура. Поскольку текущий план обращается для считывания строк таблицы к индексу, которого уже не существует, следует создать новый план выполнения. Запросы процедуры будут выполняться только в том случае, если он будет обновлен.
  • Компиляция плана выполнения происходит также в том случае, если с этим планом, находящимся в кэше, в данный момент работает другой пользователь. Для второго пользователя создается индивидуальная копия плана выполнения. Если бы первая копия плана не была занята, не понадобилось бы создания второй копии. Когда пользователь завершает выполнение процедуры, план выполнения доступен в кэше другому пользователю, имеющему соответствующее разрешение доступа.
  • Процедура автоматически перекомпилируется, если она удаляется и заново создается. Так как новая процедура может отличаться от старой версии, все копии плана выполнения в кэше удаляются, и план компилируется заново.

SQL Server стремится оптимизировать хранимые процедуры путем кэширования наиболее интенсивно используемых процедур. Поэтому старый план выполнения, загруженный в кэш, может быть использован вместо нового плана. Для предотвращения этой проблемы следует удалить и заново создать хранимую процедуру или остановить и заново активизировать SQL Server. Это очистит кэш процедуры и исключит вероятность работы со старым планом выполнения.

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

Если повторная компиляция процедур не производится автоматически, ее можно выполнить принудительно. Например, если обновлены статистики, применяющиеся для определения возможности использования индекса в данном запросе, или если создан новый индекс, должна быть произведена принудительная перекомпиляция. Для выполнения принудительной повторной компиляции в инструкции EXECUTE применяется предложение WITH RECOMPILE :

EXECUTE имя_процедуры;
AS
<инструкции Transact-SQL>
WITH RECOMPILE

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

Замечание: Иногда бывает сложно определить, надо ли использовать опцию WITH RECOMPILE при создании процедуры или нет. Если есть сомнения, лучше не применять эту опцию, так как повторная компиляция процедуры при каждом выполнении приведет к потере очень ценного времени центрального процессора. Если в будущем вам понадобится повторная компиляция при выполнении хранимой процедуры, ее можно будет произвести, добавив предложение WITH RECOMPILE к инструкции EXECUTE .

Нельзя применять опцию WITH RECOMPILE в инструкции CREATE PROCEDURE , содержащей опцию FOR REPLICATION . Эту опцию применяют для создания процедуры, которая выполняется в процессе репликации.

1.6. Вложенность хранимых процедур

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

2. Функции, определяемые пользователем (UDF)

В MS SQL SERVER 2000 существует множество заранее определенных функций, позволяющих выполнять разнообразные действия. Однако всегда может возникнуть необходимость использовать какие-то специфичные функции. Для этого, начиная с версии 8.0 (2000), появилась возможность описывать пользовательские функции (User Defined Functions, UDF) и хранить их в виде полноценного объекта базы данных, наравне с хранимыми процедурами, представлениями и т. д.

Удобство применения функций, определяемых пользователем, очевидно. В отличие от хранимых процедур, функции можно встраивать непосредственно в оператор SELECT , причем использовать их как для получения конкретных значений (в разделе SELECT ), так и в качестве источника данных (в разделе FROM ).

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

Функции, определяемые пользователем, могут быть трех видов: скалярные функции , inline-функции и многооператорные функции, возвращающие табличный результат . Рассмотрим все эти типы функций подробнее.

2.1. Скалярные функции

Скалярные функции возвращают один скалярный результат. Этот результат может быть любого описанного выше типа, за исключением типов text , ntext , image и timestamp . Это наиболее простой вид функции. Ее синтаксис имеет следующий вид:


RETURNS скалярный_тип_данных

BEGIN
тело_функции
RETURN скалярное_выражение
END

  • Параметр ENCRYPTION уже был описан в разделе, посвященном хранимым процедурам;
  • SCHEMABINDING — привязывает функцию к схеме. Это означает, что нельзя будет удалить таблицы или представления, на основе которых строится функция, без удаления или изменения самой функции. Нельзя также изменить структуру этих таблиц, если изменяемая часть используется функцией. Таким образом, эта опция позволяет исключить ситуации, когда функция использует какие-либо таблицы или представления, а кто-то, не зная об этом, удалил или изменил их;
  • RETURNS скалярный_тип_данных — описывает тип данных, который возвращает функция;
  • скалярное_выражение — выражение, которое непосредственно возвращает результат выполнения функции. Оно должно иметь тот же тип, что и тот, что описан после RETURNS;
  • тело_функции — набор инструкций на Transact-SQL.

Рассмотрим примеры использования скалярных функций.

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

Пусть функция выглядит следующим образом:

CREATE FUNCTION min_num(@a INT, @b INT)
RETURNS INT
BEGIN
DECLARE @c INT
IF @a < @b SET @c = @a
ELSE SET @c = @b
RETURN @c
END

Выполним теперь эту функцию:

SELECT dbo.min_num(4, 7)

В результате мы получим значение 4.

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

SELECT min_lvl, max_lvl, min_num(min_lvl, max_lvl)
FROM Jobs

Создадим функцию, которая будет получать на вход параметр типа datetime и возвращать дату и время, соответствующие началу указанного дня. Например, если входной параметр — 20.09.03 13:31, то результатом будет 20.09.03 00:00.

CREATE FUNCTION dbo.daybegin(@dat DATETIME)
RETURNS smalldatetime AS
BEGIN
RETURN CONVERT(datetime, FLOOR(convert(FLOAT, @dat)))
END

Здесь функция CONVERT осуществляет преобразование типов. Сначала тип даты-времени приводится к типу FLOAT . При таком приведении целая часть — это число дней, считая с 1 января 1900 года, а дробная — время. Далее происходит округление до меньшего целого с помощью функции FLOOR и приведение к типу даты-времени.

Проверим действие функции:

SELECT dbo.daybegin(GETDATE())

Здесь GETDATE() — функция, возвращающая текущую дату и время.

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

Создадим функцию, которая будет принимать в качестве параметров две даты: начало и окончание временного интервала — и рассчитывать суммарную выручку от продаж за этот интервал. Дата продажи и количество будут браться из таблицы Sales , а цены на продаваемые издания — в таблице Titles .

CREATE FUNCTION dbo.SumSales(@datebegin DATETIME, @dateend DATETIME)
RETURNS Money
AS
BEGIN
DECLARE @Sum Money
SELECT @Sum = sum(t.price * s.qty)

RETURN @Sum
END

2.2. Inline-функции

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

Еще более полезны функции такого типа в тех случаях, когда требуется, чтобы возвращаемая таблица зависела от входных параметров. Как известно, представления не могут иметь параметров, поэтому проблему такого рода могут решить только inline-функции.

Особенностью inline-фукций является то, что они могут содержать только один запрос в своем теле. Таким образом, функции этого типа очень напоминают представления, но дополнительно могут иметь входные параметры. Синтаксис inline-функции:

CREATE FUNCTION [владелец.]имя_функции
([{@имя_параметра скалярный_тип_данных [= значение_по_умолчанию]} [,… n]])
RETURNS TABLE

RETURN [(<запрос>)]

В определении функции указано, что она будет возвращать таблицу; <запрос> — это тот запрос, результат выполнения которого будет результатом работы функции.

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

CREATE FUNCTION Sales_Period (@datebegin DATETIME, @dateend DATETIME)
RETURNS TABLE
WITH ENCRYPTION
AS
RETURN (
SELECT t.title, t.price, s.qty, ord_date, t.price * s.qty as stoim
FROM Titles t JOIN Sales s ON t.title_Id = s.Title_ID
WHERE ord_date BETWEEN @datebegin and @dateend
)

Теперь вызовем эту функцию. Как уже говорилось, вызвать ее можно только в разделе FROM оператора SELECT :

SELECT * FROM Sales_Period("01.09.94", "13.09.94")

2.3. Многооператорные функции, возвращающие табличный результат

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

CREATE FUNCTION [владелец.]имя_функции
([{@имя_параметра скалярный_тип_данных [= значение_по_умолчанию]} [,... n]])
RETURNS @имя_переменной_результата TABLE
<описание_таблицы>

BEGIN
<тело_функции>
RETURN
END

  • TABLE <описание_таблицы> — описывает структуру возвращаемой таблицы;
  • <описание_таблицы> — содержит перечисление столбцов и ограничений.

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

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

Рис. 13. Структура базы данных для описания иерархии файлов и каталогов

Теперь напишем функцию, которая будет принимать на входе идентификатор каталога и выводить все файлы, которые хранятся в нем и во всех каталогах вниз по иерархии. Например, если в каталоге Институт созданы каталоги Факультет1 , Факультет2 и т. д., в них есть каталоги кафедр, и в каждом из каталогов есть файлы, то при указании в качестве параметра нашей функции идентификатора каталога Институт должен быть выведен список всех файлов для всех этих каталогов. Для каждого файла должно выводиться имя, размер и дата создания.

Решить задачу с помощью inline-функции нельзя, поскольку SQL не предназначен для выполнения иерархических запросов, так что одним SQL-запросом здесь не обойтись. Скалярная функция тоже не может быть применена, поскольку результат должен быть таблицей. Тут нам на помощь и придет многооператорная функция, возвращающая таблицу:

CREATE FUNCTION dbo.GetFiles(@Folder_ID int)
RETURNS @files TABLE(Name VARCHAR(100), Date_Create DATETIME, FileSize INT) AS
BEGIN
DECLARE @tmp TABLE(Folder_Id int)
DECLARE @Cnt INT
INSERT INTO @tmp values(@Folder_ID)
SET @Cnt = 1
WHILE @Cnt <> 0 BEGIN
INSERT INTO @tmp SELECT Folder_Id
FROM Folders f JOIN @tmp t ON f.parent=t.Folder_ID
WHERE F.id NOT IN(SELECT Folder_ID FROM @tmp)
SET @Cnt = @@ROWCOUNT
END
INSERT INTO @Files(Name, Date_Create, FileSize)
SELECT F.Name, F.Date_Create, F.FileSize
FROM Files f JOIN Folders Fl on f.Folder_id = Fl.id
JOIN @tmp t on Fl.id = t.Folder_Id
RETURN
END

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

Задания для самостоятельной работы

Необходимо создать и отладить пять хранимых процедур из следующего обязательного списка:

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

Процедура 2. Подсчет количества свободных экземпляров заданной книги.

Процедура 3. Проверка существования читателя с заданными фамилией и датой рождения.

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

Процедура 5. Подсчет штрафа в денежном выражении для читателей-должников.

Краткое описание процедур

Процедура 1. Увеличение срока сдачи книг

Для каждой записи в таблице Exemplar проверяется, попадает ли дата сдачи книги в заданный временной интервал. Если попадает, то дата возврата книги увеличивается на неделю. При выполнении процедуры необходимо использовать функцию по работе с датами:

DateAdd(day, <число добавляемых дней>, <начальная дата>)

Процедура 2. Подсчет количества свободных экземпляров заданной книги

Входным параметром процедуры является ISBN — уникальный шифр книги. Процедура возвращает 0 (ноль), если все экземпляры данной книги находятся на руках у читателей. Процедура возвращает значение N , равное числу экземпляров книги, которые в данный момент находятся на руках у читателей.

Если книги с заданным ISBN нет в библиотеке, то процедура возвращает –100 (минус сто).

Процедура 3. Проверка существования читателя с заданными фамилией и датой рождения

Процедура возвращает номер читательского билета, если читатель с такими данными существует, и 0 (ноль) в противном случае.

При сравнении даты рождения необходимо использовать функцию преобразования Convert() для преобразования даты рождения — символьной переменной типа Varchar(8) , используемой в качестве входного параметра процедуры, в данные типа datatime , которые используются в таблице Readers . В противном случае операция сравнения при поиске данного читателя не сработает.

Процедура 4. Ввод нового читателя

Процедура имеет пять входных и три выходных параметра.

Входные параметры:

  • Полное имя с инициалами;
  • Адрес;
  • Дата рождения;
  • Телефон домашний;
  • Телефон рабочий.

Выходные параметры:

  • Номер читательского билета;
  • Признак того, был ли читатель ранее записан в библиотеке (0 — не был, 1 — был);
  • Количество книг, которое числится за читателем.
Процедура 5. Подсчет штрафа в денежном выражении для читателей-должников

Процедура работает с курсором, который содержит перечень номеров читательских билетов всех должников. В процессе работы должна быть создана глобальная временная таблица ##DOLG , в которую для каждого должника будет занесен его суммарный долг в денежном выражении за все книги, которые он продержал дольше срок возврата. Денежная компенсация исчисляется в 0,5 % от цены за книгу за день задержки.

Порядок выполнения работы

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

Дополнительные задания

Приведенные ниже дополнительные хранимые процедуры предназначены для индивидуальных заданий.

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

Процедура 7. Ввод новой книги с указанием количества ее экземпляров. При вводе экземпляров новой книги не забудьте ввести их корректные инвентарные номера. Подумайте, как это можно сделать. Напоминаю, что у вас есть функции Max и Min , которые позволяют найти максимальное или минимальное значение любого числового атрибута средствами запроса Select .

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

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

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

Процедура 11. Вывод списка книг с указанием количества экземпляров данной книги в библиотеке и количества свободных экземпляров на текущий момент.

Версия для печати

Хранимая процедура - это специальный тип пакета инструкций Transact-SQL, созданный, используя язык SQL и процедурные расширения. Основное различие между пакетом и хранимой процедурой состоит в том, что последняя сохраняется в виде объекта базы данных. Иными словами, хранимые процедуры сохраняются на стороне сервера, чтобы улучшить производительность и постоянство выполнения повторяемых задач.

Компонент Database Engine поддерживает хранимые процедуры и системные процедуры. Хранимые процедуры создаются таким же образом, как и все другие объекты баз данных, т.е. при помощи языка DDL. Системные процедуры предоставляются компонентом Database Engine и могут применяться для доступа к информации в системном каталоге и ее модификации.

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

Хранимая процедура предварительно компилируется перед тем, как она сохраняется в виде объекта в базе данных. Предварительно компилированная форма процедуры сохраняется в базе данных и используется при каждом ее вызове. Это свойство хранимых процедур предоставляет важную выгоду, заключающуюся в устранении (почти во всех случаях) повторных компиляций процедуры и получении соответствующего улучшения производительности. Это свойство хранимых процедур также оказывает положительный эффект на объем данных, участвующих в обмене между системой баз данных и приложениями. В частности, для вызова хранимой процедуры объемом в несколько тысяч байтов может потребоваться меньше, чем 50 байт. Когда множественные пользователи выполняют повторяющиеся задачи с применением хранимых процедур, накопительный эффект такой экономии может быть довольно значительным.

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

    для создания журнала логов о действиях с таблицами баз данных.

Использование хранимых процедур предоставляет возможность управления безопасностью на уровне, значительно превышающем уровень безопасности, предоставляемый использованием инструкций GRANT и REVOKE, с помощью которых пользователям предоставляются разные привилегии доступа. Это возможно вследствие того, что авторизация на выполнение хранимой процедуры не зависит от авторизации на модифицирование объектов, содержащихся в данной хранимой процедуре, как это описано в следующем разделе.

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

Создание и исполнение хранимых процедур

Хранимые процедуры создаются посредством инструкции CREATE PROCEDURE , которая имеет следующий синтаксис:

CREATE PROC proc_name [({@param1} type1 [ VARYING] [= default1] )] {, …} AS batch | EXTERNAL NAME method_name Соглашения по синтаксису

Параметр schema_name определяет имя схемы, которая назначается владельцем созданной хранимой процедуры. Параметр proc_name определяет имя хранимой процедуры. Параметр @param1 является параметром процедуры (формальным аргументом), чей тип данных определяется параметром type1. Параметры процедуры являются локальными в пределах процедуры, подобно тому, как локальные переменные являются локальными в пределах пакета. Параметры процедуры - это значения, которые передаются вызывающим объектом процедуре для использования в ней. Параметр default1 определяет значение по умолчанию для соответствующего параметра процедуры. (Значением по умолчанию также может быть NULL.)

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

Как уже упоминалось ранее, предварительно компилированная форма процедуры сохраняется в базе данных и используется при каждом ее вызове. Если же по каким-либо причинам хранимую процедуру требуется компилировать при каждом ее вызове, при объявлении процедуры используется опция WITH RECOMPILE . Использование опции WITH RECOMPILE сводит на нет одно из наиболее важных преимуществ хранимых процедур: улучшение производительности благодаря одной компиляции. Поэтому опцию WITH RECOMPILE следует использовать только при частых изменениях используемых хранимой процедурой объектов базы данных.

Предложение EXECUTE AS определяет контекст безопасности, в котором должна исполняться хранимая процедура после ее вызова. Задавая этот контекст, с помощью Database Engine можно управлять выбором учетных записей пользователей для проверки полномочий доступа к объектам, на которые ссылается данная хранимая процедура.

По умолчанию использовать инструкцию CREATE PROCEDURE могут только члены предопределенной роли сервера sysadmin и предопределенной роли базы данных db_owner или db_ddladmin. Но члены этих ролей могут присваивать это право другим пользователям с помощью инструкции GRANT CREATE PROCEDURE .

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

USE SampleDb; GO CREATE PROCEDURE IncreaseBudget (@percent INT=5) AS UPDATE Project SET Budget = Budget + Budget * @percent/100;

Как говорилось ранее, для разделения двух пакетов используется инструкция GO . Инструкцию CREATE PROCEDURE нельзя объединять с другими инструкциями Transact-SQL в одном пакете. Хранимая процедура IncreaseBudget увеличивает бюджеты для всех проектов на определенное число процентов, определяемое посредством параметра @percent. В процедуре также определяется значение числа процентов по умолчанию (5), которое применяется, если во время выполнения процедуры этот аргумент отсутствует.

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

В отличие от основных хранимых процедур, которые всегда сохраняются в текущей базе данных, возможно создание временных хранимых процедур, которые всегда помещаются во временную системную базу данных tempdb. Одним из поводов для создания временных хранимых процедур может быть желание избежать повторяющегося исполнения определенной группы инструкций при соединении с базой данных. Можно создавать локальные или глобальные временные процедуры. Для этого имя локальной процедуры задается с одинарным символом # (#proc_name), а имя глобальной процедуры - с двойным (##proc_name).

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

Жизненный цикл хранимой процедуры состоит из двух этапов: ее создания и ее выполнения. Каждая процедура создается один раз, а выполняется многократно. Хранимая процедура выполняется посредством инструкции EXECUTE пользователем, который является владельцем процедуры или обладает правом EXECUTE для доступа к этой процедуре. Инструкция EXECUTE имеет следующий синтаксис:

[] [@return_status =] {proc_name | @proc_name_var} {[[@parameter1 =] value | [@parameter1=] @variable ] | DEFAULT}.. Соглашения по синтаксису

За исключением параметра return_status, все параметры инструкции EXECUTE имеют такое же логическое значение, как и одноименные параметры инструкции CREATE PROCEDURE. Параметр return_status определяет целочисленную переменную, в которой сохраняется состояние возврата процедуры. Значение параметру можно присвоить, используя или константу (value), или локальную переменную (@variable). Порядок значений именованных параметров не важен, но значения неименованных параметров должны предоставляться в том порядке, в каком они определены в инструкции CREATE PROCEDURE.

Предложение DEFAULT предоставляет значения по умолчанию для параметра процедуры, которое было указано в определении процедуры. Когда процедура ожидает значение для параметра, для которого не было определено значение по умолчанию и отсутствует параметр, либо указано ключевое слово DEFAULT, то происходит ошибка.

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

USE SampleDb; EXECUTE IncreaseBudget 10;

Инструкция EXECUTE в этом примере выполняет хранимую процедуру IncreaseBudget, которая увеличивает бюджет всех проектов на 10%.

В примере ниже показано создание хранимой процедуры для обработки данных в таблицах Employee и Works_on:

Процедура ModifyEmpId в примере иллюстрирует использование хранимых процедур, как часть процесса обеспечения ссылочной целостности (в данном случае между таблицами Employee и Works_on). Подобную хранимую процедуру можно использовать внутри определения триггера, который собственно и обеспечивает ссылочную целостность.

В примере ниже показано использование в хранимой процедуре предложения OUTPUT:

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

DECLARE @quantityDeleteEmployee INT; EXECUTE DeleteEmployee @empId=18316, @ OUTPUT; PRINT N"Удалено сотрудников: " + convert(nvarchar(30), @quantityDeleteEmployee);

Эта процедура подсчитывает количество проектов, над которыми занят сотрудник с табельным номером @empId, и присваивает полученное значение параметру ©counter. После удаления всех строк для данного табельного номера из таблиц Employee и Works_on вычисленное значение присваивается переменной @quantityDeleteEmployee.

Значение параметра возвращается вызывающей процедуре только в том случае, если указана опция OUTPUT. В примере выше процедура DeleteEmployee передает вызывающей процедуре параметр @counter, следовательно, хранимая процедура возвращает значение системе. Поэтому параметр @counter необходимо указывать как в опции OUTPUT при объявлении процедуры, так и в инструкции EXECUTE при ее вызове.

Предложение WITH RESULTS SETS инструкции EXECUTE

В SQL Server 2012 для инструкции EXECUTE вводится предложение WITH RESULTS SETS , посредством которого при выполнении определенных условий можно изменять форму результирующего набора хранимой процедуры.

Следующие два примера помогут объяснить это предложение. Первый пример является вводным примером, который показывает, как может выглядеть результат, когда опущено предложение WITH RESULTS SETS:

Процедура EmployeesInDept - это простая процедура, которая отображает табельные номера и фамилии всех сотрудников, работающих в определенном отделе. Номер отдела является параметром процедуры, и его нужно указать при ее вызове. Выполнение этой процедуры выводит таблицу с двумя столбцами, заголовки которых совпадают с наименованиями соответствующих столбцов таблицы базы данных, т.е. Id и LastName. Чтобы изменить заголовки столбцов результата (а также их тип данных), в SQL Server 2012 применяется новое предложение WITH RESULTS SETS. Применение этого предложения показано в примере ниже:

USE SampleDb; EXEC EmployeesInDept "d1" WITH RESULT SETS (( INT NOT NULL, [Фамилия] CHAR(20) NOT NULL));

Результат выполнения хранимой процедуры, вызванной таким способом, будет следующим:

Как можно видеть, запуск хранимой процедуры с использованием предложения WITH RESULT SETS в инструкции EXECUTE позволяет изменить наименования и тип данных столбцов результирующего набора, выдаваемого данной процедурой. Таким образом, эта новая функциональность предоставляет большую гибкость в исполнении хранимых процедур и помещении их результатов в новую таблицу.

Изменение структуры хранимых процедур

Компонент Database Engine также поддерживает инструкцию ALTER PROCEDURE для модификации структуры хранимых процедур. Инструкция ALTER PROCEDURE обычно применяется для изменения инструкций Transact-SQL внутри процедуры. Все параметры инструкции ALTER PROCEDURE имеют такое же значение, как и одноименные параметры инструкции CREATE PROCEDURE. Основной целью использования этой инструкции является избежание переопределения существующих прав хранимой процедуры.

Компонент Database Engine поддерживает тип данных CURSOR . Этот тип данных используется для объявления курсоров в хранимых процедурах. Курсор - это конструкция программирования, применяемая для хранения результатов запроса (обычно набора строк) и для предоставления пользователям возможности отображать этот результат построчно.

Для удаления одной или группы хранимых процедур используется инструкция DROP PROCEDURE . Удалить хранимую процедуру может только ее владелец или члены предопределенных ролей db_owner и sysadmin.

Хранимые процедуры и среда CLR

SQL Server поддерживает общеязыковую среду выполнения CLR (Common Language Runtime), которая позволяет разрабатывать различные объекты баз данных (хранимые процедуры, определяемые пользователем функции, триггеры, определяемые пользователем статистические функции и пользовательские типы данных), применяя языки C# и Visual Basic. Среда CLR также позволяет выполнять эти объекты, используя систему общей среды выполнения.

Среда CLR разрешается и запрещается посредством опции clr_enabled системной процедуры sp_configure , которая запускается на выполнение инструкцией RECONFIGURE . В примере ниже показано, как можно с помощью системной процедуры sp_configure разрешить использование среды CLR:

USE SampleDb; EXEC sp_configure "clr_enabled",1 RECONFIGURE

Для создания, компилирования и сохранения процедуры с помощью среды CLR требуется выполнить следующую последовательность шагов в указанном порядке:

    Создать хранимую процедуру на языке C# или Visual Basic, а затем скомпилировать ее, используя соответствующий компилятор.

    Используя инструкцию CREATE ASSEMBLY , создать соответствующий выполняемый файл.

    Выполнить процедуру, используя инструкцию EXECUTE.

На рисунке ниже показана графическая схема ранее изложенных шагов. Далее приводится более подробное описание этого процесса.

Сначала создайте требуемую программу в какой-либо среде разработки, например Visual Studio. Скомпилируйте готовую программу в объектный код, используя компилятор C# или Visual Basic. Этот код сохраняется в файле динамической библиотеки (.dll), который служит источником для инструкции CREATE ASSEMBLY, создающей промежуточный выполняемый код. Далее выполните инструкцию CREATE PROCEDURE, чтобы сохранить выполняемый код в виде объекта базы данных. Наконец, запустите процедуру на выполнение, используя уже знакомую нам инструкцию EXECUTE.

В примере ниже показан исходный код хранимой процедуры на языке C#:

Using System.Data.SqlClient; using Microsoft.SqlServer.Server; public partial class StoredProcedures { public static int CountEmployees() { int rows; SqlConnection connection = new SqlConnection("Context Connection=true"); connection.Open(); SqlCommand cmd = connection.CreateCommand(); cmd.CommandText = "select count(*) as "Количество сотрудников" " + "from Employee"; rows = (int)cmd.ExecuteScalar(); connection.Close(); return rows; } }

В этой процедуре реализуется запрос для подсчета числа строк в таблице Employee. В директивах using в начале программы указываются пространства имен, требуемые для ее выполнения. Применение этих директив позволяет указывать в исходном коде имена классов без явного указания соответствующих пространств имен. Далее определяется класс StoredProcedures, для которого применяется атрибут SqlProcedure , который информирует компилятор о том, что этот класс является хранимой процедурой. Внутри кода класса определяется метод CountEmployees(). Соединение с системой баз данных устанавливается посредством экземпляра класса SqlConnection . Чтобы открыть соединение, применяется метод Open() этого экземпляра. А метод CreateCommand() позволяет обращаться к экземпляру класса SqlCommnd , которому передается нужная SQL-команда.

В следующем фрагменте кода:

Cmd.CommandText = "select count(*) as "Количество сотрудников" " + "from Employee";

используется инструкция SELECT для подсчета количества строк в таблице Employee и отображения результата. Текст команды указывается, присваивая свойству CommandText переменной cmd экземпляр, возвращаемый методом CreateCommand(). Далее вызывается метод ExecuteScalar() экземпляра SqlCommand. Этот метод возвращает скалярное значение, которое преобразовывается в целочисленный тип данных int и присваивается переменной rows.

Теперь вы можете скомпилировать этот код, используя среду Visual Studio. Я добавил этот класс в проект с именем CLRStoredProcedures, поэтому Visual Studio скомпилирует одноименную сборку с расширением *.dll. В примере ниже показан следующий шаг в создании хранимой процедуры: создание выполняемого кода. Прежде чем выполнять код в этом примере, необходимо узнать расположение скомпилированного dll-файла (обычно находится в папке Debug проекта).

USE SampleDb; GO CREATE ASSEMBLY CLRStoredProcedures FROM "D:\Projects\CLRStoredProcedures\bin\Debug\CLRStoredProcedures.dll" WITH PERMISSION_SET = SAFE

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

CREATE ASSEMBLY assembly_name [ AUTHORIZATION owner_name ] FROM {dll_file} Соглашения по синтаксису

В параметре assembly_name указывается имя сборки. В необязательном предложении AUTHORIZATION указывается имя роли в качестве владельца этой сборки. В предложении FROM указывается путь, где находится загружаемая сборка.

Предложение WITH PERMISSION_SET является очень важным предложением инструкции CREATE ASSEMBLY и всегда должно указываться. В нем определяется набор прав доступа, предоставляемых коду сборки. Набор прав SAFE является наиболее ограничивающим. Код сборки, имеющий эти права, не может обращаться к внешним системным ресурсам, таким как файлы. Набор прав EXTERNAL_ACCESS позволяет коду сборки обращаться к определенным внешним системным ресурсам, а набор прав UNSAFE предоставляет неограниченный доступ к ресурсам, как внутри, так и вне системы базы данных.

Чтобы сохранить информацию о коде сборке, пользователь должен иметь возможность выполнить инструкцию CREATE ASSEMBLY. Владельцем сборки является пользователь (или роль), исполняющий эту инструкцию. Владельцем сборки можно сделать другого пользователя, используя предложение AUTHORIZATION инструкции CREATE SCHEMA.

Компонент Database Engine также поддерживает инструкции ALTER ASSEMBLY и DROP ASSEMBLY. Инструкция ALTER ASSEMBLY используется для обновления сборки до последней версии. Эта инструкция также добавляет или удаляет файлы, связанные с соответствующей сборкой. Инструкция DROP ASSEMBLY удаляет указанную сборку и все связанные с ней файлы из текущей базы данных.

В примере ниже показано создание хранимой процедуры на основе управляемого кода, реализованного ранее:

USE SampleDb; GO CREATE PROCEDURE CountEmployees AS EXTERNAL NAME CLRStoredProcedures.StoredProcedures.CountEmployees

Инструкция CREATE PROCEDURE в примере отличается от такой же инструкции в примерах ранее тем, что она содержит параметр EXTERNAL NAME . Этот параметр указывает, что код создается средой CLR. Имя в этом предложении состоит из трех частей:

assembly_name.class_name.method_name

    assembly_name - указывает имя сборки;

    class_name - указывает имя общего класса;

    method_name - необязательная часть, указывает имя метода, который задается внутри класса.

Выполнение процедуры CountEmployees показано в примере ниже:

USE SampleDb; DECLARE @count INT EXECUTE @count = CountEmployees PRINT @count -- Вернет 7

Инструкция PRINT возвращает текущее количество строк в таблице Employee.

Хранимая процедура (англ. stored procedure) – это именованный программный объект БД. В SQL Server есть хранимые процедуры нескольких типов.

Системные хранимые процедуры (англ. system stored procedure) поставляются разработчиками СУБД и используются для выполнения действий с системным каталогом или получения системной информации. Их названия обычно начинаются с префикса "sp_". Запускаются хранимые процедуры всех типов с помощью команды EXECUTE, которую можно сократить до ЕХЕС. Например, хранимая процедура sp_helplogins, запущенная без параметров, формирует два отчета об именах учетных записей (англ. logins) и соответствующих им в каждой БД пользователях (англ. users).

EXEC sp_helplogins;

Чтобы дать представление о действиях, выполняемых с помощью системных хранимых процедур, в табл. 10.6 приведены некоторые примеры. Всего же системных хранимых процедур в SQL Server более тысячи.

Таблица 10.6

Примеры системных хранимых процедур SQL Server

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

Программируемые объекты SQL Server могут создаваться как с использованием средств Transact-SQL, так и с помощью сборок (англ. assembly) в среде CRL (Common Language Runtime) платформы Microsoft.Net Framework . В данном учебнике будет рассматриваться только первый способ.

Для создания хранимых процедур используется оператор CREATE PROCEDURE (можно сократить до PROC), формат которого приведен ниже:

CREATE {PROC I PROCEDURE) proc_name [ ; number ]

[{gparameter data_type }

[“default] |

[ WITH [ ,...n ] ]

[ FOR REPLICATION ]

AS {[ BEGIN ] sql_statement [;] [ ...n ] [ END ] }

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

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

EXECUTE AS определяет контекст безопасности, в котором должна быть выполнена процедура. Далее указывается одно из значений f CALLER | SELF | OWNER | " user_name"). CALLER является значением по умолчанию и означает, что код будет выполняться в контексте безопасности пользователя, вызывающего этот модуль. Соответственно, пользователь должен иметь разрешения не только на сам программируемый объект, но и на другие затрагиваемые им объекты БД. EXECUTE AS SELF означает использование контекста пользователя, создающего или изменяющего программируемый объект. OWNER указывает, что код будет выполняться в контексте текущего владельца процедуры. Если для нее не определен владелец, то подразумевается владелец схемы, к которой она относится. EXECUTE AS "user_name" позволяет явно указать имя пользователя (в одинарных кавычках).

Для процедуры могут указываться параметры. Это локальные переменные, используемые для передачи значений в процедуру. Если параметр объявлен с ключевым словом OUTPUT (или сокращенно OUT), он является выходным: заданное ему в процедуре значение после ее окончания может быть использовано вызвавшей процедуру программой. Ключевое слово READONLY означает, что значение параметра не может быть изменено внутри хранимой процедуры.

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

CREATE PROC surma (@а int, @b int=0,

©result int OUTPUT) AS

SET @result=0a+0b

Мы создали процедуру с тремя параметрами, причем у параметра @b значение по умолчанию =0, а параметр @result – выходной: через него возвращается значение в вызвавшую программу. Выполняемые действия достаточно просты – выходной параметр получает значение суммы двух входных.

При работе в SQL Server Management Studio созданную хранимую процедуру можно найти в разделе программируемых объектов БД (англ. Programmability) в подразделе для хранимых процедур (рис. 10.2).

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

Рис. 10.2.

DECLARE @с int;

EXEC summa 10,5,@c OUTPUT;

PRINT 0c; – будет выведено 15

DECLARE Gi int = 5;

– при вызове используем значение по умолчанию

EXEC summa Gi,DEFAULT , 0с OUTPUT;

PRINT 0c; – будет выведено 5

Рассмотрим теперь пример с анализом кода возврата, с которым заканчивается процедура. Пусть надо подсчитать, сколько в таблице Bookl книг, изданных в заданном диапазоне лет. При этом если начальный год оказался больше конечного, процедура возвращает "1" и подсчет не проводит, иначе – считаем количество книг и возвращаем 0:

CREATE PROC dbo.rownum (0FirsYear int, GLastYear int, 0result int OUTPUT) AS

IF 0FirsYear>0LastYear RETURN 1

SET @result= (SELECT COUNT(*) FROM dbo.Bookl

WHERE BETWEEN 0FirsYear AND 0LastYear) ;

Рассмотрим вариант вызова данной процедуры, в котором код возврата сохраняется в целочисленной переменной 0ret, после чего анализируется его значение (в данном случае это будет 1). Используемая в операторе PRINT функция CAST служит для преобразования значения целочисленной переменной Gres к строковому типу:

DECLARE 0ret int, Gres int

EXEC Gret = rownum 2004, 2002, Gres OUT;

IF 0ret=l PRINT "Начальный год больше конечного"

PRINT "Число книг "+ CAST(Gres as varchar(20))

Хранимые процедуры могут не только считывать данные из таблицы, но и изменять данные и даже создавать таблицы и ряд других объектов БД.

Однако создавать схемы, функции, триггеры, процедуры и представления из хранимой процедуры нельзя.

Следующий пример иллюстрирует как эти возможности, так и вопросы, связанные с областью видимости временных объектов. Приведенная ниже хранимая процедура проверяет наличие временной таблицы #ТаЬ2; если этой таблицы нет, то создает ее. После этого в таблицу #ТаЬ2 заносятся значения двух столбцов, и содержимое таблицы выводится оператором SELECT:

CREATE PROC My_Procl (@id int, @name varchar(30))

IF OBJECT_ID("tempdb.dbo.#Tab21) IS NULL

INSERT INTO dbo.#Tab2 (id, name)VALUES (0id,0name)

SELECT * FROM dbo. #Tab2 –№1

Перед первым вызовом хранимой процедуры создадим используемую в ней временную таблицу #ТаЬ2. Обратите внимание на оператор ЕХЕС. В предыдущих примерах параметры передавались в процедуру "по позиции", а в данном случае используется другой формат передачи параметров – "по имени", явно указывается имя параметра и его значение:

CREATE TABLE dbo.#Tab2 (id int, name varchar(30));

EXEC My_Procl 0name="lvan", 0id=2;

SELECT * FROM dbo.#Tab2; –№2

В приведенном примере оператор SELECT отработает дважды: первый раз – внутри процедуры, второй раз – из вызывающего фрагмента кода (отмечен комментарием "№ 2").

Перед вторым вызовом процедуры удалим временную таблицу #ТаЬ2. Тогда одноименная временная таблица будет создана из хранимой процедуры:

DROP TABLE dbo.#Tab2;

EXEC My_Procl 0name="Ivan", 0id=2;

SELECT * FROM dbo.#Tab2; –№2

В этом случае данные выведет только оператор SELECT, находящийся внутри процедуры (с комментарием "Ха 1"). Выполнение SELECT "№ 2" приведет к ошибке, так как созданная в хранимой процедуре временная таблица на момент возврата из процедуры будет уже удалена из базы tempdb.

Удалить хранимую процедуру можно с помощью оператора DROP PROCEDURE. Его формат представлен ниже. Одним оператором можно удалить несколько хранимых процедур, перечислив их через запятую:

DROP (PROC I PROCEDURE) { procedure } [

Например, удалим ранее созданную процедуру summa:

DROP PROC summa;

Внести изменения в существующую процедуру (а фактически – переопределить ее) можно с помощью оператора ALTER PROCEDURE (допу

стимо сокращение PROC). За исключением ключевого слова ALTER, формат оператора практически совпадает с форматом CREATE PROCEDURE. Например, изменим процедуру dbo. rownum, установив ей опцию выполнения в контексте безопасности владельца:

ALTER PROC dbo.rownum (SFirsYear int,

SLastYear int, Sresult int OUTPUT)

WITH EXECUTE AS Owner – устанавливаемая опция

IF 0FirsYear>0LastYear RETURN 1 ELSE BEGIN

SET 0result= (SELECT COUNT(*) FROM dbo.Bookl

WHERE BETWEEN SFirsYear AND SLastYear);

В некоторых случаях может возникнуть необходимость в динамическом формировании команды и выполнении ее на сервере БД. Эта задача также может решаться с помощью оператора ЕХЕС. В приведенном ниже примере выполняется выборка записей из таблицы Bookl по условию равенства атрибута Year значению, задаваемому с помощью переменной:

DECLARE 0у int = 2000;

EXEC ("SELECT * FROM dbo.Bookl WHERE = "+@y) ;

Выполнение динамически сформированных инструкций создает предпосылки для реализации компьютерных атак типа "SQL-инъекция" (англ. SQL injection). Суть атаки заключается в том, что нарушитель внедряет в динамически формируемый запрос собственный код на SQL. Обычно это происходит, когда подставляемые параметры берут из результатов ввода данных пользователем.

Несколько изменим предыдущий пример:

DECLARE 0у varchar(100);

SET 0у="2ООО"; – это мы получили от пользователя

Если предположить, что присваиваемое в операторе SET строковое значение мы получили от пользователя (неважно каким образом, например, через веб-приложение), то пример иллюстрирует "штатное" поведение нашего кода.

DECLARE 0у varchar(100);

SET 0у="2000; DELETE FROM dbo.Book2"; – инъекция

EXEC ("SELECT * FROM dbo.Book2 WHERE ="+0y);

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

EXECUTE sp_executesql

N"SELECT * FROM dbo.Bookl WHERE =0y",

Здесь явно указывается тип используемого в запросе параметра, и SQL Server при выполнении будет его контролировать. Буква "N" перед кавычками указывает, что это литерная константа в формате Unicode, как того требует процедура. Параметру можно присвоить не только постоянное значение, но и значение другой переменной.

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

Типы хранимых процедур

В SQL Server имеется несколько типов хранимых процедур .

  • Системные хранимые процедуры предназначены для выполнения различных административных действий. Практически все действия по администрированию сервера выполняются с их помощью. Можно сказать, что системные хранимые процедуры являются интерфейсом, обеспечивающим работу с системными таблицами, которая, в конечном счете, сводится к изменению, добавлению, удалению и выборке данных из системных таблиц как пользовательских, так и системных баз данных. Системные хранимые процедуры имеют префикс sp_ , хранятся в системной базе данных и могут быть вызваны в контексте любой другой базы данных.
  • Пользовательские хранимые процедуры реализуют те или иные действия. Хранимые процедуры – полноценный объект базы данных. Вследствие этого каждая хранимая процедура располагается в конкретной базе данных, где и выполняется.
  • Временные хранимые процедуры существуют лишь некоторое время, после чего автоматически уничтожаются сервером. Они делятся на локальные и глобальные. Локальные временные хранимые процедуры могут быть вызваны только из того соединения, в котором созданы. При создании такой процедуры ей необходимо дать имя, начинающееся с одного символа # . Как и все временные объекты, хранимые процедуры этого типа автоматически удаляются при отключении пользователя, перезапуске или остановке сервера. Глобальные временные хранимые процедуры доступны для любых соединений сервера, на котором имеется такая же процедура. Для ее определения достаточно дать ей имя, начинающееся с символов ## . Удаляются эти процедуры при перезапуске или остановке сервера, а также при закрытии соединения, в контексте которого они были созданы.

Создание, изменение и удаление хранимых процедур

Создание хранимой процедуры предполагает решение следующих задач:

  • определение типа создаваемой хранимой процедуры : временная или пользовательская. Кроме этого, можно создать свою собственную системную хранимую процедуру , назначив ей имя с префиксом sp_ и поместив ее в системную базу данных. Такая процедура будет доступна в контексте любой базы данных локального сервера;
  • планирование прав доступа. При создании хранимой процедуры следует учитывать, что она будет иметь те же права доступа к объектам базы данных, что и создавший ее пользователь;
  • определение параметров хранимой процедуры . Подобно процедурам, входящим в состав большинства языков программирования, хранимые процедуры могут обладать входными и выходными параметрами ;
  • разработка кода хранимой процедуры . Код процедуры может содержать последовательность любых команд SQL, включая вызов других хранимых процедур .

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

<определение_процедуры>::= {CREATE | ALTER } имя_процедуры [;номер] [{@имя_параметра тип_данных } [=default] ][,...n] AS sql_оператор [...n]

Рассмотрим параметры данной команды.

Используя префиксы sp_ , # , ## , создаваемую процедуру можно определить в качестве системной или временной. Как видно из синтаксиса команды, не допускается указывать имя владельца, которому будет принадлежать создаваемая процедура, а также имя базы данных, где она должна быть размещена. Таким образом, чтобы разместить создаваемую хранимую процедуру в конкретной базе данных, необходимо выполнить команду CREATE PROCEDURE в контексте этой базы данных. При обращении из тела хранимой процедуры к объектам той же базы данных можно использовать укороченные имена, т. е. без указания имени базы данных. Когда же требуется обратиться к объектам, расположенным в других базах данных, указание имени базы данных обязательно.

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

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

Для определения типа данных, который будет иметь соответствующий параметр хранимой процедуры , годятся любые типы данных SQL, включая определенные пользователем. Однако тип данных CURSOR может быть использован только как выходной параметр хранимой процедуры , т.е. с указанием ключевого слова OUTPUT .

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

Ключевое слово VARYING применяется совместно с

Последнее обновление: 14.08.2017

Нередко операция с данными представляет набор инструкций, которые необходимо выполнить в определенной последовательности. Например, при добавлении покупке товара необходимо внести данные в таблицу заказов. Однако перед этим надо проверить, а есть ли покупаемый товар в наличии. Возможно, при этом понадобится проверить еще ряд дополнительных условий. То есть фактически процесс покупки товара охватывает несколько действий, которые должны выполняться в определенной последовательности. И в этом случае более оптимально будет инкапсулировать все эти действия в один объект - хранимую процедуру (stored procedure).

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

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

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

Для создания хранимой процедуры применяется команда CREATE PROCEDURE или CREATE PROC .

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

Например, пусть в базе данных есть таблица, которая хранит данные о товарах:

CREATE TABLE Products (Id INT IDENTITY PRIMARY KEY, ProductName NVARCHAR(30) NOT NULL, Manufacturer NVARCHAR(20) NOT NULL, ProductCount INT DEFAULT 0, Price MONEY NOT NULL);

Создадим хранимую процедуру для извлечения данных из этой таблицы:

USE productsdb; GO CREATE PROCEDURE ProductSummary AS SELECT ProductName AS Product, Manufacturer, Price FROM Products

Поскольку команда CREATE PROCEDURE должна вызываться в отдельном пакете, то после команды USE, которая устанавливает текущую базу данных, используется команда GO для определения нового пакета.

После имени процедуры должно идти ключевое слово AS.

Для отделения тела процедуры от остальной части скрипта код процедуры нередко помещается в блок BEGIN...END:

USE productsdb; GO CREATE PROCEDURE ProductSummary AS BEGIN SELECT ProductName AS Product, Manufacturer, Price FROM Products END;

После добавления процедуры мы ее можем увидеть в узле базы данных в SQL Server Management Studio в подузле Programmability -> Stored Procedures :

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

Выполнение процедуры

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

EXEC ProductSummary

Удаление процедуры

Для удаления процедуры применяется команда DROP PROCEDURE :

DROP PROCEDURE ProductSummary