Mam inne zupełnie inne rozwiązanie, które w ogóle nie używa COLUMNS_UPDATED, ani nie polega na budowaniu dynamicznego SQL w czasie wykonywania. (Możesz użyć dynamicznego SQL w czasie projektowania, ale to już inna historia).
Zasadniczo zaczynasz od wstawionych i usuniętych tabel, przestawiaj każdą z nich, tak aby pozostały tylko unikalne kolumny klucza, wartości pola i nazwy pola dla każdej z nich. Następnie dołączasz do tych dwóch i filtrujesz wszystko, co się zmieniło.
Oto pełny przykład działania, w tym kilka wywołań testowych, aby pokazać, co jest rejestrowane.
-- -------------------- 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','[email protected]',24);
INSERT INTO Sample_Table VALUES (2,'Alice','Brown','2255','[email protected]',32);
INSERT INTO Sample_Table VALUES (3,'Reg','Jones','2280','[email protected]',19);
INSERT INTO Sample_Table VALUES (4,'Mary','Doe','2216','[email protected]',28);
INSERT INTO Sample_Table VALUES (5,'Peter','Nash','2214','[email protected]',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','[email protected]',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;
Więc nie ma kłopotów z polami bitowymi Bigint i problemami z przepełnieniem arthu. Jeśli znasz kolumny, które chcesz porównać w czasie projektowania, nie potrzebujesz dynamicznego SQL.
Z drugiej strony dane wyjściowe są w innym formacie, a wszystkie wartości pól są konwertowane na sql_variant, pierwsze można naprawić, ponownie obracając dane wyjściowe, a drugie można naprawić, przerzucając z powrotem do wymaganych typów w oparciu o Twoją wiedzę na temat projekt tabeli, ale oba te wymagałyby skomplikowanego dynamicznego sql. Oba te elementy mogą nie stanowić problemu w danych wyjściowych XML. To pytanie działa podobnie do odzyskania danych wyjściowych w tym samym formacie.
Edycja:przeglądając poniższe komentarze, jeśli masz naturalny klucz podstawowy, który może się zmienić, nadal możesz korzystać z tej metody. Wystarczy dodać kolumnę, która jest domyślnie wypełniona identyfikatorem GUID za pomocą funkcji NEWID(). Następnie użyj tej kolumny zamiast klucza podstawowego.
Możesz dodać indeks do tego pola, ale ponieważ usunięte i wstawione tabele w wyzwalaczu znajdują się w pamięci, może nie być używane i może mieć negatywny wpływ na wydajność.