2014-09-29 6 views
0

У меня есть таблица со столбцами businessname, sortcode и accountnumber все заселены, name, nationality и DOB все в настоящее время заселены. Мне нужно создать триггер, чтобы снимать каждое обновление в таблицу аудита, когда обновляется любое из нулевых полей, поэтому, если я изменю только имя, я получу отметку времени, имя пользователя, поле, старое значение и новое значение. Если я изменил все 3 нулевых поля, я бы хотел отправить 3 строки в таблицу аудита.SQL триггер для записи UPDATE в таблице аудита

Может кто-нибудь дать мне указатель на логику этого, пожалуйста?

В зачаточном формате для тестирования у меня

CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE 
ON UPDATE 
AS 
    INSERT INTO RM_AUDITLOG 
     SELECT CURRENT_TIMESTAMP, SRT_CD 
     FROM RM_BASE 

но это посылает все существующие строки через после UPDATE к любому из них, я хочу только строки, которые были затронуты. Я не уверен, что мне нужно собрать больше таблиц, чтобы получить окончательный ответ или использовать таблицы INSERT/DELETE. Я видел таблицу аудита в этом формате в предыдущих ролях, поэтому я знаю, что она работает, t понять это!

Благодаря

+0

Sorry - SQL сервера 2008 R2 – user3294008

ответ

0

Да, вы должны использовать псевдо-таблицы INSERTED и/или DELETED тех, содержат только те строки, которые были изменены. Как и в:

CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE 
ON UPDATE 
AS 
    INSERT INTO RM_AUDITLOG 
     SELECT CURRENT_TIMESTAMP, SRT_CD 
     FROM INSERTED 

Таблица INSERTED имеет «новый» или «текущий» версии каждой строки в то время как DELETED таблица имеет «старую» версию, которая была заменена через UPDATE операции. Это относится ко всем версиям SQL Server, по крайней мере, вплоть до SQL Server 2000.

Чтобы отслеживать само изменение (как «старые», так и «новые» значения), вам необходимо подключиться эти два псевдо-таблицы, как в:

INSERT INTO RM_AUDITLOG 
    SELECT CURRENT_TIMESTAMP, ins.SRT_CD AS [SRT_CD_new], del.SRT_CD AS [SRT_CD_old] 
    FROM INSERTED ins 
    INNER JOIN DELETED del 
      ON del.PKfield = ins.PKfield 

Это основная операция для захвата изменений (если, конечно, вы не используете Change Data Capture) в качестве триггера DML.

Если вы хотите unpivot этих данных, чтобы каждый набор «старых» и «новых» столбцов становился строкой, которая должна быть легко адаптирована из приведенного выше. В этом случае вы также можете добавить WHERE ISNULL(ins.column, '~~~~') <> ISNULL(del.column, '~~~~') COLLATE Latin1_General_BIN, чтобы избежать захвата полей, которые не изменились. COLLATE обеспечивает чувствительность к регистру/акцент-чувствительность/etc.

Разумеется, для разворота очень сложно восстановить всю строку, поскольку тогда вам необходимо сохранить всю историю навсегда. Вам нужно будет начать с базовых значений для всех полей и применять изменения поэтапно. Типичным сценарием аудита является просто захват строки с полями как для старых, так и для новых для каждого исходного поля (как я уже показал).Если таблица аудита выглядит как:

PKfield, DateModified, businessname_old, businessname_new, sortcode_old, sortcode_new

, то вы можете написать запрос, чтобы определить, какие поля фактически изменены путем сравнения каждого набора (при условии, что более чем 1 поле может изменяться в той же UPDATE операции), что-то как:

SELECT PKfield, 
     DateModified, 
     CASE 
     WHEN ISNULL(businessname_old, '~~~~') <> ISNULL(businessname_new, '~~~~') 
       COLLATE Latin1_General_BIN THEN 'BusinessName ' ELSE '' 
     END + 
     CASE 
     WHEN ISNULL(sortcode_old, '~~~~') <> ISNULL(sortcode_new, '~~~~') 
       COLLATE Latin1_General_BIN THEN 'SortCode ' ELSE '' 
     END AS [FieldsChanged] 
FROM AuditTable 
ORDER BY DateModified DESC; 

НО, если вы действительно хотите UNPIVOT данные, чтобы иметь одну строку для каждого фактического измененного поля, то следующая структура должна работать:

;WITH ins AS 
(
    SELECT PKfield, FieldName, Value 
    FROM (
      SELECT PKfield, businessname, sortcode, accountnumber, name, 
        nationality, DOB 
      FROM INSERTED 
     ) cols 
    UNPIVOT (Value FOR FieldName IN 
       (businessname, sortcode, accountnumber, name, nationality, DOB) 
      ) colvals 
), del AS 
(
    SELECT PKfield, FieldName, Value 
    FROM (
      SELECT PKfield, businessname, sortcode, accountnumber, name, 
        nationality, DOB 
      FROM DELETED 
     ) cols 
    UNPIVOT (Value FOR FieldName IN 
       (businessname, sortcode, accountnumber, name, nationality, DOB) 
      ) colvals 
) 
INSERT INTO AuditTable (PKfield, DateModified, FieldName, OldValue, NewValue) 
    SELECT ins.PKfield, CURRENT_TIMESTAMP, ins.FieldName, del.Value, ins.Value 
    FROM ins 
    INNER JOIN del 
      ON del.PKfield = ins.PKfield 
      AND del.FieldName = ins.FieldName 
    WHERE ISNULL(del.Value, '~~~~') <> 
      ISNULL(ins.Value, '~~~~') COLLATE Latin1_General_BIN; 

Вы может нужно добавить CONVERT(VARCHAR(1000), field) к условию WHERE если DOB является DATE или DATETIME поле, или если SRT_CD является INT или другой тип номера поля:

WHERE ISNULL(CONVERT(VARCHAR(1000), del.Value), '~~~~') <> 
      ISNULL(CONVERT(VARCHAR(1000), ins.Value), '~~~~') 
      COLLATE Latin1_General_BIN; 
+0

Ah Awesome, спасибо - я никогда не использовал псевдо таблицы. Как я могу захватить имя поля, которое было обновлено от вставки или удаления? – user3294008

+0

@ user3294008, во-первых, я просто перечитываю вопрос, и кажется, что вы хотите только захватить поля, когда они переходят из «NULL» в значение, но не тогда, когда значение, отличное от NULL, изменяется на другое значение, отличное от NULL стоимость. Это правильно или просто неверно сформулировано в вопросе? –

+0

Нет. Я хочу, чтобы все изменения теперь были изменены, будь то пустые или заполненные, просто они все в настоящее время равны нулю – user3294008

Смежные вопросы