数据库触发器的深入解析

数据库触发器是一种特殊的存储过程,它在对表执行特定操作(如插入、删除或更新数据)时自动执行。触发器是绑定到表上的数据库对象,并且是自动执行的,不能显式调用触发器。唯一触发它们的方式是对它们所绑定的表执行所需的操作。

触发器的类型

SQL中,有三种动作查询类型:INSERT、UPDATE和DELETE。因此,存在三种类型的触发器,以及由混合事件和触发时间产生的混合类型。基本上,触发器主要分为两种类型:

  • AFTER触发器(后置触发器)
  • INSTEAD OF触发器(替代触发器)

这些触发器在对表执行插入、更新或删除操作后运行。它们不支持视图。AFTER触发器可以进一步细分为三种类型:

  • AFTER INSERT触发器
  • AFTER UPDATE触发器
  • AFTER DELETE触发器

让创建AFTER触发器。首先,创建一个表并插入一些示例数据。然后,将在这张表上附加几个触发器。

CREATE TABLE Employee_Test ( Emp_ID INT Identity, Emp_name Varchar(100), Emp_Sal Decimal(10,2) ) INSERT INTO Employee_Test VALUES ('Anees', 1000); INSERT INTO Employee_Test VALUES ('Rick', 1200); INSERT INTO Employee_Test VALUES ('John', 1100); INSERT INTO Employee_Test VALUES ('Stephen', 1300); INSERT INTO Employee_Test VALUES ('Maria', 1400);

将创建一个AFTER INSERT触发器,该触发器将在插入到表中的行插入到另一个审计表中。这个审计表的主要目的是记录主表中的更改。这可以被认为是一个通用的审计触发器。

CREATE TABLE Employee_Test_Audit ( Emp_ID int, Emp_name varchar(100), Emp_Sal decimal(10,2), Audit_Action varchar(100), Audit_Timestamp datetime )

这个触发器在表上执行INSERT操作后触发。让创建这个触发器:

CREATE TRIGGER trgAfterInsert ON [dbo].[Employee_Test] FOR INSERT AS BEGIN DECLARE @empid INT; DECLARE @empname VARCHAR(100); DECLARE @empsal DECIMAL(10,2); DECLARE @audit_action VARCHAR(100); SELECT @empid = i.Emp_ID FROM inserted i; SELECT @empname = i.Emp_Name FROM inserted i; SELECT @empsal = i.Emp_Sal FROM inserted i; SET @audit_action = 'Inserted Record -- After Insert Trigger.'; INSERT INTO Employee_Test_Audit (Emp_ID, Emp_Name, Emp_Sal, Audit_Action, Audit_Timestamp) VALUES (@empid, @empname, @empsal, @audit_action, GETDATE()); PRINT 'AFTER INSERT trigger fired.'; END

CREATE TRIGGER语句用于创建触发器。ON子句指定了触发器要附加的表名。FOR INSERT指定这是一个AFTER INSERT触发器。在FOR INSERT的位置上,可以使用AFTER INSERT。两者的意思相同。

在触发器主体中,使用了名为inserted的表。这是一个逻辑表,包含已插入的行。从逻辑inserted表中选择了字段,并将这些值插入到审计表中。

这个触发器在表上执行UPDATE操作后触发。让创建这个触发器:

CREATE TRIGGER trgAfterUpdate ON [dbo].[Employee_Test] FOR UPDATE AS BEGIN DECLARE @empid INT; DECLARE @empname VARCHAR(100); DECLARE @empsal DECIMAL(10,2); DECLARE @audit_action VARCHAR(100); SELECT @empid = i.Emp_ID FROM inserted i; SELECT @empname = i.Emp_Name FROM inserted i; SELECT @empsal = i.Emp_Sal FROM inserted i; IF UPDATE(Emp_Name) SET @audit_action = 'Updated Record -- After Update Trigger.'; IF UPDATE(Emp_Sal) SET @audit_action = 'Updated Record -- After Update Trigger.'; INSERT INTO Employee_Test_Audit (Emp_ID, Emp_Name, Emp_Sal, Audit_Action, Audit_Timestamp) VALUES (@empid, @empname, @empsal, @audit_action, GETDATE()); PRINT 'AFTER UPDATE Trigger fired.'; END

AFTER UPDATE触发器创建后,更新的记录将插入到审计表中。与逻辑表inserted不同,没有逻辑表updated。

可以使用update(column_name)函数获取字段的更新值。在触发器中,使用了if update(Emp_Name)来检查列Emp_Name是否已更新。同样检查了列Emp_Sal的更新。

这个触发器在表上执行DELETE操作后触发。让创建这个触发器:

CREATE TRIGGER trgAfterDelete ON [dbo].[Employee_Test] AFTER DELETE AS BEGIN DECLARE @empid INT; DECLARE @empname VARCHAR(100); DECLARE @empsal DECIMAL(10,2); DECLARE @audit_action VARCHAR(100); SELECT @empid = d.Emp_ID FROM deleted d; SELECT @empname = d.Emp_Name FROM deleted d; SELECT @empsal = d.Emp_Sal FROM deleted d; SET @audit_action = 'Deleted -- After Delete Trigger.'; INSERT INTO Employee_Test_Audit (Emp_ID, Emp_Name, Emp_Sal, Audit_Action, Audit_Timestamp) VALUES (@empid, @empname, @empsal, @audit_action, GETDATE()); PRINT 'AFTER DELETE TRIGGER fired.'; END

在这个触发器中,从逻辑deleted表中提取已删除记录的数据,并将其插入到审计表中。

这些触发器可以作为对表或视图上任何操作的拦截器。如果在表上为Delete操作定义了一个INSTEAD OF触发器,当他们尝试删除行时,实际上并不会被删除(除非在触发器内发出另一个delete指令)。

INSTEAD OF触发器可以进一步细分为三种类型:

  • INSTEAD OF INSERT触发器
  • INSTEAD OF UPDATE触发器
  • INSTEAD OF DELETE触发器

让创建一个INSTEAD OF DELETE触发器:

CREATE TRIGGER trgInsteadOfDelete ON [dbo].[Employee_Test] INSTEAD OF DELETE AS BEGIN DECLARE @emp_id INT; DECLARE @emp_name VARCHAR(100); DECLARE @emp_sal INT; SELECT @emp_id = d.Emp_ID FROM deleted d; SELECT @emp_name = d.Emp_Name FROM deleted d; SELECT @emp_sal = d.Emp_Sal FROM deleted d; BEGIN IF (@emp_sal > 1200) BEGIN RAISERROR ('Cannot delete where salary > 1200', 16, 1); ROLLBACK; END ELSE BEGIN DELETE FROM Employee_Test WHERE Emp_ID = @emp_id; COMMIT; INSERT INTO Employee_Test_Audit (Emp_ID, Emp_Name, Emp_Sal, Audit_Action, Audit_Timestamp) VALUES (@emp_id, @emp_name, @emp_sal, 'Deleted -- Instead Of Delete Trigger.', GETDATE()); PRINT 'Record Deleted -- Instead Of Delete Trigger.'; END END END

这个触发器将阻止从表中删除Emp_Sal > 1200的记录。如果尝试删除这样的记录,INSTEAD OF触发器将回滚事务,否则事务将被提交。现在,让尝试删除一个Emp_Sal > 1200的记录:

DELETE FROM Employee_Test WHERE Emp_ID = 4

这将打印出在RAISE ERROR语句中定义的错误消息:

Server: Msg 50000, Level 16, State 1, Procedure trgInsteadOfDelete, Line 15 Cannot delete where salary > 1200
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485