这是我的情况。我正在使用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的同时执行操作的情况。这是可接受的方法吗?
感谢任何评论。
答案 0 :(得分:1)
问题:
我在触发器中看到的唯一问题是您假设INSERTED
和DELETED
表只有一行。
此假设将导致此行中第一个触发器出错:
WHERE AID = (SELECT ID FROM Inserted)
如果使用一个INSERT
语句插入了多行。
在第二个触发器中,您通过选择TOP 1
来解决此问题,但这会给您带来一些错误,因为它会为插入的一行选择AHistories.ID
并将其保存到所有其他行进入BHistories
。
<强>解决方案强>:
您可以随时加入INSERTED
和DELETED
表格以获取所需内容。以下是更改:
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