sql >> Databasteknik >  >> RDS >> Sqlserver

Loggar uppdateringsoperation i triggers

Innan jag svarar, låt mig först säga att jag inte tycker att det är bäst att logga alla tabeller till en enda tabell. Om din databas växer kan du hamna i allvarliga konflikter i loggtabellen. Dessutom måste alla dina data ändras till varchar eller sql_variant för att placeras i samma kolumn, vilket tvingar den att ta upp mer utrymme. Jag tror också att logga varje uppdaterad kolumn till en separat rad (hoppa över kolumner som inte är uppdaterade) kommer att göra det mycket svårt för dig att fråga. Vet du hur man drar ihop all denna data för att faktiskt få en sammansatt och vettig bild av varje rads ändringar, när och av vem? Att ha en loggtabell per tabell kommer enligt min mening att bli mycket lättare. Då kommer du inte ha de problem du upplever när du försöker få det att fungera.

Visste du också om SQL Server 2008 Ändra datainsamling ? Använd det istället om du använder Enterprise- eller Developer-utgåvorna av SQL Server!

Bortsett från det problemet kan du göra vad du vill med en logisk UNPIVOT (utför din egen version av den). Du kan inte riktigt använda Native SQL 2005 UNPIVOT eftersom du har två målkolumner, inte en. Här är ett exempel för SQL Server 2005 och senare med CROSS APPLY för att utföra 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

Här är en mer generisk metod för SQL 2000 eller andra DBMS:er (bör teoretiskt fungera i Oracle, MySQL, etc. -- för Oracle lägg till FROM DUAL till varje SELECT i den härledda tabellen):

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 och senare har det inbyggda UNPIVOT-kommandot, men i allmänhet, även när UNPIVOT kommer att fungera, gillar jag att använda CROSS APPLY istället eftersom det finns mer flexibilitet att göra vad jag vill. Specifikt är det inbyggda UNPIVOT-kommandot inte användbart här eftersom UNPIVOT bara kan rikta in sig på en enda destinationskolumn, men du behöver två (Old_Value, New_Value). Att sammanfoga de två kolumnerna till ett enda värde (och separera senare) är inte bra; Att skapa ett meningslöst radkorrelatorvärde att PIVOT med efteråt är inte bra, och jag kan inte komma på något annat sätt att göra det på som inte är en variant av dessa två. CROSS APPLY-lösningen kommer verkligen att vara den bästa för dig för att matcha den exakta loggtabellstrukturen du har beskrivit.

Jämfört med mina frågor här kommer din metod #1 inte att fungera lika bra (i förhållandet ungefär {antalet kolumner}:1 sämre prestanda). Din metod #2 är en bra idé men fortfarande suboptimal eftersom att anropa en UDF har en stor overhead, plus då måste du loopa över varje rad (rysning).




  1. Oracle E-Business Suite Architecture i 12.2

  2. Vad är databaser?

  3. MySQL -- Går ihop mellan databaser på olika servrar med Python?

  4. Byt från SQLite till PostgreSQL i ett nytt Rails-projekt