数据库触发器是一种特殊的存储过程,它在对表执行特定操作(如插入、删除或更新数据)时自动执行。触发器是绑定到表上的数据库对象,并且是自动执行的,不能显式调用触发器。唯一触发它们的方式是对它们所绑定的表执行所需的操作。
在SQL中,有三种动作查询类型:INSERT、UPDATE和DELETE。因此,存在三种类型的触发器,以及由混合事件和触发时间产生的混合类型。基本上,触发器主要分为两种类型:
这些触发器在对表执行插入、更新或删除操作后运行。它们不支持视图。AFTER触发器可以进一步细分为三种类型:
让创建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 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