Tôi có một giải pháp hoàn toàn khác hoàn toàn không sử dụng COLUMNS_UPDATED, cũng như không dựa vào việc xây dựng SQL động trong thời gian chạy. (Bạn có thể muốn sử dụng SQL động tại thời điểm thiết kế nhưng đó là một câu chuyện khác.)
Về cơ bản, bạn bắt đầu với các bảng đã chèn và đã xóa, bỏ chia từng bảng để bạn chỉ còn lại các cột khóa, giá trị trường và tên trường duy nhất cho mỗi bảng. Sau đó, bạn tham gia cả hai và lọc cho bất kỳ thứ gì đã thay đổi.
Đây là một ví dụ hoạt động đầy đủ, bao gồm một số lệnh gọi kiểm tra để hiển thị những gì đã được ghi nhật ký.
-- -------------------- 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;
Vì vậy, không có vấn đề gì với các trường bitfields bigint và vấn đề tràn arth. Nếu bạn biết các cột bạn muốn so sánh tại thời điểm thiết kế thì bạn không cần bất kỳ SQL động nào.
Mặt khác, đầu ra có định dạng khác và tất cả các giá trị trường được chuyển đổi thành sql_variant, giá trị đầu tiên có thể được sửa bằng cách xoay đầu ra một lần nữa và thứ hai có thể được sửa bằng cách quay lại các loại bắt buộc dựa trên kiến thức của bạn về thiết kế của bảng, nhưng cả hai điều này sẽ yêu cầu một số sql động phức tạp. Cả hai điều này có thể không phải là vấn đề trong đầu ra XML của bạn. Câu hỏi này thực hiện một cái gì đó tương tự như lấy lại đầu ra ở cùng một định dạng.
Chỉnh sửa:Xem lại các nhận xét bên dưới, nếu bạn có khóa chính tự nhiên có thể thay đổi thì bạn vẫn có thể sử dụng phương pháp này. Bạn chỉ cần thêm một cột được điền theo mặc định với GUID bằng cách sử dụng hàm NEWID (). Sau đó, bạn sử dụng cột này thay cho khóa chính.
Bạn có thể muốn thêm chỉ mục vào trường này, nhưng vì các bảng đã xóa và được chèn trong trình kích hoạt nằm trong bộ nhớ, nó có thể không được sử dụng và có thể có ảnh hưởng tiêu cực đến hiệu suất.