SQL 服务器触发器无法快速连续插入行

SQL Server trigger failing for row inserts in quick succession

我在 SO 上看了一圈,发现了很多类似的问题:

SQL Server A trigger to work on multiple row inserts

Trigger to handle multiple row inserts and updates

update multiple rows with trigger after insert (sql server)

Trigger not working when inserting multiple records

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

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

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 以进行新预订时(将一行插入预订 table),此触发器工作得很好。但是,如果我尝试更新多个游览组件(将多行插入预订 table 以更新 TourComponent table 中的多行),则只会更新第一个游览组件,任何行。

其他答案和研究表明

Triggers are NOT executed once per row but rather as a set based operation so executed only ONCE for the entire DML operation. So you need to treat it like any other update date with join statement. So I would have expected my joining on the INSERTED table to have handled multiple rows or have I misunderstood this?

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

使用 sql 探查器捕获在 运行 时间执行的实际 sql 并 运行 手动对数据库执行此操作,我根据需要更新了两行。仅当使用 Entity Framework 更新数据库时,即 运行 应用程序我发现只有一行被更新。

我尝试将值记录到触发器中的 table FOO

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

这记录了两行,每次行数为 1,并且记录了正确的 tourcomponentsID 和 reservationID,但是 TourComponent table 仍然只更新了一行。

非常感谢任何建议。

更新

旅游组件 ID 在 Ajax post 中作为字符串传递给填充旅游组件模型的 MVC 操作,然后传递以在代码中一次更新一个

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

这里是对 UpdateTourComponent 的调用

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

以及对 Update 的最终调用

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,即只有第一个插入触发器起作用时的 运行。

有趣的是,当完全相同的 sql 在 SQL Management Studio 中 运行 时,触发器按预期工作,并且两个游览组件都使用预订 ID 更新。

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

是否还有其他建议可能导致此问题?

您遇到的问题与特定触发器不同。尝试查看正在更新的 table 名称“[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

所以潜在的问题归结为 Entity Framework。

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

是一个属性 用于SQL 数据访问层。这被缓存并且导致从数据库中读取的数据不是最新的当前数据,因此如果我们在 Reservations table 中有一个插入,第二个插入将被缓存的值覆盖,在我的例子中为 NULL。

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

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

查看更多信息 HasDatabaseGeneratedOption