Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server-Aktualisierungsauslöser, Nur geänderte Felder abrufen

Ich habe eine andere völlig andere Lösung, die COLUMNS_UPDATED überhaupt nicht verwendet und auch nicht darauf angewiesen ist, zur Laufzeit dynamisches SQL zu erstellen. (Vielleicht möchten Sie zur Entwurfszeit dynamisches SQL verwenden, aber das ist eine andere Geschichte.)

Grundsätzlich beginnen Sie mit den eingefügten und gelöschten Tabellen, entpivozieren jede von ihnen, sodass Sie nur die eindeutigen Schlüssel-, Feldwert- und Feldnamenspalten für jede Tabelle haben. Dann verbinden Sie die beiden und filtern nach allem, was sich geändert hat.

Hier ist ein voll funktionsfähiges Beispiel, einschließlich einiger Testanrufe, um zu zeigen, was protokolliert wird.

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

Also kein Herumspielen mit Bigint-Bitfeldern und Art-Überlaufproblemen. Wenn Sie die zu vergleichenden Spalten zur Entwurfszeit kennen, benötigen Sie kein dynamisches SQL.

Auf der anderen Seite ist die Ausgabe in einem anderen Format und alle Feldwerte werden in sql_variant konvertiert, der erste könnte behoben werden, indem die Ausgabe erneut geschwenkt wird, und der zweite könnte behoben werden, indem man basierend auf Ihrem Wissen auf die erforderlichen Typen zurückkehrt Design der Tabelle, aber beides würde ein komplexes dynamisches SQL erfordern. Beides ist möglicherweise kein Problem in Ihrer XML-Ausgabe. Diese Frage macht etwas Ähnliches wie das Zurückholen der Ausgabe im selben Format.

Bearbeiten:Wenn Sie die folgenden Kommentare überprüfen, können Sie diese Methode weiterhin verwenden, wenn Sie einen natürlichen Primärschlüssel haben, der sich ändern könnte. Sie müssen nur eine Spalte hinzufügen, die standardmäßig mit einer GUID gefüllt ist, indem Sie die Funktion NEWID() verwenden. Diese Spalte verwenden Sie dann anstelle des Primärschlüssels.

Möglicherweise möchten Sie diesem Feld einen Index hinzufügen, aber da sich die gelöschten und eingefügten Tabellen in einem Trigger im Speicher befinden, werden sie möglicherweise nicht verwendet und können sich negativ auf die Leistung auswirken.