Триггер SQL-сервера при обновлении для аудита - PullRequest
0 голосов
/ 07 октября 2011

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

Я попытался сделать это с помощью Trigger on после обновления следующим образом:

Прежде всего определение таблицы аудита:

CREATE TABLE [Audit](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL default GETDATE(),
[IdTypeAudit] [int] NOT NULL, --2 for Modify
[UserName] [varchar](50) NULL,
[TableName] [varchar](50) NOT NULL,
[ColumnName] [varchar](50) NULL,
[OldData] [varchar](50) NULL,
[NewData] [varchar](50) NULL )

Следующий триггер на AFTER UPDATE в любой таблице:

DECLARE 
    @sql varchar(8000),
    @col int,
    @colcount int

select @colcount = count(*) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'MyTable'
set @col = 1

while(@col < @colcount )
begin

    set @sql=
    'INSERT INTO Audit
    SELECT 2, UserNameLastModif, ''MyTable'', COL_NAME(Object_id(''MyTable''), '+ convert(varchar,@col) +'), Deleted.' 
    + COL_NAME(Object_id('MyTable'), @col) + ', Inserted.' + COL_NAME(Object_id('MyTable'), @col) + '
    FROM Inserted LEFT JOIN Deleted ON Inserted.[MyTableId] = Deleted.[MyTableId]
    WHERE COALESCE(Deleted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''') <> COALESCE(Inserted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''')'

    --UserNameLastModif is an optional column on MyTable
    exec(@sql)
    set @col = @col + 1

end

Проблемы

  1. Вставленный и удаленный потерял контекст, когда я использую функцию exec
  2. Кажется, что число не всегда является соответствующим числом, кажется, если вы создаете таблицу с 20столбцы, и вы удаляете один и создаете другой, последний имеет номер> @ colcount

Я искал решение для всей сети, но я не смог выяснить

Любая идея?

Спасибо!

Ответы [ 3 ]

1 голос
/ 08 октября 2011

@ Сантьяго: Если вы все еще хотите написать его в динамическом SQL, вам следует сначала подготовить все операторы, а затем выполнить их. 8000 символов может быть недостаточно для всех утверждений. Хорошим решением является использование таблицы для их хранения.

IF NOT OBJECT_ID('tempdb..#stmt') IS NULL
    DROP TABLE #stmt; 
CREATE TABLE #stmt (ID int NOT NULL IDENTITY(1,1), SQL varchar(8000) NOT NULL); 

Затем замените строку exec(@sql) на INSERT INTO #stmt (SQL) VALUES (@sql);

Затем выполните каждую строку.

WHILE EXISTS (SELECT TOP 1 * FROM #stmt)
BEGIN
    BEGIN TRANSACTION; 
        EXEC (SELECT TOP 1 SQL FROM #stmt ORDER BY ID); 
        DELETE FROM #stmt WHERE ID = (SELECT MIN(ID) FROM #stmt); 
    COMMIT TRANSACTION; 
END

Не забудьте использовать sys.columns для цикла столбцов (я предполагаю, что вы используете SQL 2005/2008).

SET @col = 0; 
WHILE EXISTS (SELECT TOP 1 * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col) 
BEGIN
    SELECT TOP 1 @col = column_id FROM sys.columns 
    WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col ORDER BY column_id ASC; 
    SET @sql ....
    INSERT INTO #stmt ....
END

Удалить строку 4 @colcount int и исходящую запятую. Удалить информационную схему выберите.

1 голос
/ 08 октября 2011

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

Если вы хотите использовать динамический sql, используйте его для создания скрипта для создания триггера. И создайте таблицу аудита для каждой таблицы, которую вы хотите проверить (у нас фактически есть две таблицы для каждой таблицы), или у вас будут проблемы с производительностью из-за блокировки «одной таблицы, чтобы управлять ими всеми».

1 голос
/ 07 октября 2011

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

В SQL 2000 используйте syscolumns.В SQL 2005+ используйте sys.columns.т.е.

SELECT column_id FROM sys.columns WHERE object_id = OBJECT_ID(DB_NAME()+'.dbo.Table'); 
...