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

Registrazione dell'operazione di aggiornamento nei trigger

Prima di rispondere, lasciami dire prima che non penso sia meglio registrare tutte le tabelle su un'unica tabella. Se il tuo database cresce, potresti finire con una seria contesa sulla tabella Log. Inoltre, tutti i tuoi dati devono essere modificati in varchar o sql_variant per essere inseriti nella stessa colonna, costringendoli a occupare più spazio. Penso anche che la registrazione di ogni colonna aggiornata su una riga separata (saltando le colonne che non sono aggiornate) lo renderà molto difficile per te interrogare. Sai come riunire tutti quei dati per ottenere effettivamente una visione composita e sensata delle modifiche di ogni riga, quando e da chi? Avere una tabella di registro per tabella è, secondo me, molto più semplice. Quindi non avrai i problemi che stai riscontrando cercando di farlo funzionare.

Inoltre, sapevi di SQL Server 2008 Modifica acquisizione dati ? Usalo invece, se stai usando le edizioni Enterprise o Developer di SQL Server!

A parte questo problema, puoi fare quello che vuoi con un UNPIVOT logico (eseguendo la tua versione di esso). Non puoi davvero usare Native SQL 2005 UNPIVOT perché hai due colonne di destinazione, non una. Ecco un esempio per SQL Server 2005 e versioni successive che utilizza CROSS APPLY per eseguire 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

Ecco un metodo più generico per SQL 2000 o altri DBMS (in teoria dovrebbe funzionare in Oracle, MySQL, ecc. -- per Oracle aggiungi FROM DUAL a ogni SELECT nella tabella derivata):

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 e versioni successive hanno il comando UNPIVOT nativo, anche se in generale, anche quando UNPIVOT funziona, mi piace usare CROSS APPLY perché c'è più flessibilità per fare ciò che voglio. In particolare, il comando UNPIVOT nativo non è utilizzabile qui perché UNPIVOT può indirizzare solo una singola colonna di destinazione, ma ne servono due (Old_Value, New_Value). Concatenare le due colonne in un unico valore (e separare successivamente) non va bene; la creazione di un valore correlatore di riga senza significato a PIVOT con in seguito non va bene, e non riesco a pensare a un altro modo per farlo che non sia una variazione su quei due. La soluzione CROSS APPLY sarà davvero la migliore per te per abbinare l'esatta struttura della tabella di registro che hai descritto.

Rispetto alle mie query qui, il tuo metodo n. 1 non funzionerà altrettanto bene (in un rapporto di circa {il numero di colonne}:1 prestazioni peggiori). Il tuo metodo n. 2 è una buona idea ma comunque non ottimale perché chiamare un UDF ha un grande sovraccarico, inoltre devi scorrere ogni riga (tremito).