Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Operação de atualização de log em gatilhos


Antes de responder, deixe-me dizer primeiro que não acho melhor registrar todas as tabelas em uma única tabela. Se seu banco de dados crescer, você poderá acabar com uma séria contenção na tabela Log. Além disso, todos os seus dados precisam ser alterados para varchar ou sql_variant para serem colocados na mesma coluna, forçando-os a ocupar mais espaço. Eu também acho que registrar cada coluna atualizada em uma linha separada (pulando colunas que não são atualizadas) vai torná-la muito difícil para você consultar. Você sabe como reunir todos esses dados para realmente obter uma visão composta e sensata das alterações de cada linha, quando e por quem? Ter uma tabela de log por tabela é, na minha opinião, muito mais fácil. Então você não terá os problemas que está enfrentando tentando fazê-lo funcionar.

Além disso, você sabia sobre o SQL Server 2008 Alterar captura de dados ? Use isso se estiver usando as edições Enterprise ou Developer do SQL Server!

Além desse problema, você pode fazer o que quiser com um UNPIVOT lógico (executando sua própria versão dele). Você realmente não pode usar o Native SQL 2005 UNPIVOT porque você tem duas colunas de destino, não uma. Aqui está um exemplo para SQL Server 2005 e superior usando CROSS APPLY para executar o 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

Aqui está um método mais genérico para SQL 2000 ou outros DBMSs (teoricamente deveria funcionar em Oracle, MySQL, etc. -- para Oracle adicione FROM DUAL para cada SELECT na tabela derivada):
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

O SQL Server 2005 e superior têm o comando UNPIVOT nativo, embora em geral, mesmo quando o UNPIVOT funcionar, eu gosto de usar CROSS APPLY porque há mais flexibilidade para fazer o que eu quero. Especificamente, o comando UNPIVOT nativo não funciona aqui porque UNPIVOT só pode direcionar uma única coluna de destino, mas você precisa de duas (Old_Value, New_Value). Concatenar as duas colunas em um único valor (e separar depois) não é bom; criar um valor de correlacionador de linha sem sentido para PIVOT depois não é bom, e não consigo pensar em outra maneira de fazer isso que não seja uma variação desses dois. A solução CROSS APPLY será realmente a melhor para você corresponder à estrutura exata da tabela de log que você descreveu.

Comparado com minhas consultas aqui, seu método nº 1 não funcionará tão bem (em uma proporção de aproximadamente {o número de colunas}:1 desempenho pior). Seu método nº 2 é uma boa ideia, mas ainda está abaixo do ideal porque chamar uma UDF tem uma grande sobrecarga, além disso, você precisa fazer um loop em cada linha (estremecer).