带有触发器的SQL Server外键用作历史记录器

时间:2014-08-10 22:47:11

标签: sql sql-server database triggers

这是我的情况。我正在使用ASP.NET MVC 5。

我的要求是跟踪记录中的添加/更新/删除操作到历史表中。

此示例有2个表A和B用于原始记录,另一个表,AHistories和BHistories用于保存历史记录。有一对多关系,一个A和多个B。

我使用触发器来实现这一点。我被困在与外键相关的东西上。当我在触发器中将记录插入历史表时,我想保存记录,其中外键指向历史表中的ID而不是原始表。

这就是我在触发器中所做的。

CREATE TRIGGER A_History_Trigger ON A FOR INSERT, UPDATE, DELETE AS

SET NOCOUNT ON;

DECLARE @AHistoryID INT

IF EXISTS (SELECT * FROM Inserted)
BEGIN
    DECLARE @HistoryType NVARCHAR(6)

    IF EXISTS (SELECT * FROM Deleted)
        SET @HistoryType = 'update'
    ELSE
        SET @HistoryType = 'add'

    /* Save record from table A into AHistories table */
    INSERT INTO AHistories (HistoryID, Area, ModifiedDate, HistoryType)
    SELECT ID, Area, CURRENT_TIMESTAMP, @HistoryType
    FROM Inserted

    /* Save updated record from table B into BHistories table */
    IF @HistoryType = 'update'
        INSERT INTO BHistories (HistoryID, AHistoryID, Name, ModifiedDate, HistoryType)
        SELECT ID, SCOPE_IDENTITY(), Name, CURRENT_TIMESTAMP, @HistoryType
        FROM B
        WHERE AID = (SELECT ID FROM Inserted)
END
ELSE /* Delete */
BEGIN
    INSERT INTO AHistories (HistoryID, Area, ModifiedDate, HistoryType)
    SELECT ID, Area, CURRENT_TIMESTAMP, 'delete' FROM Deleted

    DECLARE @AID INT
    SET @AID = (SELECT ID FROM Deleted)
    SET @AID = SCOPE_IDENTITY()

    /* B */
    INSERT INTO BHistories (HistoryID, AHistoryID, Name, ModifiedDate, HistoryType)
    SELECT ID, @AHistoryID, Name, CURRENT_TIMESTAMP, 'delete'
    FROM B
    WHERE AID = @AID

    DELETE FROM B WHERE AID = @AID
END

在表B的触发器中,

CREATE TRIGGER B_History_Trigger ON B FOR INSERT AS

SET NOCOUNT ON;

DECLARE @AHistoryID INT

/* Is this safe? */
SET @AHistoryID = (SELECT TOP 1 ID FROM AHistories WHERE HistoryID = (SELECT TOP 1 AID FROM Inserted) ORDER BY ID DESC)

INSERT INTO BHistories (HistoryID, Name, AHistoryID, ModifiedDate, HistoryType)
SELECT ID, Name, @AHistoryID, CURRENT_TIMESTAMP, 'add' FROM Inserted

在B的触发器中,通过在那里执行SELECT来获得正确的AHistoryID是否安全?在生产中,我担心是否存在多个用户在触发器选择错误的AHistoryID的同时执行操作的情况。这是可接受的方法吗?

感谢任何评论。

1 个答案:

答案 0 :(得分:1)

问题

我在触发器中看到的唯一问题是您假设INSERTEDDELETED表只有一行。

此假设将导致此行中第一个触发器出错:

WHERE AID = (SELECT ID FROM Inserted)

如果使用一个INSERT语句插入了多行。

在第二个触发器中,您通过选择TOP 1来解决此问题,但这会给您带来一些错误,因为它会为插入的一行选择AHistories.ID并将其保存到所有其他行进入BHistories

<强>解决方案

您可以随时加入INSERTEDDELETED表格以获取所需内容。以下是更改:

INSERT INTO BHistories (HistoryID, AHistoryID, Name, ModifiedDate, HistoryType)
SELECT B.ID, SCOPE_IDENTITY(), B.Name, CURRENT_TIMESTAMP, @HistoryType
FROM B
INNER JOIN Inserted i ON i.ID = B.AID

并且:

INSERT INTO BHistories (HistoryID, AHistoryID, Name, ModifiedDate, HistoryType)
SELECT B.ID, @AHistoryID, B.Name, CURRENT_TIMESTAMP, 'delete'
FROM B
INNER JOIN Deleted d ON d.ID = B.AID

最后一个。远程这一行:

SET @AHistoryID = (SELECT TOP 1 ID FROM AHistories WHERE HistoryID = (SELECT TOP 1 AID 

并像这样更新查询:

INSERT INTO BHistories (HistoryID, Name, AHistoryID, ModifiedDate, HistoryType)
SELECT i.ID, i.Name, ah.ID, CURRENT_TIMESTAMP, 'add' 
FROM Inserted i
INNER JOIN AHistories ah ON ah.HistoryID = i.AID