创建触发器

已完成

触发器 是特殊存储过程,可在数据库中发生特定事件时自动执行。 定义触发器以维护数据完整性、强制实施业务规则并自动执行数据库作,而无需应用程序级代码。

了解触发器基础知识

触发器响应数据库中的数据修改或架构更改。 创建触发器时,指定激活该触发器的事件及其执行的作。

触发器会自动执行。 与显式调用的存储过程不同,触发器在响应 INSERTUPDATEDELETE 或 DDL 语句时触发。 这种自动执行使它们非常强大,用于强制实施必须一致地应用于所有数据修改的规则。

SQL Server 支持两个主要类别的触发器: DML(数据作语言) 触发器和 DDL(数据定义语言) 触发器。 DML 触发器响应表数据中的更改,而 DDL 触发器响应架构更改,例如 CREATEALTERDROP 语句。

为数据修改创建 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 触发器替换原始数据修改语句。 触发器代码执行,而不是执行INSERTUPDATEDELETE操作。 使用 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 操作将旧值填充到 已删除 表中,新值填充到 插入 表中。

为特定事件实现触发器

指定哪些数据修改事件激活触发器。 单个触发器可以通过组合INSERTUPDATEDELETE触发器定义中的多个事件来响应多个事件。

为了精确控制,您为每个操作创建单独的触发器。 此方法简化了代码,并使触发器更易于维护:

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;

同时,当同一逻辑应用于多个作时,可以合并事件。 例如,您可以创建一个响应 INSERTUPDATEDELETE 的单个审核触发器:

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;

彻底记录触发器。 其他开发人员需要了解触发器存在的原因及其用途,因为它们在正常数据库作期间不可见地执行。

了解如何创建和实现触发器后,即可了解如何与其他可编程性对象集成,以生成全面的数据库解决方案。