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

Protokollieren des Aktualisierungsvorgangs in Triggern

Bevor ich antworte, lassen Sie mich zunächst sagen, dass ich es nicht für das Beste halte, alle Tabellen in einer einzigen Tabelle zu protokollieren. Wenn Ihre Datenbank wächst, könnte es zu ernsthaften Konflikten in der Log-Tabelle kommen. Außerdem müssen alle Ihre Daten in varchar oder sql_variant geändert werden, um in dieselbe Spalte eingefügt zu werden, was dazu führt, dass sie mehr Platz beanspruchen. Ich denke auch, dass das Protokollieren jeder aktualisierten Spalte in einer separaten Zeile (Überspringen von nicht aktualisierten Spalten) es sehr machen wird schwer für dich zu hinterfragen. Wissen Sie, wie Sie all diese Daten zusammenführen, um tatsächlich eine zusammengesetzte und vernünftige Ansicht der Änderungen jeder Zeile zu erhalten, wann und von wem? Meiner Meinung nach wird es viel einfacher sein, eine Protokolltabelle pro Tabelle zu haben. Dann werden Sie nicht die Probleme haben, die Sie haben, wenn Sie versuchen, es zum Laufen zu bringen.

Wussten Sie auch über SQL Server 2008 Datenerfassung ändern ? Verwenden Sie diese stattdessen, wenn Sie die Enterprise- oder Developer-Edition von SQL Server verwenden!

Abgesehen von diesem Problem können Sie mit einem logischen UNPIVOT machen, was Sie wollen (indem Sie Ihre eigene Version davon ausführen). Sie können Native SQL 2005 UNPIVOT nicht wirklich verwenden, da Sie zwei Zielspalten haben, nicht eine. Hier ist ein Beispiel für SQL Server 2005 und höher, das CROSS APPLY verwendet, um UNPIVOT auszuführen:

INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT 12345, I.Id, X.Id_Value, X.Old_Value, X.New_Value
FROM
   INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   CROSS APPLY (
      SELECT 4556645, D.Name, I.Name
      UNION ALL SELECT 544589, D.Surname, I.Surname
    ) X (Id_Value, Old_Value, New_Value)
WHERE
   X.Old_Value <> X.New_Value

Hier ist eine allgemeinere Methode für SQL 2000 oder andere DBMS (sollte theoretisch in Oracle, MySQL usw. funktionieren – für Oracle fügen Sie FROM DUAL hinzu zu jedem SELECT in der abgeleiteten Tabelle):

INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT *
FROM (
   SELECT
      12345,
      I.Id,
      X.Id_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN D.Name
         WHEN 544589 THEN D.Surname
      END Old_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN I.Name
         WHEN 544589 THEN I.Surname
      END New_Value   
   FROM
      INSERTED I 
      INNER JOIN DELETED D ON I.ID = D.ID
      CROSS JOIN (
         SELECT 4556645
         UNION ALL SELECT 544589
      ) X (Id_Value)
) Y
WHERE
   Y.Old_Value <> Y.New_Value

SQL Server 2005 und höher haben den nativen UNPIVOT-Befehl, obwohl ich im Allgemeinen, auch wenn UNPIVOT funktioniert, lieber CROSS APPLY verwende, weil ich mehr Flexibilität habe, um das zu tun, was ich will. Insbesondere der native UNPIVOT-Befehl ist hier nicht funktionsfähig, da UNPIVOT nur auf eine einzelne Zielspalte abzielen kann, Sie jedoch zwei benötigen (Old_Value, New_Value). Die beiden Spalten zu einem einzigen Wert zu verketten (und später zu trennen) ist nicht gut; Es ist nicht gut, danach einen bedeutungslosen Zeilenkorrelatorwert für PIVOT zu erstellen, und ich kann mir keine andere Möglichkeit vorstellen, dies nicht als Variation dieser beiden zu tun. Die CROSS APPLY-Lösung wird wirklich die beste Lösung für Sie sein, um genau die von Ihnen beschriebene Protokolltabellenstruktur abzugleichen.

Im Vergleich zu meinen Abfragen hier wird Ihre Methode Nr. 1 nicht so gut abschneiden (in einem Verhältnis von etwa {Anzahl der Spalten}:1 schlechtere Leistung). Ihre Methode Nr. 2 ist eine gute Idee, aber immer noch suboptimal, da das Aufrufen einer UDF einen großen Overhead hat und Sie dann jede Zeile durchlaufen müssen (schaudern).