ИИмитация табличных триггеров

Включение в приложения Access возможностей, схожих с имеющимися в SQL Server

или Oracle.

Ни Access 2003, ни более ранние версии не поддерживают события в таблица*.

Триггер представляет собой событие в таблице, возникающее при операциях встав -

ки, редактирования или удаления, которое можно обработать с помощью собственной

функции. Примером полезного применения может послужить обработка

события редактирования до сохранения его результатов с целью организации

хранения прежнего состояния данных, то есть для помещения прежней версии

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

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

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

варианту.

Та же логика применима и к удалениям. Используя триггеры, вы можете вклиниться

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

их безвозвратно. В случае вставок (когда в таблицу добавляется новая

запись) вы можете перепроверить данные, прежде чем позволить им попасть

в таблицу.

К сожалению, Access не позволяет совершать подобные действия напрямую, при

работе непосредственно с таблицами. Но вы можете проделывать то же самое,

работая с таблицами из форм. В формах можно обрабатывать множество событий,

получая тот же результат, что и при использовании настоящих триггеров, но

работая при этом вместо таблиц с формами.

Ведение контрольного журнала

Чтобы показать этот механизм в действии, создадим в базе данных еще одну таблицу

НЕ нашли? Не то? Что вы ищете?

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

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

добавив два дополнительных поля: одного — для хранения типа операции, а другого

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

таблица данных (1Ы_Клиенты) и таблица, предназначенная для хранения записе \

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

или удаления (т. Ы_Клиенты_контрольный_журнал).

Обратите внимание на следующие особенности:

• Таблица журнала содержит два дополнительных поля: Операция и Время_опе-

рации.

• Если для таблицы данных поле КлиентЮ является первичным ключом, то длл

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

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

к одному и тому же клиенту (и с одним и тем же значением

поля КлиентID.

Отслеживание событий формы

Теперь вы можете воспользоваться стандартной формой для просмотра записей,

их добавления, редактирования и удаления из таблицы данных.

На рис. 2.17 показаны две таблиць :

таблица данных (1Ы_Клиенты) и таблица, предназначенная для хранения записе \

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

или удаления (т. Ы_Клиенты_контрольный_журнал).

Обратите внимание на следующие особенности:

• Таблица журнала содержит два дополнительных поля: Операция и Время_опе-

рации.

• Если для таблицы данных поле КлиентЮ является первичным ключом, то длл

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

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

к одному и тому же клиенту (и с одним и тем же значением

поля КлиентID).

Отслеживание событий формы

Теперь вы можете воспользоваться стандартной формой для просмотра записей,

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

обычная форма, имеющая в качестве источника записей таблицу tbL

Клиенты.

Разумеется, форма содержит встроенный программный модуль, обрабатывающий

два события: До обновления и Удаление. Обработчиком события До обновления обслуживаются

и вставки, и обновления, а обработчик события Удаление работает

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

события До обновления проверяет приемлемость данных (то есть, скажем,

наличие фамилии). Если данные не пройдут проверку, свойство Cancel будет

установлено в True, что приведет к отмене события.

В процессе обновления (редактирования) запись, подвергаемая изменениям, сначала

копируется в таблицу журнала, то есть сохраняется в нем в своем прежнем

виде. Перед удалением запись также попадает в таблицу журнала.

Код

Этот код должен быть помещен во встроенный программный модуль формы.

Поле Операция таблицы журнала получает одно из двух значений: Обновленке

или Удаление. Оба обработчика событий используют одну общую функцио

(build_sql):

Private Sub Form_Beforellpdate(Cancel As Integer)

On Error GoTo err_end

Dim ssql As String

Dim conn As ADODB. Connection

Set conn = CurrentProject. Connection

I f NewRecord = False Then

ssql = bui ld_sql (КлиентЮ, "Обновление")

conn. Execute ssql

conn. Close

Set conn = Nothing

Else

I f IsNull(ClientLastName) Or ClientLastName = "" Then

MsgBox "Нужно ввести фамилию"

Cancel = True

End If

End If

Exit Sub

err_end:

MsgBox Err. Description

End Sub

Private Sub Form_Delete(Cancel As Integer)

On Error GoTo err_end

Dim ssql As String

Dim conn As ADODB. Connection

Set conn = CurrentProject. Connection

ssql = build_sql(KnneHTlD, "Удаление")

conn. Execute ssql

Exit Sub

err_end:

MsgBox E r r. D e s c r i p t i on

End Sub

Function b u i l d _ s q l ( c l i e n t _ i d As Long, operation As

build_sql • "Insert Into 1Ы_Клиенты_контрольный

build_sql = build_sql & КлиентЮ & ", "

build_sql = build_sql & & _

String) As String

журнал Values ("

DLookup("Фамилия" , " 1Ы_Клиенты" , "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & & _

DLookup ("Имя " , 'ЧЫ_Клиенты" , "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & & _

DLookup ("Отчество", 'ЧЫ_Клиенты", "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & " " " & _

DLookupC'Aflpec", 'ЧЫ_Клиенты", "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & " " " & _

DLookup( "Город", " 1Ы._Клиенты", "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & & _

DLookup ("Почтовый_индекс", " 1Ы_Клиенты", "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sqi & & _

DLookup ("Телефон", 'ЧЫ_Клиенты", "КлиентЮ=" &_

c l i e n t _ i d ) & " " , "

build_sql = build_sql & & operation & " " , "

build_sql = build_sql & "#" & Now( ) & "#)"

End Function

Выполнение кода

Код выполняется в том случае, когда в форме совершаются операции вставки,

обновления и удаления. При этом никаких особых дополнительных действий —

скажем, щелчков на кнопке — не требуется. Журнал ведется во время работы

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

многократные изменения, произведенные в записи клиента. Функция

build_sql формирует команду SQL Insert. В эту команду, в зависимости от функции,

осуществившей вызов (и передавшей в виде аргумента слово «Обновление»

или «Уда-ление»), включается в качестве одного из регистрируемых параметров

либо Обновление, либо Удаление. Строка, содержащая команду SQL, возвращается

в функцию, осуществившую вызов, а затем эта функция выполняет

команду на вставку данных.

Все это может пригодиться на практике. Например, клиент переехал (изменился

адрес) или вступил в брак (изменилась фамилия), переметнулся к конкуренту (его

запись нужно удалять) и т. д. На рис. 2.19 показана таблица регистрационного

журнала, содержащая несколько записей. В каждой записи отражена операция

и время ее проведения.

Метод, применяемый в данном трюке, имитирует все то же самое, что предоставляется

SQL Server, Oracle и другими системами управления базами данных при

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

Access в чем-то им уступает!

В таблице клиентов и таблице регистрационного журнала содержится множество

записей и благодаря замечательному свойству регистрации времени сохраняете ч

вся последовательность их изменений.