У меня есть другое совершенно другое решение, которое вообще не использует COLUMNS_UPDATED и не основывается на построении динамического SQL во время выполнения. (Возможно, вы захотите использовать динамический SQL во время разработки, но это уже другая история.)
По сути, вы начинаете с вставленных и удаленных таблиц , отмените разворачивание каждой из них, так что у вас останется уникальный ключ, значение поля и столбцы имени поля для каждого. Затем вы присоединяетесь к ним и фильтруете все, что изменилось.
Вот полный рабочий пример, включая некоторые тестовые вызовы, чтобы показать, что регистрируется.
-- -------------------- Setup tables and some initial data --------------------
CREATE TABLE dbo.Sample_Table (ContactID int, Forename varchar(100), Surname varchar(100), Extn varchar(16), Email varchar(100), Age int );
INSERT INTO Sample_Table VALUES (1,'Bob','Smith','2295','bs@example.com',24);
INSERT INTO Sample_Table VALUES (2,'Alice','Brown','2255','ab@example.com',32);
INSERT INTO Sample_Table VALUES (3,'Reg','Jones','2280','rj@example.com',19);
INSERT INTO Sample_Table VALUES (4,'Mary','Doe','2216','md@example.com',28);
INSERT INTO Sample_Table VALUES (5,'Peter','Nash','2214','pn@example.com',25);
CREATE TABLE dbo.Sample_Table_Changes (ContactID int, FieldName sysname, FieldValueWas sql_variant, FieldValueIs sql_variant, modified datetime default (GETDATE()));
GO
-- -------------------- Create trigger --------------------
CREATE TRIGGER TriggerName ON dbo.Sample_Table FOR DELETE, INSERT, UPDATE AS
BEGIN
SET NOCOUNT ON;
--Unpivot deleted
WITH deleted_unpvt AS (
SELECT ContactID, FieldName, FieldValue
FROM
(SELECT ContactID
, cast(Forename as sql_variant) Forename
, cast(Surname as sql_variant) Surname
, cast(Extn as sql_variant) Extn
, cast(Email as sql_variant) Email
, cast(Age as sql_variant) Age
FROM deleted) p
UNPIVOT
(FieldValue FOR FieldName IN
(Forename, Surname, Extn, Email, Age)
) AS deleted_unpvt
),
--Unpivot inserted
inserted_unpvt AS (
SELECT ContactID, FieldName, FieldValue
FROM
(SELECT ContactID
, cast(Forename as sql_variant) Forename
, cast(Surname as sql_variant) Surname
, cast(Extn as sql_variant) Extn
, cast(Email as sql_variant) Email
, cast(Age as sql_variant) Age
FROM inserted) p
UNPIVOT
(FieldValue FOR FieldName IN
(Forename, Surname, Extn, Email, Age)
) AS inserted_unpvt
)
--Join them together and show what's changed
INSERT INTO Sample_Table_Changes (ContactID, FieldName, FieldValueWas, FieldValueIs)
SELECT Coalesce (D.ContactID, I.ContactID) ContactID
, Coalesce (D.FieldName, I.FieldName) FieldName
, D.FieldValue as FieldValueWas
, I.FieldValue AS FieldValueIs
FROM
deleted_unpvt d
FULL OUTER JOIN
inserted_unpvt i
on D.ContactID = I.ContactID
AND D.FieldName = I.FieldName
WHERE
D.FieldValue <> I.FieldValue --Changes
OR (D.FieldValue IS NOT NULL AND I.FieldValue IS NULL) -- Deletions
OR (D.FieldValue IS NULL AND I.FieldValue IS NOT NULL) -- Insertions
END
GO
-- -------------------- Try some changes --------------------
UPDATE Sample_Table SET age = age+1;
UPDATE Sample_Table SET Extn = '5'+Extn where Extn Like '221_';
DELETE FROM Sample_Table WHERE ContactID = 3;
INSERT INTO Sample_Table VALUES (6,'Stephen','Turner','2299','st@example.com',25);
UPDATE Sample_Table SET ContactID = 7 where ContactID = 4; --this will be shown as a delete and an insert
-- -------------------- See the results --------------------
SELECT *, SQL_VARIANT_PROPERTY(FieldValueWas, 'BaseType') FieldBaseType, SQL_VARIANT_PROPERTY(FieldValueWas, 'MaxLength') FieldMaxLength from Sample_Table_Changes;
-- -------------------- Cleanup --------------------
DROP TABLE dbo.Sample_Table; DROP TABLE dbo.Sample_Table_Changes;
Так что не стоит возиться с битовыми полями bigint и проблемами с переполнением арт. Если вы знаете столбцы, которые хотите сравнить во время разработки, вам не нужен динамический SQL.
С другой стороны, вывод находится в другом формате, и все значения полей преобразуются в sql_variant, первое можно исправить, снова повернув вывод, а второе можно исправить, вернувшись к требуемым типам на основе вашего знание дизайна таблицы, но оба из них потребуют некоторого сложного динамического SQL. Оба из них могут не быть проблемой в вашем выводе XML. Этот вопрос делает что-то похожее на получение результата в том же формате.
Редактировать: Изучив комментарии ниже, если у вас есть естественный первичный ключ, который может измениться, вы все равно можете использовать этот метод. Вам просто нужно добавить столбец, который по умолчанию заполнен GUID, используя функцию NEWID (). Затем вы используете этот столбец вместо первичного ключа.
Возможно, вы захотите добавить индекс в это поле, но поскольку удаленные и вставленные таблицы в триггере находятся в памяти, он может не использоваться и может отрицательно повлиять на производительность.