Опубликован: 25.11.2008 | Доступ: свободный | Студентов: 4431 / 676 | Оценка: 4.46 / 4.18 | Длительность: 26:08:00
Лекция 12:

Встроенный SQL

Триггеры

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

В разных коммерческих СУБД рассматриваются разные триггеры. Так, в MS SQL Server триггеры определены только как постфильтры, то есть такие триггеры, которые выполняются после свершения события.

В СУБД Oracle определены два типа триггеров: триггеры, которые могут быть запущены перед реализацией операции модификации, они называются BEFORE-триггерами, и триггеры, которые активизируются после выполнения соответствующей модификации, аналогично триггерам MS SQL Server, — они называются AFTER-триггерами.

Триггеры могут быть эффективно использованы для поддержки семантической целостности БД, однако приоритет их ниже, чем приоритет правил-ограничений (constraints), задаваемых на уровне описания таблиц и на уровне связей между таблицами. При написании триггеров всегда надо помнить об этом, при нарушении правил целостности по связям (DRI declarative Referential Integrity) триггер просто может никогда не сработать.

В стандарте SQL1 ни хранимые процедуры, ни триггеры были не определены. Но в добавлении к стандарту SQL2, выпущенному в 1996 году, те и другие объекты были стандартизированы и определены.

Для создания триггеров используется специальная команда:

CREATE TRIGGER <имя_триггера>
ON <имя_таблицы>
FOR {[INSERT][, UPDATE] [, DELETE] }
[WITH ENCRIPTING]
AS
SQL-операторы (Тело триггера)

Имя триггера является идентификатором во встроенном языке программирования СУБД и должно удовлетворять соответствующим требованиям.

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

Параметр WITH ENCRIPTING имеет тот же смысл, что и для хранимых процедур, он скрывает исходный текст тела триггера.

Существует несколько правил, которые ограничивают набор операторов, которые могут быть использованы в теле триггера.

Так, в большинстве СУБД действуют следующие ограничения:

  • Нельзя использовать в теле триггера операции создания объектов БД (новой БД, новой таблицы, нового индекса, новой хранимой процедуры, нового триггера, новых индексов, новых представлений).
  • Нельзя использовать в триггере команду удаления объектов DROP для всех типов базовых объектов БД.
  • Нельзя использовать в теле триггера команды изменения базовых объектов ALTER TABLE, ALTER DATABASE.
  • Нельзя изменять права доступа к объектам БД, то есть выполнять команду GRAND или REVOKE.
  • Нельзя создать триггер для представления ( VIEW ) .
  • В отличие от хранимых процедур, триггер не может возвращать никаких значений, он запускается автоматически сервером и не может связаться самостоятельно ни с одним клиентом.

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

Текст этого триггера на языке Transact SQL приведен ниже:

/* Проверка существования данного триггера в системном каталоге */
if exists (select * from sysobjects 
where id = object_id('dbo.DEL_EXEMP') 
and sysstat & 0xf = 8)
drop trigger dbo.DEL_EXEMP
GO
CREATE TRIGGER DEL_EXEMP ON dbo.EXEMPLAR 
/* мы создаем триггер для таблицы EXEMPLAR */
FOR DELETE 
/* только для операции удаления */
AS
/* опишем локальные переменные */
DECLARE @Ntek int /* количество оставшихся экземпляров удаленной книги */
DECLARE @DEL_EX VARCHAR(12) /* шифр удаленного экземпляра*/
Begin 
/* по временной системной таблице, содержащей удаленные записи, 
определяем шифр книги, соответствующей последнему удаленномуэкземпляру */
  SELECT @DEL_EX = ISBN From deleted
  /* вызовем хранимую процедуру, которая определит количество экземпляров 
  книги с заданным шифром */
  EXEC @Ntek = COUNT_EX @DEL_EX
  /* Если больше нет экземпляров данной книги, то мы удаляем запись о книге 
  из таблицы BOOKS */
  IF @Ntek = 0 DELETE from BOOKS WHERE BOOKS.ISBN = @DEL_EX
END
GO

Динамический SQL

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

Сформированный таким образом текст SQL-оператора поступает в СУБД, которая должна его скомпилировать и выполнить "на лету", в процессе работы приложения. Если мы снова вернемся к этапам выполнения SQL-операторов, то первые четыре действия, связанные с синтаксическим анализом, семантическим анализом, построением и оптимизацией плана выполнения запроса, выполняются на этапе компиляции. В момент исполнения этого оператора СУБД просто изымает хранимый план выполнения этого оператора и исполняет его.

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

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

Наиболее простой формой динамического SQL является оператор непосредственного выполнения EXECUTE IMMEDIATE. Этот оператор имеет следующий синтаксис:

EXECUTE IMMEDIATE <имя_базовой переменной>

Базовая переменная содержит текст SQL оператора.

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

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

Этот этап выполняется оператором PREPARE, синтаксис которого приведен ниже:

PREPARE <имя_оператора> 
FROM <имя_базовой переменной>

<имя_оператора> - это идентификатор базового языка.

Далее на втором этапе этот определенный на первом этапе оператор может быть выполнен операцией EXECUTE, которая имеет синтаксис:

EXECUTE <имя_оператора> 
USING {<список базовых переменных> 
DESCRIPTOR <имя_дескриптора>}

Здесь DESCRIPTOR — это некоторая структура, которая описывается на клиенте, но создается и управляется сервером. Дескриптор представляет совокупность элементов данных, принадлежащих СУБД. Программное обеспечение СУБД должно содержать и поддерживать набор операций над дескрипторами. Эта структура была введена в стандарт SQL2 для типизации динамического SQL.

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

  • ALLOCATE DESCRIPTOR <имя_дескриптора> [WITH MAX <число_элементов>] — оператор связывает имя дескриптора с числом его базовых элементов и обеспечивает выделение памяти под данный дескриптор.
  • DEALLOCATE DESCRIPTOR <имя_дескриптора> — оператор освобождает разделяемую память СУБД, занятую хранением описания данного дескриптора. После выполнения данного оператора невозможно обратиться к дескриптору ни с одной операцией.
  • SET DESCRIPTOR {COUNT = <имя_базовой переменной> | VALUE <номер элемента> {<имя_ элемента>= <имя_базовой переменной>[,..]}} — оператор занесения в дескриптор описания передаваемых параметров. Описания передаются СУБД, которая их обрабатывает, внося соответствующие изменения в область данных, отведенную под дескриптор.
  • GET DESCRIPTOR { <имя_базовой переменной> = COUNT | VALUE <номер элемента> {<имя_базовой переменной>=<имя_элемента>[,..]} — оператор получения информации из дескрипторов после выполнения запроса.
  • DESCRIBE [ INPUT | OUTPUT] <имя_оператора> USING SQL DESCRIPTOR <имя_дескрипто-ра> — оператор, позволяющий получить описания таблиц результатов запросов ( DESCRIBE OUTPUT ) или входных параметров ( DESCRIBE INPUT ).
  • OPEN <имя_курсора> [USING <список базовых переменных> | USING SQL DESCRIPTOR <имя_дескриптора>] — динамический оператор открытия курсора.
  • FETCH <имя_курсора> [USING <список базовых переменных> | USING SQL DESCRIPTOR <имя_дескриптора>] — динамический оператор перемещения по курсору.
  • DEALLOCATE PREPARE <имя_оператора> — оператор уничтожает ранее подготовленный план выполнения оператора SQL и освобождает разделяемую память СУБД, связанную с хранением этого плана. Этот оператор имеет смысл применять, если вы не будете больше применять команду выполнения к подготовленному ранее оператору SQL.

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

Михаил Дубовик
Михаил Дубовик

В лекции как пример отношения в третьей нормальной форме приводится такая схема: (Номер зач. кн.\ ФИО \ Специальность \ Группа). Первичный ключ - Номер зач. кн. Но ведь существует следующая транзитивная зависимость: 

Номер зач. кн. -> Группа -> Специальность.

Получается, что отношение все же еще во второй нормальной форме. Или в моих рассуждениях ошибка?

Михаил Скок
Михаил Скок
Валентин Федченко
Валентин Федченко
Россия
Атанас Маринов
Атанас Маринов
Болгария