Несколько раз встречал подобные вопросы в форумах. Задача, формулируется примерно так. Получить в триггере текст команды, которая привела к тому, что этот триггер сработал.
Это может быть полезно если мы, например, хотим увидеть/залогировать конкретный запрос, который привел к изменению данных. Сразу скажу, что штатный механизм для обеспечения этого на сегодняшний день, в версиях 2000, 2005, 2008 — не предусмотрен. Но тем не менее, что-то похожее можно реализовать с некоторыми ограничениями.
В этом нам поможет команда DBCC INPUTBUFFER. В отличии от fn_get_sql и, появившейся в 2005, sys.dm_exec_sql_text, которые вернули бы текст самого триггера, DBCC INPUTBUFFER — вернет последнюю инструкцию, отправленную серверу, в которой будет весь текст пакета.
Для того чтобы иметь возможность записать данные, возвращаемые DBCC, воспользуемся маленькой хитростью, при помощи динамического sql, обернем выполнение этой команды в вызов хранимой процедуры и запишем результат процедуры во временную таблицу, пользуясь синтаксисом insert … exec.
В итоге, выглядит это все так:
/*
создаем тестовые таблицы, одну - которую будем логировать,
вторую - в которую будем писать лог
*/
if object_id('dbo.TriggerTable') is not null drop table dbo.TriggerTable
if object_id('dbo.LogTable') is not null drop table dbo.LogTable
create table dbo.TriggerTable( field int )
create table dbo.LogTable( field nvarchar(4000) )
/*создаем триггер на вставку*/
go
create trigger TriggerTable_insert on dbo.TriggerTable after insert as
begin
set nocount on;
declare @temp table(EventType nvarchar (30), Parameters int, EventInfo nvarchar(4000) )
insert into @temp
exec sp_executesql N'DBCC inputbuffer (@@spid) WITH NO_INFOMSGS'
insert into dbo.LogTable
select EventInfo from @temp
end
/* добавляем значения */
go
insert into dbo.TriggerTable values (1)
go
insert into dbo.TriggerTable values (2)
go
/* проверяем что записалось в лог */
select * from dbo.LogTable
go
/* удаляем тестовые таблицы */
drop table dbo.LogTable
drop table dbo.TriggerTable
go
результат:
field ---------------------------------------- insert into dbo.TriggerTable values (1) insert into dbo.TriggerTable values (2) (2 row(s) affected)
О проблемах и ограничениях. Как было сказано, это не штатный механизм, и он имеет некоторые проблемы. Первая и главная, на мой взгяд, проблема, в том, что в буфер попадают все инструкции в текущем пакете, а не только та, что инициировала выполнение триггера. По этому получить в чистом виде именно тот запрос, который вызвал триггер, без остальных инструкций — невозможно. Вторая проблема заключается в том, что буфер имеет ограниченную четырьмя тысячами символов длину. Еще одна проблема, это выполнение вставки из ХП, в этом случае в буфер записывается вызов этой ХП, а не сам запрос на вставку. Ниже это все проиллюстрировано.
/*
создаем тестовые таблицы, одну - которую будем логировать,
вторую - в которую будем писать лог
*/
if object_id('dbo.TriggerTable') is not null drop table dbo.TriggerTable
if object_id('dbo.LogTable') is not null drop table dbo.LogTable
if object_id('dbo.TriggerTable_add') is not null drop proc dbo.TriggerTable_add
create table dbo.TriggerTable( field int )
create table dbo.LogTable( field nvarchar(4000) )
/*создаем триггер на вставку*/
go
create trigger TriggerTable_insert on dbo.TriggerTable after insert as
begin
set nocount on;
declare @temp table(EventType nvarchar (30), Parameters int, EventInfo nvarchar(4000) )
insert into @temp
exec sp_executesql N'DBCC inputbuffer (@@spid) WITH NO_INFOMSGS'
insert into dbo.LogTable
select EventInfo from @temp
end
/* добавляем значения */
/* Тут все в порядке, в лог попадает запрос на вставку*/
go
insert into dbo.TriggerTable values (1)
go
/* Вот тут в лог попадет все что до и после инсерта, т.е. все содержимое пакета*/
go
-- попадает все что есть в пакете
select 1
insert into dbo.TriggerTable values (2)
select 2
go
/* Тут в лог попадет только сам вызов процедуры*/
create proc dbo.TriggerTable_add @value int
as
insert into dbo.TriggerTable values (@value)
go
exec dbo.TriggerTable_add 3
go
/* проверяем что записалось в лог */
select * from dbo.LogTable
go
/* удаляем тестовые таблицы */
drop proc TriggerTable_add
drop table dbo.LogTable
drop table dbo.TriggerTable
go
результат:
field ---------------------------------------- insert into dbo.TriggerTable values (1) -- попадает все что есть в пакете select 1 insert into dbo.TriggerTable values (2) select 2 exec dbo.TriggerTable_add 3 (3 row(s) affected)
Наверняка существуют и еще какие-то проблемы, но мне хватило вышеперечисленного, чтобы отказаться всерьез рассматривать такой метод логирования действий пользователей на рабочих серверах. Тем не менее даже в таком виде, с ограничениями, этот способ имеет право на жизнь, и может использоваться в простых случаях. А для своих нужд, я остановился на использовании штатного механизма трассировки, при помощи функций семейства sp_trace_XXX. О том как и для чего они у меня используются напишу в следующем посте.


PDF


