我有另一种不使用COLUMNS_UPDATED都完全不同的解决方案,也不依赖于在运行时创建动态SQL。 (您可能想在设计时使用动态SQL,但那是另一回事。)
基本上,您从the inserted and deleted tables开始,取消每个对象,因此您只剩下每个对象的唯一键,字段值和字段名称列。然后你加入这两个并过滤任何改变的东西。
这是一个完整的工作示例,其中包括一些测试调用以显示记录内容。
-- -------------------- 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;
所以没有与BIGINT位域和ARTH溢出问题瞎搞。如果您在设计时知道要比较的列,那么您不需要任何动态SQL。
缺点是输出格式不同,所有字段值都转换为sql_variant,第一个可以通过再次旋转输出来修复,第二个可以通过基于你对表的设计知识,但这两个都需要一些复杂的动态SQL。这两个可能不是您的XML输出中的问题。
编辑:回顾下面的评论,如果你有一个自然的主键可以改变,那么你仍然可以使用这种方法。您只需添加一个使用NEWID()函数默认填充GUID的列。然后,您使用此列代替主键。
您可能希望为此字段添加索引,但由于触发器中已删除和插入的表在内存中可能无法使用,因此可能会对性能产生负面影响。
感谢您的回答,但该连接是在这个级别考虑了巨大的变化确实昂贵的,所以我不打算使用它,因为我试图发现在更改跟踪,但由于东西反正。 – 2009-12-17 16:59:18
不错,但是对于信息来说,当sys.columns中的column_id存在空隙时(上面的代码被丢弃的列),上面的代码没有检测到它。 Ex的表格为26列,结果为[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27]虽然没有column_id = 9 – Yahia 2016-01-22 12:06:24
它没有按预期工作。有时它适用于1个字段,有时可用,有时完全失败(它基于查询,但不是同一查询的随机输出)。我正在使用SQL Server 2014(120)! – SKLTFZ 2017-01-04 05:30:23