sql >> Databáze >  >> RDS >> Sqlserver

SQL Server Update Trigger, získat pouze upravená pole

Mám další úplně jiné řešení, které vůbec nepoužívá COLUMNS_UPDATED, ani se nespoléhá na vytváření dynamického SQL za běhu. (Možná budete chtít použít dynamické SQL v době návrhu, ale to je jiný příběh.)

V zásadě začnete s vloženými a odstraněnými tabulkami, každou z nich rozbalíte, takže pro každou z nich zbude jedinečný klíč, hodnota pole a sloupec názvu pole. Pak se k nim připojíte a vyfiltrujete vše, co se změnilo.

Zde je úplný pracovní příklad, včetně několika testovacích volání, která ukazují, co je protokolováno.

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

Takže žádné zahrávání si s velkými bitovými poli a problémy s přetečením arthů. Pokud znáte sloupce, které chcete porovnat v době návrhu, nepotřebujete žádné dynamické SQL.

Na druhou stranu je výstup v jiném formátu a všechny hodnoty polí jsou převedeny na sql_variant, první lze opravit opětovným otočením výstupu a druhé lze opravit přetypováním zpět na požadované typy na základě vašich znalostí design tabulky, ale obojí by vyžadovalo nějaký komplexní dynamický sql. Obojí nemusí být problém ve vašem výstupu XML. Tato otázka dělá něco podobného jako získání výstupu zpět ve stejném formátu.

Upravit:Z níže uvedených komentářů zjistíte, že pokud máte přirozený primární klíč, který by se mohl změnit, můžete tuto metodu stále používat. Stačí přidat sloupec, který je ve výchozím nastavení vyplněn GUID pomocí funkce NEWID(). Tento sloupec pak použijete místo primárního klíče.

Možná budete chtít do tohoto pole přidat index, ale protože smazané a vložené tabulky ve spouštěči jsou v paměti, nemusí se použít a může to mít negativní vliv na výkon.



  1. Najděte záznamy SQL obsahující podobné řetězce

  2. SQLite - Změna tabulky

  3. Ušetřete čas spouštěním sestav Microsoft Access pomocí filtrů v zobrazení rozvržení

  4. Změna priority účtu v databázovém poštovním profilu (SSMS)