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

时间:2019-01-24 18:29:14

标签: sql-server triggers sql-update database-trigger

我四处看看,发现了许多类似的问题:

SQL Server A trigger to work on multiple row inserts

SQL trigger multiple insert update

Trigger to handle multiple row inserts and updates

update multiple rows with trigger after insert (sql server)

Trigger not working when inserting multiple records

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

代码概述 我有一个保留表,其中分别有一个ReservationID和TourComponentID列。当我插入到预订表中时,我有以下触发器用刚插入匹配表中的TourComponentID的预订行中的ReservationID更新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,ReservationID和INSERTED行计数的触发变量记录到临时表foo中,我可以看到两条记录插入到我的临时表中,每条记录的行数均为1。

使用sql profiler捕获在运行时执行的实际sql,并针对数据库手动运行它,从而根据需要更新了两行。只有在使用Entity Framework更新数据库(即运行应用程序)时,我才发现只有一行被更新。

我尝试将值记录到触发器中的表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);
}

和对Update的最终调用

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

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

更新2

我已经使用sql profiler来捕获仅在第一个插入触发器起作用时运行的sql。

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

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

还有其他建议可能导致此问题的原因吗?

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

因此潜在的问题在于实体框架。

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

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

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

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

查看有关HasDatabaseGeneratedOption的更多信息