sql >> Database teknologi >  >> RDS >> Sqlserver

Logning af opdateringsoperation i triggere

Inden jeg svarer, så lad mig først sige, at jeg ikke synes, det er bedst at logge alle tabeller til en enkelt tabel. Hvis din database vokser, kan du ende med alvorlige stridigheder i Log-tabellen. Plus, alle dine data skal ændres til varchar eller sql_variant for at blive sat i den samme kolonne, hvilket tvinger dem til at optage mere plads. Jeg tror også, at logning af hver opdateret kolonne til en separat række (at springe kolonner over, der ikke er opdateret), vil gøre det meget svært for dig at forespørge. Ved du, hvordan du samler alle disse data for faktisk at få et sammensat og fornuftigt overblik over hver rækkes ændringer, hvornår og af hvem? Det vil efter min mening være meget nemmere at have én logtabel pr. tabel. Så vil du ikke have de problemer, du oplever, når du prøver at få det til at fungere.

Vidste du også om SQL Server 2008 Skift datafangst ? Brug det i stedet, hvis du bruger Enterprise- eller Developer-udgaven af ​​SQL Server!

Bortset fra det problem kan du gøre hvad du vil med en logisk UNPIVOT (udføre din egen version af den). Du kan ikke rigtig bruge Native SQL 2005 UNPIVOT, fordi du har to målkolonner, ikke én. Her er et eksempel for SQL Server 2005 og nyere ved at bruge CROSS APPLY til at udføre UNPIVOT:

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

Her er en mere generisk metode til SQL 2000 eller andre DBMS'er (bør teoretisk fungere i Oracle, MySQL osv. -- for Oracle tilføj FROM DUAL til hver SELECT i den afledte tabel):

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 og nyere har den oprindelige UNPIVOT-kommando, selvom jeg generelt, selv når UNPIVOT fungerer, kan lide at bruge CROSS APPLY i stedet, fordi der er mere fleksibilitet til at gøre, hvad jeg vil. Specifikt kan den oprindelige UNPIVOT-kommando ikke fungere her, fordi UNPIVOT kun kan målrette mod en enkelt destinationskolonne, men du har brug for to (Old_Value, New_Value). At sammenkæde de to kolonner i en enkelt værdi (og adskille senere) er ikke godt; at skabe en meningsløs rækkekorrelatorværdi til at PIVOT med bagefter er ikke godt, og jeg kan ikke komme i tanke om en anden måde at gøre det på, som ikke er en variation af disse to. CROSS APPLY-løsningen vil virkelig være den bedste for dig til at matche den nøjagtige logtabelstruktur, du har beskrevet.

Sammenlignet med mine forespørgsler her, vil din metode #1 ikke fungere så godt (i et forhold på ca. {antallet af kolonner}:1 dårligere ydeevne). Din metode nr. 2 er en god idé, men stadig suboptimal, fordi kald af en UDF har en stor overhead, plus så skal du løkke over hver række (gys).




  1. Indsat IP i MySQL-databasen ændres hver gang

  2. MS-rapporteringstjenester begrænser antallet af rækker

  3. DATE ADD funktion i PostgreSQL

  4. Trimmer MySQL TRIM-funktionen ikke linjeskift eller vognretur?