Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Wyzwalacz aktualizacji SQL Server, Pobierz tylko zmodyfikowane pola

Mam inne zupełnie inne rozwiązanie, które w ogóle nie używa COLUMNS_UPDATED, ani nie polega na budowaniu dynamicznego SQL w czasie wykonywania. (Możesz użyć dynamicznego SQL w czasie projektowania, ale to już inna historia).

Zasadniczo zaczynasz od wstawionych i usuniętych tabel, przestawiaj każdą z nich, tak aby pozostały tylko unikalne kolumny klucza, wartości pola i nazwy pola dla każdej z nich. Następnie dołączasz do tych dwóch i filtrujesz wszystko, co się zmieniło.

Oto pełny przykład działania, w tym kilka wywołań testowych, aby pokazać, co jest rejestrowane.

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

Więc nie ma kłopotów z polami bitowymi Bigint i problemami z przepełnieniem arthu. Jeśli znasz kolumny, które chcesz porównać w czasie projektowania, nie potrzebujesz dynamicznego SQL.

Z drugiej strony dane wyjściowe są w innym formacie, a wszystkie wartości pól są konwertowane na sql_variant, pierwsze można naprawić, ponownie obracając dane wyjściowe, a drugie można naprawić, przerzucając z powrotem do wymaganych typów w oparciu o Twoją wiedzę na temat projekt tabeli, ale oba te wymagałyby skomplikowanego dynamicznego sql. Oba te elementy mogą nie stanowić problemu w danych wyjściowych XML. To pytanie działa podobnie do odzyskania danych wyjściowych w tym samym formacie.

Edycja:przeglądając poniższe komentarze, jeśli masz naturalny klucz podstawowy, który może się zmienić, nadal możesz korzystać z tej metody. Wystarczy dodać kolumnę, która jest domyślnie wypełniona identyfikatorem GUID za pomocą funkcji NEWID(). Następnie użyj tej kolumny zamiast klucza podstawowego.

Możesz dodać indeks do tego pola, ale ponieważ usunięte i wstawione tabele w wyzwalaczu znajdują się w pamięci, może nie być używane i może mieć negatywny wpływ na wydajność.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak usunąć ograniczenie w SQL Server (T-SQL)

  2. Funkcje matematyczne programu SQL Server (pełna lista)

  3. Jak zmienić rozmiar kolumny w SQL Server (T-SQL)

  4. SQL Server DATEPART() vs DATENAME() — jaka jest różnica?

  5. Jak pobrać liczbę wierszy dla wszystkich tabel w bazie danych SQL SERVER?