sql >> Database >  >> RDS >> Sqlserver

SQL Server Update Trigger, Alleen gewijzigde velden ophalen

Ik heb nog een totaal andere oplossing die COLUMNS_UPDATED helemaal niet gebruikt, en ook niet afhankelijk is van het bouwen van dynamische SQL tijdens runtime. (Misschien wilt u tijdens het ontwerpen dynamische SQL gebruiken, maar dat is een ander verhaal.)

In principe begin je met de ingevoegde en verwijderde tabellen, draai je ze allemaal los, zodat je alleen de unieke sleutel, veldwaarde en veldnaamkolommen voor elk overhoudt. Dan voeg je je bij de twee en filter je op alles wat is veranderd.

Hier is een volledig werkend voorbeeld, inclusief enkele testoproepen om te laten zien wat er is vastgelegd.

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

Dus geen gedoe met bigint bitfields en arth overflow problemen. Als u de kolommen kent die u tijdens het ontwerp wilt vergelijken, heeft u geen dynamische SQL nodig.

Aan de andere kant is de uitvoer in een ander formaat en worden alle veldwaarden geconverteerd naar sql_variant, de eerste kan worden opgelost door de uitvoer opnieuw te draaien, en de tweede kan worden opgelost door terug te keren naar de vereiste typen op basis van uw kennis van de ontwerp van de tabel, maar voor beide zou een complexe dynamische sql nodig zijn. Beide zijn mogelijk geen probleem in uw XML-uitvoer. Deze vraag doet iets vergelijkbaars met het terugkrijgen van de uitvoer in hetzelfde formaat.

Bewerken:als u de onderstaande opmerkingen bekijkt, kunt u deze methode nog steeds gebruiken als u een natuurlijke primaire sleutel heeft die zou kunnen veranderen. U hoeft alleen een kolom toe te voegen die standaard is gevuld met een GUID met behulp van de functie NEWID(). Deze kolom gebruik je dan in plaats van de primaire sleutel.

Misschien wilt u een index aan dit veld toevoegen, maar aangezien de verwijderde en ingevoegde tabellen in een trigger zich in het geheugen bevinden, wordt deze mogelijk niet gebruikt en kan dit een negatief effect hebben op de prestaties.



  1. SQL voltooien. Verhalen van succes en mislukking

  2. BDE vs ADO in Delphi

  3. Uw database upgraden naar PostgreSQL versie 10 - wat u moet weten

  4. Help alstublieft met STRING_SPLIT verbeteringen