使用 MSSQL 中的触发器跟踪和存储新旧值

Posted

技术标签:

【中文标题】使用 MSSQL 中的触发器跟踪和存储新旧值【英文标题】:Track and store old and new value using trigger in MSSQL 【发布时间】:2021-10-11 10:20:32 【问题描述】:

我对 MSSQL 非常陌生,为了跟踪 sql 表中的更改,我创建了一个单独的表并在发生任何更改时填充相同的表,我在触发器下面写这个(目前我正在为插入而写,因此旧值和新值可能看起来很傻,但我需要同样的东西,而任何东西都得到更新)我计划在此之后再写两个触发器用于删除和更新。 以下是触发代码,但我无法获取旧值,请帮助我:

    create trigger ToolHistory_Insert on [Tool Management]
after Insert
as
begin
set nocount on;
declare @oldID int
declare @type nvarchar(20)
declare @material float
declare @matdes nvarchar(MAX)
declare @toolno nvarchar(20)
declare @tooldes nvarchar(MAX)
declare @toollife float
select @oldID = inserted.SL_NO , @type = inserted.[Type]  , @material = inserted.Material , @matdes = inserted.Material_Description , @toolno = inserted.Tool_Code  , @tooldes = inserted.Tool_Description
 , @toollife = inserted.Tool_Life
from inserted
insert into PINQC.dbo.Tool_Management_History(Old_Table_ID , Old_Type , New_Type , Old_Material , New_Material , Old_Material_Description , New_Material_Description , Old_Tool_Code , New_Tool_Code , 
Old_Tool_Description , New_Tool_Description , Old_Tool_Life , New_Tool_Life , Changed_By , Changed_At , Change_Type)
values(@oldID  , [Tool Management].[Type] , @type , [Tool Management].Material , @material , [Tool Management].Material_Description , @matdes , [Tool Management].Tool_Code , @toolno , 
[Tool Management].Tool_Description , @tooldes , [Tool Management].Tool_Life , @toollife , CURRENT_USER , GETDATE() , 'Inserted')
END

这是执行触发器时的错误消息(我知道我不能像这样从 [Tool Management] 调用值,但我不知道其他方式)

    Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 18 [Batch Start Line 0]
The multi-part identifier "Tool Management.Type" could not be bound.
Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 18 [Batch Start Line 0]
The multi-part identifier "Tool Management.Material" could not be bound.
Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 18 [Batch Start Line 0]
The multi-part identifier "Tool Management.Material_Description" could not be bound.
Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 18 [Batch Start Line 0]
The multi-part identifier "Tool Management.Tool_Code" could not be bound.
Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 19 [Batch Start Line 0]
The multi-part identifier "Tool Management.Tool_Description" could not be bound.
Msg 4104, Level 16, State 1, Procedure ToolHistory_Insert, Line 19 [Batch Start Line 0]
The multi-part identifier "Tool Management.Tool_Life" could not be bound.

【问题讨论】:

小心您的触发器假设 INSERT 只会包含 1 行;那明显是错的。 INSERT 可以影响 0+ 行。 @Larnu 谢谢你的建议,你能帮忙解决这个问题吗? 还有你说的变化,不过以上只是在INSERTUPDATEDELETE 语句呢?为什么不直接使用临时表,而不是重新发明***? 假设您使用的是受支持的 SQL Server 版本,我会使用 Temporal Table。 让我们从学习好习惯开始吧。首先,不要使用带空格的名称。使用符合常规标识符规则的名称。格式化您的代码,使其可读 - 您发布了一堆乱七八糟的代码。使用语句终止符。不要在没有充分理由和了解代码的脆弱性的情况下使用 3 部分名称。正如其他人所指出的那样,插入没有“旧”信息 - 逻辑废话。如果您的架构允许更改任何行的 PK 值,那么您的历史记录将几乎毫无用处。 【参考方案1】:

您的触发器使a classic mistake:未考虑inserted 表中的多个(或零个)行。

一般情况下,您应该将INSERT UPDATEDELETE 触发器合二为一。但这里是一个非常简单的触发器,因此您可以通过对inserteddeleted 表进行完全连接来组合它们。你应该通过主键加入,我假设是SL_NO

CREATE OR ALTER TRIGGER ToolHistory on [Tool Management]
AFTER INSERT, UPDATE, DELETE
AS

SET NOCOUNT ON;

INSERT PINQC.dbo.Tool_Management_History
  (Old_Table_ID, Old_Type, New_Type, Old_Material, New_Material,
  Old_Material_Description, New_Material_Description, Old_Tool_Code, New_Tool_Code,
  Old_Tool_Description, New_Tool_Description, Old_Tool_Life, New_Tool_Life,
  Changed_By, Changed_At, Change_Type)
SELECT
  ISNULL(i.SL_NO, d.SL_NO),
  d.[Type],
  i.[Type],
  d.Material,
  i.Material,
  d.Material_Description,
  i.Material_Description,
  d.Tool_Code,
  i.Tool_Code,
  d.Tool_Description,
  i.Tool_Description,
  d.Tool_Life,
  i.Tool_Life,
  CURRENT_USER,
  GETDATE(),
  CASE WHEN d.SL_NO IS NULL THEN 'Inserted' WHEN d.SL_NO IS NULL THEN 'Deleted' ELSE 'Update' END
FROM inserted i
FULL JOIN deleted d ON d.SL_NO = i.SL_NO;

GO

【讨论】:

以上是关于使用 MSSQL 中的触发器跟踪和存储新旧值的主要内容,如果未能解决你的问题,请参考以下文章

使用 Sybase 触发器编写动态语句,使用所有新旧值创建您自己的复制事务语句日志?

新旧列中的触发器

powershell 使用PowerShell导出MSSQL架构。此脚本将导出表,存储过程,触发器,函数和视图的模式定义

MSSQL 事务,视图,索引,存储过程,触发器

SQL Server 触发器:更新查询新旧数据

MSSQL 中的 INSERT 触发器:按变量的目标表