创建触发器
触发器 是特殊存储过程,可在数据库中发生特定事件时自动执行。 定义触发器以维护数据完整性、强制实施业务规则并自动执行数据库作,而无需应用程序级代码。
了解触发器基础知识
触发器响应数据库中的数据修改或架构更改。 创建触发器时,指定激活该触发器的事件及其执行的作。
触发器会自动执行。 与显式调用的存储过程不同,触发器在响应 INSERT、UPDATE、DELETE 或 DDL 语句时触发。 这种自动执行使它们非常强大,用于强制实施必须一致地应用于所有数据修改的规则。
SQL Server 支持两个主要类别的触发器: DML(数据作语言) 触发器和 DDL(数据定义语言) 触发器。 DML 触发器响应表数据中的更改,而 DDL 触发器响应架构更改,例如 CREATE, ALTER或 DROP 语句。
为数据修改创建 DML 触发器
DML 触发器监视和响应表或视图中的数据更改。 将它们定义为 AFTER 触发器或 INSTEAD OF 触发器。
AFTER 触发器在触发语句完成后执行。 数据库首先执行数据修改,然后运行触发器代码。 可以使用 AFTER 触发器来验证更改、更新相关表或日志修改:
CREATE TRIGGER tr_UpdateInventory
ON Sales.OrderDetails
AFTER INSERT
AS
BEGIN
UPDATE Inventory.Products
SET QuantityInStock = QuantityInStock - i.Quantity
FROM Inventory.Products p
INNER JOIN inserted i ON p.ProductID = i.ProductID;
END;
INSTEAD OF 触发器替换原始数据修改语句。 触发器代码执行,而不是执行INSERT、UPDATE或DELETE操作。 使用 INSTEAD OF 触发器来修改通常不接受直接修改的视图,或用于实现复杂的业务逻辑:
CREATE TRIGGER tr_UpdateOrderView
ON Sales.OrderSummaryView
INSTEAD OF UPDATE
AS
BEGIN
UPDATE Sales.Orders
SET OrderStatus = i.OrderStatus,
ModifiedDate = GETDATE()
FROM Sales.Orders o
INNER JOIN inserted i ON o.OrderID = i.OrderID;
END;
使用 DML 触发器可以访问 插入 和 删除的 伪表。 这些临时表存储受影响行的副本。
INSERT 操作将数据填充到 插入 表中,DELETE 操作将数据填充到 已删除 表中,UPDATE 操作将旧值填充到 已删除 表中,新值填充到 插入 表中。
为特定事件实现触发器
指定哪些数据修改事件激活触发器。 单个触发器可以通过组合INSERTUPDATE和DELETE触发器定义中的多个事件来响应多个事件。
为了精确控制,您为每个操作创建单独的触发器。 此方法简化了代码,并使触发器更易于维护:
CREATE TRIGGER tr_LogPriceChanges
ON Products.Catalog
AFTER UPDATE
AS
BEGIN
IF UPDATE(Price)
BEGIN
INSERT INTO Audit.PriceHistory (ProductID, OldPrice, NewPrice, ChangeDate)
SELECT d.ProductID, d.Price, i.Price, GETDATE()
FROM deleted d
INNER JOIN inserted i ON d.ProductID = i.ProductID
WHERE d.Price <> i.Price;
END;
END;
同时,当同一逻辑应用于多个作时,可以合并事件。 例如,您可以创建一个响应 INSERT、UPDATE 和 DELETE 的单个审核触发器:
CREATE TRIGGER tr_AuditEmployeeChanges
ON HR.Employees
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE @Operation NVARCHAR(10);
IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS (SELECT * FROM deleted)
SET @Operation = 'INSERT';
ELSE IF EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
SET @Operation = 'UPDATE';
ELSE
SET @Operation = 'DELETE';
INSERT INTO Audit.EmployeeLog (EmployeeID, Operation, ChangeDate)
SELECT COALESCE(i.EmployeeID, d.EmployeeID), @Operation, GETDATE()
FROM inserted i
FULL OUTER JOIN deleted d ON i.EmployeeID = d.EmployeeID;
END;
该 UPDATE() 函数可帮助你确定哪些列已更改。 仅当某些字段对业务逻辑很重要时,检查特定列以避免不必要的处理。
应用触发器最佳做法
触发器会影响数据库性能,因为它们在每次符合条件的操作时执行。 编写高效的触发器代码,以最大程度地减少对事务吞吐量的影响。
请让您的触发器逻辑集中且简化。 仅在触发器正文中执行必要操作。 对于复杂或耗时的作,请考虑记录事件详细信息并通过单独的作业异步处理它们:
CREATE TRIGGER tr_QueueLargeOrders
ON Sales.Orders
AFTER INSERT
AS
BEGIN
INSERT INTO Processing.OrderQueue (OrderID, TotalAmount, QueuedDate)
SELECT OrderID, TotalAmount, GETDATE()
FROM inserted
WHERE TotalAmount > 10000;
END;
避免递归操作的情况,其中触发器的修改会导致同一触发器再次触发。
RECURSIVE_TRIGGERS适当地设置数据库选项并设计触发器以防止无休止的循环。
在触发器中正确处理错误。 事务行为取决于你的错误处理。 如果触发器遇到错误并且您未处理该错误,SQL Server 将回滚触发器和原始语句。
CREATE TRIGGER tr_ValidateOrderDate
ON Sales.Orders
AFTER INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted WHERE OrderDate > GETDATE())
BEGIN
THROW 50001, 'Order date cannot be in the future', 1;
END;
END;
彻底记录触发器。 其他开发人员需要了解触发器存在的原因及其用途,因为它们在正常数据库作期间不可见地执行。
了解如何创建和实现触发器后,即可了解如何与其他可编程性对象集成,以生成全面的数据库解决方案。