2011-10-06 2 views
0

Я не могу найти простой/общий способ регистрации в таблице аудита, в котором таблицы были изменены на некоторые таблицы.SQL-Server Trigger on update for Audit

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

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

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. Inserted и Исключен потеряли контекст, когда я использую функцию EXEC
  2. Кажется, что colnumber не всегда корреляционное число, кажется, если вы создаете таблицу с 20 столбцами и удалить одну и создать другие, последний имеет номер> @colcount

Я искал решение для всей сети, но я couln't выяснить

Любая идея?

Спасибо!

ответ

1

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

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

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

Я понимаю решение, которое вы сказали, но это означает, что мне нужна таблица аудита для каждой таблицы, которую я хочу провести аудит, поэтому у вас есть дополнительная сложность в поиске изменений в базе данных. Спасибо за вашу помощь! Может быть, я тебе порекомендую. – Santiago

+1

@Sanitago, вам нужна таблица аудита для каждой таблицы, иначе у вас будут проблемы с блокировкой. – HLGEM

+0

@HLGEM: очень хорошая точка. Недавний триггер, который я написал, использует сервис-брокер (а не для аудита, но это возможное решение). Он будет стоять в очереди асинхронно, пока блокировки не будут освобождены. Хотя, если вы собираетесь так много усилий, почему бы не иметь отдельные таблицы. Они также запрашивали бы быстрее. –

1

@Santiago: Если вы все еще хотите, чтобы записать его в динамическом 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);

Затем EXEC каждую строку.

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

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

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

+0

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