Jeg har en anden helt anden løsning, der slet ikke bruger COLUMNS_UPDATED, og den er heller ikke afhængig af at bygge dynamisk SQL under kørsel. (Du vil måske bruge dynamisk SQL på designtidspunktet, men det er en anden historie.)
Grundlæggende starter du med de indsatte og slettede tabeller, unpivot hver af dem, så du bare er tilbage med den unikke nøgle, feltværdi og feltnavnskolonner for hver. Så slutter du dig til de to og filtrerer efter alt, der er ændret.
Her er et fuldt fungerende eksempel, inklusive nogle testopkald for at vise, hvad der er logget.
-- -------------------- 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;
Så ingen roder rundt med bigint bitfields og arth overflow-problemer. Hvis du kender de kolonner, du vil sammenligne på designtidspunktet, behøver du ikke nogen dynamisk SQL.
På den negative side er output i et andet format, og alle feltværdier konverteres til sql_variant, den første kunne rettes ved at pivotere outputtet igen, og den anden kunne rettes ved at omforme tilbage til de påkrævede typer baseret på din viden om design af bordet, men begge disse ville kræve noget kompleks dynamisk sql. Begge disse er muligvis ikke et problem i dit XML-output. Dette spørgsmål gør noget, der ligner at få output tilbage i samme format.
Rediger:Gennemgang af kommentarerne nedenfor, hvis du har en naturlig primær nøgle, der kan ændre sig, kan du stadig bruge denne metode. Du skal blot tilføje en kolonne, der som standard er udfyldt med en GUID ved hjælp af NEWID()-funktionen. Du bruger så denne kolonne i stedet for den primære nøgle.
Du vil måske tilføje et indeks til dette felt, men da de slettede og indsatte tabeller i en trigger er i hukommelsen, bliver det muligvis ikke brugt og kan have en negativ effekt på ydeevnen.