Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Тригер за актуализиране на SQL Server, Получавайте само променени полета

Имам друго напълно различно решение, което изобщо не използва COLUMNS_UPDATED, нито разчита на изграждане на динамичен SQL по време на изпълнение. (Може да искате да използвате динамичен SQL по време на проектиране, но това е друга история.)

По принцип започвате с вмъкнатите и изтритите таблици, отменяте всяка от тях, така че просто оставате с уникалния ключ, стойността на полето и колоните с име на полето за всяка. След това се присъединявате към двете и филтрирате за всичко, което е променено.

Ето пълен работен пример, включително някои тестови извиквания, за да се покаже какво е регистрирано.

-- -------------------- 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;

Така че не се бъркайте с големи битови полета и проблеми с препълването на арт. Ако знаете колоните, които искате да сравните по време на проектиране, тогава нямате нужда от динамичен SQL.

От друга страна, изходът е в различен формат и всички стойности на полетата се преобразуват в sql_variant, първият може да бъде фиксиран чрез завъртане на изхода отново, а вторият може да бъде коригиран чрез пренасочване обратно към необходимите типове въз основа на вашите познания за дизайн на таблицата, но и двете ще изискват някакъв сложен динамичен sql. И двете може да не са проблем във вашия XML изход. Този въпрос прави нещо подобно на връщането на изхода в същия формат.

Редактиране:Преглеждайки коментарите по-долу, ако имате естествен първичен ключ, който може да се промени, все още можете да използвате този метод. Просто трябва да добавите колона, която се попълва по подразбиране с GUID с помощта на функцията NEWID(). След това използвате тази колона вместо първичния ключ.

Може да искате да добавите индекс към това поле, но тъй като изтритите и вмъкнатите таблици в тригера са в паметта, това може да не се използва и да има отрицателен ефект върху производителността.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Записване на ВСИЧКИ заявки в база данни на SQL Server 2008 Express?

  2. Лоша идея ли е да имаш „ИЛИ“ в състояние INNER JOIN?

  3. Заявка, която връща йерархичен списък с типове тригерни събития в SQL Server

  4. Вътрешни елементи на SQL Server:Проблемни оператори Pt. II – Хеширане

  5. Свържете SQL Server към SugarCRM