SQL Server连续快速插入行的触发器失败



我环顾SO,发现了许多类似的问题:

SQL Server用于多行插入的触发器

SQL触发器多次插入更新

用于处理多行插入和更新的触发器

插入后使用触发器更新多行(sql server)

插入多条记录时触发器不起作用

但是,在将多行插入表时,我的触发器更新多行时仍然存在问题。

代码大纲我有一个Reservation表,它分别有ReservationID和TourComponentID列。当我插入到预订表中时,我有以下触发器,用刚刚插入到具有匹配TourComponentID的预订表中的行中的ReserveID更新TourComponent表:

CREATE TRIGGER [TR_Reservation_CurrentReservation] ON [Reservation] AFTER INSERT AS
UPDATE tc 
SET tc.[CurrentReservationId] = I.ReservationID   
FROM [tour].[TourComponent] tc 
JOIN INSERTED I on I.TourComponentID = tc.TourComponentID
End

当更新一个tourComponent以获得新的预订(在预订表中插入一行)时,此触发器非常有效。但是,如果我尝试更新多个巡回组件(在保留表中插入多行以更新TourComponent表中的多行),则只有第一个巡回组件得到更新,任何行。

其他的答案和研究表明

触发器不是每行执行一次,而是作为一个基于集合的触发器执行对于整个DML操作,仅执行一次这样的操作。所以你需要像对待任何其他带有join语句的更新日期一样对待它。所以我本以为我在INSERTED表上的联接处理了多行,或者我误解了这一点?

有趣的是,如果我将TourComponentID、ReserveID和INSERTED行计数的触发变量记录到临时表foo中,我可以看到有两条记录插入到我的临时表中,每条记录的行计数都为1。

使用sql探查器捕获运行时执行的实际sql,并对数据库手动运行它,我会根据需要更新两行。只有当使用实体框架更新数据库(即运行应用程序)时,我才会发现只有一行被更新。

我已经尝试将值记录到触发器中的表FOO中

INSERT INTO FOO (TourComponentID, ReservationID, Rowcounts )
SELECT i.TourComponentID, I.ReservationID, 1  --@ReservationId 
FROM
INSERTED I

这会记录两行,每次行数为1,并记录正确的tourcomponentsID和reservationID,但TourComponent表仍然只有一行更新。

非常感谢任何建议。

更新

巡回组件ID在Ajax帖子中作为字符串传递给MVC Action,在MVC Action中填充巡回组件模型,然后在代码中一次一个地更新

public void UpdateTourComponents(IEnumerable<TourComponent> tourComponents)
{
foreach (var tourComponent in tourComponents)
{
UpdateTourComponent(tourComponent);
}
}

这是UpdateTourComponent 的电话

public int UpdateTourComponent(TourComponent tourComponent)
{
return TourComponentRepository.Update(tourComponent);
}

以及更新的最后一次调用

public virtual int Update(TObject TObject)
{
Dictionary<string, List<string>> newChildKeys;
return Update(TObject, null, out newChildKeys);
}

所以每次插入一个,因此每个TourComponent调用一次我的触发器。这就是为什么当我计算INSERTED中的@@Rowcount并登录到Foo时,我得到的值为1。当我手动运行插入时,我会得到正确的预期结果,所以我同意@Slava Murygin的测试,即问题可能与触发器本身无关。我认为如果我们一个接一个地发送请求,这可能是一个速度问题,所以我在触发器和代码中进行了等待,但这并没有解决问题

更新2

我使用了一个sql探查器来捕获只有第一个插入触发器工作时运行的sql。

有趣的是,当EXACT相同的sql在sql Management Studio中运行时,触发器会按预期工作,并且两个tour组件都会使用保留id进行更新。

还值得一提的是,所有表中的所有约束都已删除。

有什么其他建议可能导致此问题?

您有不同于特定触发器的问题。试着查看正在更新的表名"[tour].[TourComponent]"或"[dbo].[TourComponent]"。我试过你的扳机,效果很好:

use TestDB
GO
IF object_id('Reservation') is not null DROP TABLE Reservation;
GO
IF object_id('TourComponent') is not null DROP TABLE TourComponent;
GO
CREATE TABLE Reservation (
ReservationID INT IDENTITY(1,1),
TourComponentID INT
);
GO
CREATE TABLE TourComponent (
CurrentReservationId INT,
TourComponentID INT
);
GO
CREATE TRIGGER [TR_Reservation_CurrentReservation] ON [Reservation] AFTER INSERT AS
UPDATE tc 
SET tc.[CurrentReservationId] = I.ReservationID   
FROM [TourComponent] tc 
JOIN INSERTED I on I.TourComponentID = tc.TourComponentID
GO
INSERT INTO TourComponent(TourComponentID)
VALUES (1),(2),(3),(4),(5),(6)
GO
INSERT INTO Reservation(TourComponentID)
VALUES (1),(2),(3),(4),(5),(6)
GO
SELECT * FROM Reservation
SELECT * FROM TourComponent

所以根本问题在于实体框架。

this.Property(t => t.CurrentReservationId).HasColumnName("CurrentReservationId");

是SQL数据访问层的一个属性。这是被缓存的,导致从数据库中读取的数据不是最新的,因此,如果我们在Reservations表中有一个插入,第二个插入将被缓存的值覆盖,在我的情况下,这些值为NULL。

将行更改为这样可以解决问题,并使触发器按预期工作。

this.Property(t => t.CurrentReservationId).HasColumnName("CurrentReservationId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

查看有关HasDatabaseGeneratedOption 的更多信息

最新更新