Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Journalisation de l'opération de mise à jour dans les déclencheurs

Avant de répondre, permettez-moi d'abord de dire que je ne pense pas qu'il soit préférable de connecter toutes les tables à une seule table. Si votre base de données s'agrandit, vous pourriez vous retrouver avec de sérieux conflits sur la table Log. De plus, toutes vos données doivent être modifiées en varchar ou sql_variant afin d'être placées dans la même colonne, ce qui la force à occuper plus d'espace. Je pense aussi que la journalisation de chaque colonne mise à jour sur une ligne distincte (en sautant les colonnes qui ne sont pas mises à jour) va la rendre très difficile pour vous d'interroger. Savez-vous comment rassembler toutes ces données pour obtenir une vue composite et sensible des modifications de chaque ligne, quand et par qui ? Avoir une table de journal par table est, à mon avis, beaucoup plus facile. Vous n'aurez alors plus les problèmes que vous rencontrez en essayant de le faire fonctionner.

Aussi, connaissiez-vous SQL Server 2008 Modifier la capture de données ? Utilisez-le à la place, si vous utilisez les éditions Entreprise ou Développeur de SQL Server !

Mis à part ce problème, vous pouvez faire ce que vous voulez avec un UNPIVOT logique (en exécutant votre propre version). Vous ne pouvez pas vraiment utiliser l'UNPIVOT natif SQL 2005 car vous avez deux colonnes cibles, pas une. Voici un exemple pour SQL Server 2005 et versions ultérieures utilisant CROSS APPLY pour effectuer l'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

Voici une méthode plus générique pour SQL 2000 ou d'autres SGBD (devrait théoriquement fonctionner dans Oracle, MySQL, etc. -- pour Oracle, ajoutez FROM DUAL à chaque SELECT dans la table dérivée) :

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 et versions ultérieures ont la commande UNPIVOT native, bien qu'en général, même lorsque UNPIVOT fonctionne, j'aime utiliser CROSS APPLY à la place car il y a plus de flexibilité pour faire ce que je veux. Plus précisément, la commande UNPIVOT native n'est pas utilisable ici car UNPIVOT ne peut cibler qu'une seule colonne de destination, mais vous en avez besoin de deux (Old_Value, New_Value). Concaténer les deux colonnes en une seule valeur (et les séparer plus tard) n'est pas bon ; créer une valeur de corrélateur de ligne sans signification pour PIVOT avec ensuite n'est pas bon, et je ne peux pas penser à une autre façon de le faire qui ne soit pas une variation sur ces deux. La solution CROSS APPLY sera vraiment la meilleure solution pour correspondre exactement à la structure de table de journal que vous avez décrite.

Par rapport à mes requêtes ici, votre méthode n ° 1 ne fonctionnera pas aussi bien (dans un rapport d'environ {le nombre de colonnes} :1 performances inférieures). Votre méthode n ° 2 est une bonne idée mais toujours sous-optimale car l'appel d'un UDF a une surcharge importante, en plus vous devez boucler sur chaque ligne (frisson).