Да, вы должны использовать псевдо-таблицы 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;
Sorry - SQL сервера 2008 R2 – user3294008