mam inną zupełnie inne rozwiązanie, które nie używają COLUMNS_UPDATED w ogóle, ani nie polegać na budowaniu dynamicznych SQL w czasie wykonywania. (Możesz chcieć użyć dynamicznego SQL w czasie projektowania, ale to już inna historia.)
Zasadniczo zaczynasz od the inserted and deleted tables, rozpakowujesz każdą z nich, dzięki czemu po prostu pozostawiasz unikalne kolumny klucza, kolumny wartości pola i nazwy pól dla każdego z nich. Potem dołączasz do dwóch i filtrujesz wszystko, co się zmieniło.
Oto pełny przykład działania, w tym niektóre wywołania testowe pokazujące, 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;
Nie ma problemu z dużymi bitfieldami i problemami z przepełnieniem arth. Jeśli znasz kolumny, które chcesz porównać w czasie projektowania, nie potrzebujesz dynamicznego SQL.
Z drugiej strony dane wyjściowe mają inny format, 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ć, przekształcając z powrotem na wymagane typy na podstawie znajomość projektu tabeli, ale oba te elementy wymagałyby złożonego dynamicznego sql. Oba mogą nie być problemem w wynikach XML.
Edytuj: Przeglądając poniższe komentarze, jeśli masz naturalny klucz podstawowy, który może ulec zmianie, nadal możesz korzystać z tej metody. Trzeba tylko dodać kolumnę, która jest domyślnie zapełniona za pomocą identyfikatora GUID za pomocą funkcji NEWID(). Następnie używasz tej kolumny zamiast klucza podstawowego.
Możesz dodać indeks do tego pola, ale ponieważ usunięte i wstawione tabele w wyzwalaczu są w pamięci, mogą nie zostać użyte i mogą mieć negatywny wpływ na wydajność.
Dzięki za odpowiedź, jednak JOIN jest naprawdę kosztowne na tym poziomie, biorąc pod uwagę ogromne zmiany, więc nie będę go używać jako Próbuję dowiedzieć się czegoś w śledzeniu zmian, ale dzięki tak. –
Fajnie, ale dla informacji, gdy istnieje luka w kolumnie id_kolumny w sys.columns (ex dropped column), powyższy kod go nie wykrywa. Np. Z tabelą 26 kolumn, wynikiem jest [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], podczas gdy nie ma kolumny z column_id = 9 – Yahia
nie działa zgodnie z oczekiwaniami. Czasami działa dla 1 pól, czasem działa, czasem nie działa całkowicie (jest oparty na zapytaniu, ale nie jest wynikiem randomizacji dla tego samego zapytania). Używam serwera SQL 2014 (120)! – SKLTFZ