Я еще совсем другое решение, которое не использует 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 и проблемами переполнения артефактов. Если вам известны столбцы, которые вы хотите сравнить во время разработки, вам не нужен динамический SQL.
С другой стороны, выходной сигнал находится в другом формате, и все значения поля преобразуются в sql_variant, первый может быть исправлен путем поворота вывода снова, а второй может быть исправлен путем повторного возврата к требуемым типам на основе ваши знания о дизайне таблицы, но для обоих из них потребуется сложный динамический sql. Оба эти могут не быть проблемой в вашем XML-выходе.
Редактировать: Если у вас есть первичный первичный ключ, который может измениться, вы можете использовать этот метод. Вам просто нужно добавить столбец, который по умолчанию заполняется с помощью GUID с помощью функции NEWID(). Затем вы используете этот столбец вместо первичного ключа.
Возможно, вы захотите добавить индекс в это поле, но поскольку удаленные и вставленные таблицы в триггере находятся в памяти, он может не использоваться и может отрицательно влиять на производительность.
Спасибо за ответ, однако присоединиться к действительно дорого на этом уровне, учитывая огромные изменения, поэтому я не буду использовать его как я пытаюсь найти что-нибудь в отслеживании изменений, но благодаря так или иначе. –
Приятно, но для информации, когда в столбце sys.columns (из упавшего столбца) есть пробел в столбце_ид, этот код не обнаруживает. 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
не работает должным образом. Иногда он работает для 1 поля, иногда он работает, иногда он полностью завершается (он основан на запросе, но не рандомизированном выходе для одного и того же запроса). Я использую SQL Server 2014 (120)! – SKLTFZ